@letta-ai/letta-code 0.28.12 → 0.28.14
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/dist/agent-presets.js +3 -1
- package/dist/agent-presets.js.map +2 -2
- package/dist/types/channels/types.d.ts +25 -0
- package/dist/types/channels/types.d.ts.map +1 -1
- package/letta.js +1702 -1035
- package/package.json +1 -1
- package/scripts/source-file-size-baseline.json +2 -2
- package/skills/letta-guide/SKILL.md +74 -0
- package/skills/scheduling-tasks/SKILL.md +28 -3
package/letta.js
CHANGED
|
@@ -4853,7 +4853,7 @@ var package_default;
|
|
|
4853
4853
|
var init_package = __esm(() => {
|
|
4854
4854
|
package_default = {
|
|
4855
4855
|
name: "@letta-ai/letta-code",
|
|
4856
|
-
version: "0.28.
|
|
4856
|
+
version: "0.28.14",
|
|
4857
4857
|
description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
|
|
4858
4858
|
type: "module",
|
|
4859
4859
|
packageManager: "bun@1.3.0",
|
|
@@ -159124,6 +159124,8 @@ Creating crons:
|
|
|
159124
159124
|
- Recurring monitoring/heartbeat: \`letta cron add --name <short-name> --description <description> --prompt <future-message> --every "2h"\` or \`--cron "0 9 * * *"\`
|
|
159125
159125
|
Always include \`--name\`, \`--description\`, and \`--prompt\`. \`$AGENT_ID\` is automatically injected into the shell environment, and \`letta cron\` uses it by default, so you do not need to specify which agent to invoke unless overriding the current agent intentionally.
|
|
159126
159126
|
|
|
159127
|
+
Where crons run: for cloud agents, schedules default to durable Cloud schedules that fire from the cloud and execute in your cloud sandbox — they survive local shutdown, so this is the right default. If the scheduled work must run on a specific computer (e.g. it needs that computer's filesystem or local services), add \`--computer <deviceId>\` (from \`letta environments list\`) to keep the durable Cloud schedule but execute on that computer, with sandbox fallback if it is offline. Use \`--runner local\` only when that fallback is unacceptable; local schedules only fire while a Letta session is running on that computer.
|
|
159128
|
+
|
|
159127
159129
|
# Harness Architecture
|
|
159128
159130
|
|
|
159129
159131
|
You run within the Letta Code CLI on some machine (the environment). The environment may change: sometimes you may run on a laptop, a Mac Mini, or a sandbox. Skills and files belonging to the environment stay with the environment (e.g. \`AGENTS.md\` or \`.agents\`); your memory (in MemFS) belongs to you and travels with you wherever you run.
|
|
@@ -166511,6 +166513,12 @@ function cloneAccount(account) {
|
|
|
166511
166513
|
...account,
|
|
166512
166514
|
allowedUsers: [...account.allowedUsers]
|
|
166513
166515
|
};
|
|
166516
|
+
if (account.adminUsers) {
|
|
166517
|
+
cloned.adminUsers = [...account.adminUsers];
|
|
166518
|
+
}
|
|
166519
|
+
if (account.userAllowedCommands) {
|
|
166520
|
+
cloned.userAllowedCommands = [...account.userAllowedCommands];
|
|
166521
|
+
}
|
|
166514
166522
|
if (isTelegramChannelAccount(account)) {
|
|
166515
166523
|
cloned.binding = { ...account.binding };
|
|
166516
166524
|
}
|
|
@@ -166882,9 +166890,12 @@ var init_accounts = __esm(() => {
|
|
|
166882
166890
|
init_types7();
|
|
166883
166891
|
SNAKE_TO_CAMEL = {
|
|
166884
166892
|
account_uuid: "accountUuid",
|
|
166893
|
+
admin_users: "adminUsers",
|
|
166885
166894
|
allowed_channels: "allowedChannels",
|
|
166886
166895
|
allowed_groups: "allowedGroups",
|
|
166887
166896
|
allow_bots: "allowBots",
|
|
166897
|
+
group_policy: "groupPolicy",
|
|
166898
|
+
user_allowed_commands: "userAllowedCommands",
|
|
166888
166899
|
auto_thread_on_mention: "autoThreadOnMention",
|
|
166889
166900
|
base_url: "baseUrl",
|
|
166890
166901
|
acknowledge_message_reaction: "acknowledgeMessageReaction",
|
|
@@ -167334,7 +167345,551 @@ var init_account_display2 = __esm(() => {
|
|
|
167334
167345
|
init_utils5();
|
|
167335
167346
|
});
|
|
167336
167347
|
|
|
167337
|
-
// src/channels/
|
|
167348
|
+
// src/channels/pairing.ts
|
|
167349
|
+
var exports_pairing = {};
|
|
167350
|
+
__export(exports_pairing, {
|
|
167351
|
+
rollbackPairingApproval: () => rollbackPairingApproval,
|
|
167352
|
+
removePairingStateForAccount: () => removePairingStateForAccount,
|
|
167353
|
+
loadPairingStore: () => loadPairingStore,
|
|
167354
|
+
isUserApproved: () => isUserApproved,
|
|
167355
|
+
getPendingPairings: () => getPendingPairings,
|
|
167356
|
+
getApprovedUsers: () => getApprovedUsers,
|
|
167357
|
+
createPairingCode: () => createPairingCode,
|
|
167358
|
+
consumePairingCode: () => consumePairingCode,
|
|
167359
|
+
clearPairingStores: () => clearPairingStores,
|
|
167360
|
+
__testOverrideSavePairingStore: () => __testOverrideSavePairingStore,
|
|
167361
|
+
__testOverrideLoadPairingStore: () => __testOverrideLoadPairingStore
|
|
167362
|
+
});
|
|
167363
|
+
import { randomInt } from "node:crypto";
|
|
167364
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
|
|
167365
|
+
function normalizeAccountId(accountId) {
|
|
167366
|
+
return accountId ?? LEGACY_CHANNEL_ACCOUNT_ID;
|
|
167367
|
+
}
|
|
167368
|
+
function getStore2(channelId) {
|
|
167369
|
+
let store = stores2.get(channelId);
|
|
167370
|
+
if (!store) {
|
|
167371
|
+
store = { pending: [], approved: [] };
|
|
167372
|
+
stores2.set(channelId, store);
|
|
167373
|
+
}
|
|
167374
|
+
return store;
|
|
167375
|
+
}
|
|
167376
|
+
function loadPairingStore(channelId) {
|
|
167377
|
+
if (loadPairingStoreOverride) {
|
|
167378
|
+
const overridden = loadPairingStoreOverride(channelId);
|
|
167379
|
+
if (overridden === null) {
|
|
167380
|
+
return;
|
|
167381
|
+
}
|
|
167382
|
+
stores2.set(channelId, {
|
|
167383
|
+
pending: [...overridden.pending],
|
|
167384
|
+
approved: [...overridden.approved]
|
|
167385
|
+
});
|
|
167386
|
+
return;
|
|
167387
|
+
}
|
|
167388
|
+
const path5 = getChannelPairingPath(channelId);
|
|
167389
|
+
if (!existsSync8(path5))
|
|
167390
|
+
return;
|
|
167391
|
+
try {
|
|
167392
|
+
const text = readFileSync7(path5, "utf-8");
|
|
167393
|
+
const parsed = JSON.parse(text);
|
|
167394
|
+
stores2.set(channelId, {
|
|
167395
|
+
pending: parsed.pending ?? [],
|
|
167396
|
+
approved: parsed.approved ?? []
|
|
167397
|
+
});
|
|
167398
|
+
} catch {}
|
|
167399
|
+
}
|
|
167400
|
+
function savePairingStore(channelId) {
|
|
167401
|
+
const store = getStore2(channelId);
|
|
167402
|
+
if (savePairingStoreOverride) {
|
|
167403
|
+
savePairingStoreOverride(channelId, {
|
|
167404
|
+
pending: [...store.pending],
|
|
167405
|
+
approved: [...store.approved]
|
|
167406
|
+
});
|
|
167407
|
+
return;
|
|
167408
|
+
}
|
|
167409
|
+
const dir = getChannelDir(channelId);
|
|
167410
|
+
mkdirSync5(dir, { recursive: true });
|
|
167411
|
+
writeFileSync3(getChannelPairingPath(channelId), `${JSON.stringify(store, null, 2)}
|
|
167412
|
+
`, "utf-8");
|
|
167413
|
+
}
|
|
167414
|
+
function generateCode(length = 6) {
|
|
167415
|
+
let code2 = "";
|
|
167416
|
+
for (let i2 = 0;i2 < length; i2++) {
|
|
167417
|
+
code2 += CODE_CHARS[randomInt(CODE_CHARS.length)];
|
|
167418
|
+
}
|
|
167419
|
+
return code2;
|
|
167420
|
+
}
|
|
167421
|
+
function isUserApproved(channelId, userId, accountId) {
|
|
167422
|
+
const store = getStore2(channelId);
|
|
167423
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
167424
|
+
return store.approved.some((u) => u.senderId === userId && normalizeAccountId(u.accountId) === normalizedAccountId);
|
|
167425
|
+
}
|
|
167426
|
+
function createPairingCode(channelId, userId, chatId, username, accountId) {
|
|
167427
|
+
const store = getStore2(channelId);
|
|
167428
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
167429
|
+
const nowMs = Date.now();
|
|
167430
|
+
const existing = store.pending.find((p) => p.senderId === userId && normalizeAccountId(p.accountId) === normalizedAccountId && new Date(p.expiresAt).getTime() > nowMs);
|
|
167431
|
+
if (existing) {
|
|
167432
|
+
return existing.code;
|
|
167433
|
+
}
|
|
167434
|
+
store.pending = store.pending.filter((p) => !(p.senderId === userId && normalizeAccountId(p.accountId) === normalizedAccountId));
|
|
167435
|
+
const now = Date.now();
|
|
167436
|
+
store.pending = store.pending.filter((p) => new Date(p.expiresAt).getTime() > now);
|
|
167437
|
+
while (store.pending.length >= MAX_PENDING_CODES) {
|
|
167438
|
+
store.pending.shift();
|
|
167439
|
+
}
|
|
167440
|
+
const code2 = generateCode();
|
|
167441
|
+
const pending = {
|
|
167442
|
+
accountId: normalizedAccountId,
|
|
167443
|
+
code: code2,
|
|
167444
|
+
senderId: userId,
|
|
167445
|
+
senderName: username,
|
|
167446
|
+
chatId,
|
|
167447
|
+
createdAt: new Date().toISOString(),
|
|
167448
|
+
expiresAt: new Date(now + PAIRING_CODE_TTL_MS).toISOString()
|
|
167449
|
+
};
|
|
167450
|
+
store.pending.push(pending);
|
|
167451
|
+
savePairingStore(channelId);
|
|
167452
|
+
return code2;
|
|
167453
|
+
}
|
|
167454
|
+
function consumePairingCode(channelId, code2, accountId) {
|
|
167455
|
+
const store = getStore2(channelId);
|
|
167456
|
+
const upperCode = code2.toUpperCase();
|
|
167457
|
+
const normalizedAccountId = accountId === undefined ? undefined : normalizeAccountId(accountId);
|
|
167458
|
+
const matches = store.pending.map((pending2, index2) => ({ pending: pending2, index: index2 })).filter(({ pending: pending2 }) => pending2.code === upperCode && (normalizedAccountId === undefined || normalizeAccountId(pending2.accountId) === normalizedAccountId));
|
|
167459
|
+
if (matches.length > 1) {
|
|
167460
|
+
return null;
|
|
167461
|
+
}
|
|
167462
|
+
const index = matches[0]?.index ?? -1;
|
|
167463
|
+
if (index === -1)
|
|
167464
|
+
return null;
|
|
167465
|
+
const pending = store.pending[index];
|
|
167466
|
+
const pendingAccountId = normalizeAccountId(pending.accountId);
|
|
167467
|
+
if (new Date(pending.expiresAt).getTime() < Date.now()) {
|
|
167468
|
+
store.pending.splice(index, 1);
|
|
167469
|
+
savePairingStore(channelId);
|
|
167470
|
+
return null;
|
|
167471
|
+
}
|
|
167472
|
+
store.pending.splice(index, 1);
|
|
167473
|
+
if (!store.approved.some((u) => u.senderId === pending.senderId && normalizeAccountId(u.accountId) === pendingAccountId)) {
|
|
167474
|
+
const approved = {
|
|
167475
|
+
accountId: pendingAccountId,
|
|
167476
|
+
senderId: pending.senderId,
|
|
167477
|
+
senderName: pending.senderName,
|
|
167478
|
+
approvedAt: new Date().toISOString()
|
|
167479
|
+
};
|
|
167480
|
+
store.approved.push(approved);
|
|
167481
|
+
}
|
|
167482
|
+
savePairingStore(channelId);
|
|
167483
|
+
return pending;
|
|
167484
|
+
}
|
|
167485
|
+
function getPendingPairings(channelId, accountId) {
|
|
167486
|
+
const store = getStore2(channelId);
|
|
167487
|
+
const now = Date.now();
|
|
167488
|
+
const normalizedAccountId = accountId === undefined ? undefined : normalizeAccountId(accountId);
|
|
167489
|
+
return store.pending.filter((p) => new Date(p.expiresAt).getTime() > now && (normalizedAccountId === undefined || normalizeAccountId(p.accountId) === normalizedAccountId));
|
|
167490
|
+
}
|
|
167491
|
+
function getApprovedUsers(channelId, accountId) {
|
|
167492
|
+
const normalizedAccountId = accountId === undefined ? undefined : normalizeAccountId(accountId);
|
|
167493
|
+
return getStore2(channelId).approved.filter((user) => normalizedAccountId === undefined || normalizeAccountId(user.accountId) === normalizedAccountId);
|
|
167494
|
+
}
|
|
167495
|
+
function rollbackPairingApproval(channelId, pending) {
|
|
167496
|
+
const store = getStore2(channelId);
|
|
167497
|
+
const normalizedAccountId = normalizeAccountId(pending.accountId);
|
|
167498
|
+
store.approved = store.approved.filter((u) => !(u.senderId === pending.senderId && normalizeAccountId(u.accountId) === normalizedAccountId));
|
|
167499
|
+
store.pending.push(pending);
|
|
167500
|
+
savePairingStore(channelId);
|
|
167501
|
+
}
|
|
167502
|
+
function removePairingStateForAccount(channelId, accountId) {
|
|
167503
|
+
const store = getStore2(channelId);
|
|
167504
|
+
const normalizedAccountId = normalizeAccountId(accountId);
|
|
167505
|
+
const nextPending = store.pending.filter((pending) => normalizeAccountId(pending.accountId) !== normalizedAccountId);
|
|
167506
|
+
const nextApproved = store.approved.filter((approved) => normalizeAccountId(approved.accountId) !== normalizedAccountId);
|
|
167507
|
+
const pendingRemoved = store.pending.length - nextPending.length;
|
|
167508
|
+
const approvedRemoved = store.approved.length - nextApproved.length;
|
|
167509
|
+
if (pendingRemoved === 0 && approvedRemoved === 0) {
|
|
167510
|
+
return { pendingRemoved, approvedRemoved };
|
|
167511
|
+
}
|
|
167512
|
+
store.pending = nextPending;
|
|
167513
|
+
store.approved = nextApproved;
|
|
167514
|
+
savePairingStore(channelId);
|
|
167515
|
+
return { pendingRemoved, approvedRemoved };
|
|
167516
|
+
}
|
|
167517
|
+
function clearPairingStores() {
|
|
167518
|
+
stores2.clear();
|
|
167519
|
+
}
|
|
167520
|
+
function __testOverrideLoadPairingStore(fn) {
|
|
167521
|
+
loadPairingStoreOverride = fn;
|
|
167522
|
+
}
|
|
167523
|
+
function __testOverrideSavePairingStore(fn) {
|
|
167524
|
+
savePairingStoreOverride = fn;
|
|
167525
|
+
}
|
|
167526
|
+
var PAIRING_CODE_TTL_MS, MAX_PENDING_CODES = 50, CODE_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789", stores2, loadPairingStoreOverride = null, savePairingStoreOverride = null;
|
|
167527
|
+
var init_pairing = __esm(() => {
|
|
167528
|
+
init_accounts();
|
|
167529
|
+
init_config2();
|
|
167530
|
+
PAIRING_CODE_TTL_MS = 15 * 60 * 1000;
|
|
167531
|
+
stores2 = new Map;
|
|
167532
|
+
});
|
|
167533
|
+
|
|
167534
|
+
// src/channels/signal/target.ts
|
|
167535
|
+
function assertSignalHttpProtocol(url2) {
|
|
167536
|
+
if (url2.protocol !== "http:" && url2.protocol !== "https:") {
|
|
167537
|
+
throw new Error(`Signal base URL protocol must be http or https, got ${url2.protocol}`);
|
|
167538
|
+
}
|
|
167539
|
+
}
|
|
167540
|
+
function trimPrefix(value, prefix) {
|
|
167541
|
+
if (!value.toLowerCase().startsWith(prefix)) {
|
|
167542
|
+
return null;
|
|
167543
|
+
}
|
|
167544
|
+
return value.slice(prefix.length).trim();
|
|
167545
|
+
}
|
|
167546
|
+
function normalizeSignalBaseUrl(input) {
|
|
167547
|
+
const trimmed = input.trim();
|
|
167548
|
+
if (!trimmed) {
|
|
167549
|
+
return "";
|
|
167550
|
+
}
|
|
167551
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed);
|
|
167552
|
+
const withScheme = hasScheme ? trimmed : `http://${trimmed}`;
|
|
167553
|
+
const parsed = new URL(withScheme);
|
|
167554
|
+
assertSignalHttpProtocol(parsed);
|
|
167555
|
+
if (parsed.username || parsed.password) {
|
|
167556
|
+
throw new Error("Signal base URL must not include credentials.");
|
|
167557
|
+
}
|
|
167558
|
+
return withScheme.replace(/\/+$/, "");
|
|
167559
|
+
}
|
|
167560
|
+
function parseSignalTarget(input) {
|
|
167561
|
+
const trimmed = input.trim();
|
|
167562
|
+
if (!trimmed) {
|
|
167563
|
+
throw new Error("Signal target is required.");
|
|
167564
|
+
}
|
|
167565
|
+
const signalTarget = trimPrefix(trimmed, "signal:");
|
|
167566
|
+
const value = signalTarget !== null ? signalTarget : trimmed;
|
|
167567
|
+
if (!value) {
|
|
167568
|
+
throw new Error("Signal target is required.");
|
|
167569
|
+
}
|
|
167570
|
+
const groupId = trimPrefix(value, "group:");
|
|
167571
|
+
if (groupId) {
|
|
167572
|
+
return { kind: "group", groupId };
|
|
167573
|
+
}
|
|
167574
|
+
const username = trimPrefix(value, "username:");
|
|
167575
|
+
if (username) {
|
|
167576
|
+
return { kind: "username", username };
|
|
167577
|
+
}
|
|
167578
|
+
const usernameAlias = trimPrefix(value, "u:");
|
|
167579
|
+
if (usernameAlias) {
|
|
167580
|
+
return { kind: "username", username: `u:${usernameAlias}` };
|
|
167581
|
+
}
|
|
167582
|
+
const recipient = value;
|
|
167583
|
+
if (!recipient) {
|
|
167584
|
+
throw new Error("Signal recipient is required.");
|
|
167585
|
+
}
|
|
167586
|
+
return { kind: "recipient", recipient };
|
|
167587
|
+
}
|
|
167588
|
+
function signalTargetToSendRpcParams(target2) {
|
|
167589
|
+
switch (target2.kind) {
|
|
167590
|
+
case "group":
|
|
167591
|
+
return { groupId: target2.groupId };
|
|
167592
|
+
case "username":
|
|
167593
|
+
return { username: [target2.username] };
|
|
167594
|
+
case "recipient":
|
|
167595
|
+
return { recipient: [target2.recipient] };
|
|
167596
|
+
}
|
|
167597
|
+
}
|
|
167598
|
+
function signalTargetToReactionRpcParams(target2) {
|
|
167599
|
+
switch (target2.kind) {
|
|
167600
|
+
case "group":
|
|
167601
|
+
return { groupIds: [target2.groupId] };
|
|
167602
|
+
case "recipient":
|
|
167603
|
+
return { recipients: [target2.recipient] };
|
|
167604
|
+
case "username":
|
|
167605
|
+
throw new Error("Signal reactions require a recipient or group target.");
|
|
167606
|
+
}
|
|
167607
|
+
}
|
|
167608
|
+
function normalizeSignalSenderId(value) {
|
|
167609
|
+
if (!value) {
|
|
167610
|
+
return "";
|
|
167611
|
+
}
|
|
167612
|
+
const trimmed = value.trim();
|
|
167613
|
+
if (!trimmed) {
|
|
167614
|
+
return "";
|
|
167615
|
+
}
|
|
167616
|
+
return trimmed.toLowerCase();
|
|
167617
|
+
}
|
|
167618
|
+
function normalizeSignalPhone(value) {
|
|
167619
|
+
if (!value) {
|
|
167620
|
+
return "";
|
|
167621
|
+
}
|
|
167622
|
+
const trimmed = value.trim();
|
|
167623
|
+
if (!trimmed) {
|
|
167624
|
+
return "";
|
|
167625
|
+
}
|
|
167626
|
+
const withoutPrefix = trimmed.toLowerCase().startsWith("signal:") ? trimmed.slice("signal:".length) : trimmed;
|
|
167627
|
+
return withoutPrefix.replace(/[^0-9+]/g, "");
|
|
167628
|
+
}
|
|
167629
|
+
function signalAllowedUsersIncludes(allowedUsers, senderId) {
|
|
167630
|
+
const normalizedSender = normalizeSignalSenderId(senderId);
|
|
167631
|
+
const senderPhone = normalizeSignalPhone(senderId);
|
|
167632
|
+
return allowedUsers.some((entry) => {
|
|
167633
|
+
const normalizedEntry = normalizeSignalSenderId(entry);
|
|
167634
|
+
if (normalizedEntry === normalizedSender) {
|
|
167635
|
+
return true;
|
|
167636
|
+
}
|
|
167637
|
+
const entryPhone = normalizeSignalPhone(entry);
|
|
167638
|
+
return !!senderPhone && !!entryPhone && senderPhone === entryPhone;
|
|
167639
|
+
});
|
|
167640
|
+
}
|
|
167641
|
+
function isSignalGroupAllowed(allowedGroups, groupId) {
|
|
167642
|
+
if (!allowedGroups || allowedGroups.length === 0) {
|
|
167643
|
+
return true;
|
|
167644
|
+
}
|
|
167645
|
+
const normalized = groupId.trim();
|
|
167646
|
+
return allowedGroups.some((entry) => entry.trim() === normalized);
|
|
167647
|
+
}
|
|
167648
|
+
function matchesSignalMentionPatterns(text, mentionPatterns) {
|
|
167649
|
+
const normalizedText = text.toLowerCase();
|
|
167650
|
+
return (mentionPatterns ?? []).map((entry) => entry.trim().toLowerCase()).filter(Boolean).some((entry) => normalizedText.includes(entry));
|
|
167651
|
+
}
|
|
167652
|
+
var init_target2 = () => {};
|
|
167653
|
+
|
|
167654
|
+
// src/channels/whatsapp/jid.ts
|
|
167655
|
+
function stripDeviceSuffix(jid) {
|
|
167656
|
+
if (!jid)
|
|
167657
|
+
return "";
|
|
167658
|
+
return jid.replace(/:\d+(@|$)/, "$1");
|
|
167659
|
+
}
|
|
167660
|
+
function isLidJid(jid) {
|
|
167661
|
+
return !!jid && stripDeviceSuffix(jid).endsWith(WHATSAPP_LID_SUFFIX);
|
|
167662
|
+
}
|
|
167663
|
+
function isGroupJid(jid) {
|
|
167664
|
+
return !!jid && stripDeviceSuffix(jid).endsWith(WHATSAPP_GROUP_SUFFIX);
|
|
167665
|
+
}
|
|
167666
|
+
function isStatusOrBroadcastJid(jid) {
|
|
167667
|
+
if (!jid)
|
|
167668
|
+
return true;
|
|
167669
|
+
const normalized = stripDeviceSuffix(jid);
|
|
167670
|
+
return normalized === "status@broadcast" || normalized.endsWith("@broadcast") || normalized.endsWith("@newsletter");
|
|
167671
|
+
}
|
|
167672
|
+
function jidToDigits(jid) {
|
|
167673
|
+
if (!jid)
|
|
167674
|
+
return "";
|
|
167675
|
+
const base2 = stripDeviceSuffix(jid).split("@")[0] ?? "";
|
|
167676
|
+
return base2.replace(/\D/g, "");
|
|
167677
|
+
}
|
|
167678
|
+
function normalizePhoneLike(value) {
|
|
167679
|
+
if (!value)
|
|
167680
|
+
return "";
|
|
167681
|
+
return jidToDigits(value.trim());
|
|
167682
|
+
}
|
|
167683
|
+
function phoneDigitsToJid(phoneDigits) {
|
|
167684
|
+
const digits = normalizePhoneLike(phoneDigits);
|
|
167685
|
+
return digits ? `${digits}${WHATSAPP_PHONE_SUFFIX}` : "";
|
|
167686
|
+
}
|
|
167687
|
+
function normalizeMaybePhoneJid(value) {
|
|
167688
|
+
if (!value)
|
|
167689
|
+
return null;
|
|
167690
|
+
const trimmed = value.trim();
|
|
167691
|
+
if (!trimmed)
|
|
167692
|
+
return null;
|
|
167693
|
+
if (isLidJid(trimmed))
|
|
167694
|
+
return null;
|
|
167695
|
+
if (trimmed.includes("@"))
|
|
167696
|
+
return stripDeviceSuffix(trimmed);
|
|
167697
|
+
return phoneDigitsToJid(trimmed) || null;
|
|
167698
|
+
}
|
|
167699
|
+
function isSelfChat(remoteJid, selfPhoneJid, selfLid) {
|
|
167700
|
+
const remote = stripDeviceSuffix(remoteJid);
|
|
167701
|
+
if (!remote)
|
|
167702
|
+
return false;
|
|
167703
|
+
const phone = stripDeviceSuffix(selfPhoneJid);
|
|
167704
|
+
if (phone && remote === phone)
|
|
167705
|
+
return true;
|
|
167706
|
+
const lid = stripDeviceSuffix(selfLid);
|
|
167707
|
+
if (lid && remote === lid)
|
|
167708
|
+
return true;
|
|
167709
|
+
return false;
|
|
167710
|
+
}
|
|
167711
|
+
function senderIdFromJid(jid) {
|
|
167712
|
+
return jidToDigits(jid);
|
|
167713
|
+
}
|
|
167714
|
+
function allowedUsersIncludes(allowedUsers, senderId) {
|
|
167715
|
+
const normalizedSender = normalizePhoneLike(senderId);
|
|
167716
|
+
return allowedUsers.some((entry) => normalizePhoneLike(entry) === normalizedSender);
|
|
167717
|
+
}
|
|
167718
|
+
function resolveLidToPhoneJid(params) {
|
|
167719
|
+
const { lidJid, message, sock } = params;
|
|
167720
|
+
if (!isLidJid(lidJid))
|
|
167721
|
+
return normalizeMaybePhoneJid(lidJid);
|
|
167722
|
+
const msg = message;
|
|
167723
|
+
const senderPn = normalizeMaybePhoneJid(msg?.key?.senderPn ?? undefined);
|
|
167724
|
+
if (senderPn)
|
|
167725
|
+
return senderPn;
|
|
167726
|
+
const repo = sock?.signalRepository;
|
|
167727
|
+
const mapped3 = normalizeMaybePhoneJid(repo?.lidMapping?.get(stripDeviceSuffix(lidJid)));
|
|
167728
|
+
if (mapped3)
|
|
167729
|
+
return mapped3;
|
|
167730
|
+
return null;
|
|
167731
|
+
}
|
|
167732
|
+
function resolveSendJid(params) {
|
|
167733
|
+
const { chatId, selfPhoneJid, selfLid, lidToJid, sock } = params;
|
|
167734
|
+
if (!isLidJid(chatId))
|
|
167735
|
+
return stripDeviceSuffix(chatId);
|
|
167736
|
+
const normalized = stripDeviceSuffix(chatId);
|
|
167737
|
+
if (selfLid && normalized === stripDeviceSuffix(selfLid) && selfPhoneJid) {
|
|
167738
|
+
return stripDeviceSuffix(selfPhoneJid);
|
|
167739
|
+
}
|
|
167740
|
+
const mapped3 = normalizeMaybePhoneJid(lidToJid?.get(normalized));
|
|
167741
|
+
if (mapped3)
|
|
167742
|
+
return mapped3;
|
|
167743
|
+
const repo = sock?.signalRepository;
|
|
167744
|
+
const signalMapped = normalizeMaybePhoneJid(repo?.lidMapping?.get(normalized));
|
|
167745
|
+
if (signalMapped)
|
|
167746
|
+
return signalMapped;
|
|
167747
|
+
throw new Error(`Cannot send to unresolved WhatsApp LID: ${chatId}`);
|
|
167748
|
+
}
|
|
167749
|
+
function sanitizePathSegment(input) {
|
|
167750
|
+
const cleaned = input.replace(/[^A-Za-z0-9._-]/g, "_").replace(/^_+|_+$/g, "");
|
|
167751
|
+
return cleaned || "whatsapp";
|
|
167752
|
+
}
|
|
167753
|
+
var WHATSAPP_PHONE_SUFFIX = "@s.whatsapp.net", WHATSAPP_LID_SUFFIX = "@lid", WHATSAPP_GROUP_SUFFIX = "@g.us";
|
|
167754
|
+
|
|
167755
|
+
// src/channels/access-control.ts
|
|
167756
|
+
function resolveChannelAccessScope(chatType) {
|
|
167757
|
+
return chatType === "channel" ? "group" : "dm";
|
|
167758
|
+
}
|
|
167759
|
+
function channelEnvKey(channelId, suffix) {
|
|
167760
|
+
const normalized = channelId.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
|
|
167761
|
+
return `LETTA_${normalized}_${suffix}`;
|
|
167762
|
+
}
|
|
167763
|
+
function parseUserList(raw) {
|
|
167764
|
+
if (!raw)
|
|
167765
|
+
return [];
|
|
167766
|
+
return raw.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
167767
|
+
}
|
|
167768
|
+
function isEnvFlagEnabled(raw) {
|
|
167769
|
+
const normalized = (raw ?? "").trim().toLowerCase();
|
|
167770
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
167771
|
+
}
|
|
167772
|
+
function getChannelEnvAllowedUsers(channelId) {
|
|
167773
|
+
return [
|
|
167774
|
+
...parseUserList(process.env[GLOBAL_ALLOWED_USERS_ENV]),
|
|
167775
|
+
...parseUserList(process.env[channelEnvKey(channelId, "ALLOWED_USERS")])
|
|
167776
|
+
];
|
|
167777
|
+
}
|
|
167778
|
+
function getChannelEnvAdminUsers(channelId) {
|
|
167779
|
+
return [
|
|
167780
|
+
...parseUserList(process.env[GLOBAL_ADMIN_USERS_ENV]),
|
|
167781
|
+
...parseUserList(process.env[channelEnvKey(channelId, "ADMIN_USERS")])
|
|
167782
|
+
];
|
|
167783
|
+
}
|
|
167784
|
+
function isChannelAllowAllUsersEnabled(channelId) {
|
|
167785
|
+
return isEnvFlagEnabled(process.env[GLOBAL_ALLOW_ALL_ENV]) || isEnvFlagEnabled(process.env[channelEnvKey(channelId, "ALLOW_ALL_USERS")]);
|
|
167786
|
+
}
|
|
167787
|
+
function effectiveAllowedUsers(account, channelId) {
|
|
167788
|
+
return [
|
|
167789
|
+
...account.allowedUsers,
|
|
167790
|
+
...account.adminUsers ?? [],
|
|
167791
|
+
...getChannelEnvAllowedUsers(channelId),
|
|
167792
|
+
...getChannelEnvAdminUsers(channelId)
|
|
167793
|
+
];
|
|
167794
|
+
}
|
|
167795
|
+
function allowlistMatches(channelId, allowed, senderId) {
|
|
167796
|
+
if (allowed.includes("*")) {
|
|
167797
|
+
return true;
|
|
167798
|
+
}
|
|
167799
|
+
if (!senderId) {
|
|
167800
|
+
return false;
|
|
167801
|
+
}
|
|
167802
|
+
if (channelId === "whatsapp") {
|
|
167803
|
+
return allowedUsersIncludes(allowed, senderId);
|
|
167804
|
+
}
|
|
167805
|
+
if (channelId === "signal") {
|
|
167806
|
+
return signalAllowedUsersIncludes(allowed, senderId);
|
|
167807
|
+
}
|
|
167808
|
+
return allowed.includes(senderId);
|
|
167809
|
+
}
|
|
167810
|
+
function isPairedApproved(channelId, senderId, accountId) {
|
|
167811
|
+
if (isUserApproved(channelId, senderId, accountId)) {
|
|
167812
|
+
return true;
|
|
167813
|
+
}
|
|
167814
|
+
loadPairingStore(channelId);
|
|
167815
|
+
return isUserApproved(channelId, senderId, accountId);
|
|
167816
|
+
}
|
|
167817
|
+
function evaluateChannelSenderAccess(input) {
|
|
167818
|
+
const { account, channelId, senderId, chatType } = input;
|
|
167819
|
+
if (isChannelAllowAllUsersEnabled(channelId)) {
|
|
167820
|
+
return "allow";
|
|
167821
|
+
}
|
|
167822
|
+
const allowed = effectiveAllowedUsers(account, channelId);
|
|
167823
|
+
if (allowlistMatches(channelId, allowed, senderId)) {
|
|
167824
|
+
return "allow";
|
|
167825
|
+
}
|
|
167826
|
+
const envAllowlistConfigured = getChannelEnvAllowedUsers(channelId).length > 0;
|
|
167827
|
+
const scope = resolveChannelAccessScope(chatType);
|
|
167828
|
+
const restrictive = envAllowlistConfigured || (scope === "group" ? (account.groupPolicy ?? "open") === "allowlist" : account.dmPolicy !== "open");
|
|
167829
|
+
if (!restrictive) {
|
|
167830
|
+
return "allow";
|
|
167831
|
+
}
|
|
167832
|
+
if (senderId && isPairedApproved(channelId, senderId, account.accountId)) {
|
|
167833
|
+
return "allow";
|
|
167834
|
+
}
|
|
167835
|
+
if (scope === "group") {
|
|
167836
|
+
return "deny";
|
|
167837
|
+
}
|
|
167838
|
+
if (account.dmPolicy === "pairing" && !envAllowlistConfigured) {
|
|
167839
|
+
return channelId === "slack" ? "allow" : "pair";
|
|
167840
|
+
}
|
|
167841
|
+
return "deny";
|
|
167842
|
+
}
|
|
167843
|
+
function buildChannelAccessDeniedMessage(channelId) {
|
|
167844
|
+
const noun = ACCESS_DENIED_NOUNS[channelId] ?? "bot";
|
|
167845
|
+
return `You are not on the allowed users list for this ${noun}.`;
|
|
167846
|
+
}
|
|
167847
|
+
function floorOnlyChannelCommandGate() {
|
|
167848
|
+
return {
|
|
167849
|
+
enabled: true,
|
|
167850
|
+
isAdmin: false,
|
|
167851
|
+
allowedCommands: [],
|
|
167852
|
+
pairingPending: true
|
|
167853
|
+
};
|
|
167854
|
+
}
|
|
167855
|
+
function normalizeCommandName(name) {
|
|
167856
|
+
return name.trim().replace(/^\/+/, "").toLowerCase();
|
|
167857
|
+
}
|
|
167858
|
+
function canonicalizeChannelCommandName(name) {
|
|
167859
|
+
const normalized = normalizeCommandName(name);
|
|
167860
|
+
return normalized === "reflect" ? "reflection" : normalized;
|
|
167861
|
+
}
|
|
167862
|
+
function resolveChannelCommandGate(params) {
|
|
167863
|
+
const admins = [
|
|
167864
|
+
...params.account.adminUsers ?? [],
|
|
167865
|
+
...getChannelEnvAdminUsers(params.channelId)
|
|
167866
|
+
].filter((entry) => entry.length > 0);
|
|
167867
|
+
const enabled = admins.length > 0;
|
|
167868
|
+
const allowedCommands = (params.account.userAllowedCommands ?? []).map(canonicalizeChannelCommandName).filter((name) => name.length > 0);
|
|
167869
|
+
return {
|
|
167870
|
+
enabled,
|
|
167871
|
+
isAdmin: !enabled || allowlistMatches(params.channelId, admins, params.senderId),
|
|
167872
|
+
allowedCommands
|
|
167873
|
+
};
|
|
167874
|
+
}
|
|
167875
|
+
function canRunChannelCommand(gate, commandName) {
|
|
167876
|
+
if (!gate.enabled || gate.isAdmin) {
|
|
167877
|
+
return true;
|
|
167878
|
+
}
|
|
167879
|
+
const canonical = canonicalizeChannelCommandName(commandName);
|
|
167880
|
+
return CHANNEL_COMMAND_FLOOR.includes(canonical) || gate.allowedCommands.includes(canonical);
|
|
167881
|
+
}
|
|
167882
|
+
function runnableCommandsForUser(gate) {
|
|
167883
|
+
const seen = new Set;
|
|
167884
|
+
const runnable = [];
|
|
167885
|
+
for (const name of [...CHANNEL_COMMAND_FLOOR, ...gate.allowedCommands]) {
|
|
167886
|
+
if (!seen.has(name)) {
|
|
167887
|
+
seen.add(name);
|
|
167888
|
+
runnable.push(name);
|
|
167889
|
+
}
|
|
167890
|
+
}
|
|
167891
|
+
return runnable;
|
|
167892
|
+
}
|
|
167338
167893
|
function channelDisplayName(channelId) {
|
|
167339
167894
|
try {
|
|
167340
167895
|
return getChannelDisplayName(channelId);
|
|
@@ -167342,8 +167897,60 @@ function channelDisplayName(channelId) {
|
|
|
167342
167897
|
return channelId;
|
|
167343
167898
|
}
|
|
167344
167899
|
}
|
|
167900
|
+
function buildChannelCommandDeniedMessage(channelId, commandName, gate) {
|
|
167901
|
+
const runnable = runnableCommandsForUser(gate).map((name) => `/${name}`).join(", ");
|
|
167902
|
+
const restriction = gate.pairingPending ? "is available after pairing completes" : "is limited to admins here";
|
|
167903
|
+
return [
|
|
167904
|
+
`${channelDisplayName(channelId)}: /${normalizeCommandName(commandName)} ${restriction}.`,
|
|
167905
|
+
`Commands you can run: ${runnable}. Use /whoami to see your access.`
|
|
167906
|
+
].join(`
|
|
167907
|
+
`);
|
|
167908
|
+
}
|
|
167909
|
+
function buildChannelWhoamiMessage(msg, gate) {
|
|
167910
|
+
const scope = resolveChannelAccessScope(msg.chatType) === "dm" ? "DM" : "group/channel";
|
|
167911
|
+
const who = msg.senderName ? `${msg.senderName} (${msg.senderId})` : msg.senderId;
|
|
167912
|
+
const lines = [
|
|
167913
|
+
`You — ${channelDisplayName(msg.channel)} (${scope})`,
|
|
167914
|
+
`User ID: ${who}`
|
|
167915
|
+
];
|
|
167916
|
+
if (!gate || !gate.enabled) {
|
|
167917
|
+
lines.push("Tier: unrestricted (no admin list configured for this account)", "Commands: all available");
|
|
167918
|
+
} else if (gate.pairingPending) {
|
|
167919
|
+
const runnable = runnableCommandsForUser(gate).map((name) => `/${name}`).join(", ");
|
|
167920
|
+
lines.push("Tier: pending pairing", `Commands you can run: ${runnable}`);
|
|
167921
|
+
} else if (gate.isAdmin) {
|
|
167922
|
+
lines.push("Tier: admin", "Commands: all available");
|
|
167923
|
+
} else {
|
|
167924
|
+
const runnable = runnableCommandsForUser(gate).map((name) => `/${name}`).join(", ");
|
|
167925
|
+
lines.push("Tier: user", `Commands you can run: ${runnable}`);
|
|
167926
|
+
}
|
|
167927
|
+
return lines.join(`
|
|
167928
|
+
`);
|
|
167929
|
+
}
|
|
167930
|
+
var GLOBAL_ALLOWED_USERS_ENV = "LETTA_CHANNELS_ALLOWED_USERS", GLOBAL_ADMIN_USERS_ENV = "LETTA_CHANNELS_ADMIN_USERS", GLOBAL_ALLOW_ALL_ENV = "LETTA_CHANNELS_ALLOW_ALL_USERS", ACCESS_DENIED_NOUNS, CHANNEL_COMMAND_FLOOR;
|
|
167931
|
+
var init_access_control = __esm(() => {
|
|
167932
|
+
init_pairing();
|
|
167933
|
+
init_plugin_registry();
|
|
167934
|
+
init_target2();
|
|
167935
|
+
ACCESS_DENIED_NOUNS = {
|
|
167936
|
+
slack: "Slack app",
|
|
167937
|
+
discord: "Discord bot",
|
|
167938
|
+
whatsapp: "WhatsApp account",
|
|
167939
|
+
signal: "Signal account"
|
|
167940
|
+
};
|
|
167941
|
+
CHANNEL_COMMAND_FLOOR = ["help", "status", "whoami"];
|
|
167942
|
+
});
|
|
167943
|
+
|
|
167944
|
+
// src/channels/feedback.ts
|
|
167945
|
+
function channelDisplayName2(channelId) {
|
|
167946
|
+
try {
|
|
167947
|
+
return getChannelDisplayName(channelId);
|
|
167948
|
+
} catch {
|
|
167949
|
+
return channelId;
|
|
167950
|
+
}
|
|
167951
|
+
}
|
|
167345
167952
|
function buildChannelFeedbackUsageMessage(channelId) {
|
|
167346
|
-
const displayName =
|
|
167953
|
+
const displayName = channelDisplayName2(channelId);
|
|
167347
167954
|
return [
|
|
167348
167955
|
`${displayName} received /feedback without a message.`,
|
|
167349
167956
|
"Usage: /feedback <message>"
|
|
@@ -167352,11 +167959,11 @@ function buildChannelFeedbackUsageMessage(channelId) {
|
|
|
167352
167959
|
`);
|
|
167353
167960
|
}
|
|
167354
167961
|
function buildChannelFeedbackTooLongMessage(channelId, maxLength3 = CHANNEL_FEEDBACK_MESSAGE_MAX) {
|
|
167355
|
-
const displayName =
|
|
167962
|
+
const displayName = channelDisplayName2(channelId);
|
|
167356
167963
|
return `${displayName} feedback message is too long. Maximum is ${maxLength3.toLocaleString()} characters.`;
|
|
167357
167964
|
}
|
|
167358
167965
|
function buildChannelFeedbackNoRouteMessage(channelId) {
|
|
167359
|
-
const displayName =
|
|
167966
|
+
const displayName = channelDisplayName2(channelId);
|
|
167360
167967
|
const instruction = channelId === "slack" ? "Mention the app with a normal message in this chat or thread first so it can connect, then send /feedback <message> while mentioning the app." : "Send a normal message first and follow the pairing instructions, then try /feedback <message>.";
|
|
167361
167968
|
return [
|
|
167362
167969
|
`${displayName} cannot submit /feedback until this chat is connected to a Letta agent conversation.`,
|
|
@@ -167366,11 +167973,11 @@ function buildChannelFeedbackNoRouteMessage(channelId) {
|
|
|
167366
167973
|
`);
|
|
167367
167974
|
}
|
|
167368
167975
|
function buildChannelFeedbackSubmittedMessage(channelId) {
|
|
167369
|
-
const displayName =
|
|
167976
|
+
const displayName = channelDisplayName2(channelId);
|
|
167370
167977
|
return `${displayName} feedback submitted. Thanks for helping improve Letta Code.`;
|
|
167371
167978
|
}
|
|
167372
167979
|
function buildChannelFeedbackFailedMessage(channelId) {
|
|
167373
|
-
const displayName =
|
|
167980
|
+
const displayName = channelDisplayName2(channelId);
|
|
167374
167981
|
return `${displayName} could not submit feedback right now. Please try again later.`;
|
|
167375
167982
|
}
|
|
167376
167983
|
function withDefinedValues2(payload) {
|
|
@@ -167433,7 +168040,7 @@ var init_feedback2 = __esm(() => {
|
|
|
167433
168040
|
});
|
|
167434
168041
|
|
|
167435
168042
|
// src/channels/commands.ts
|
|
167436
|
-
function
|
|
168043
|
+
function channelDisplayName3(channelId) {
|
|
167437
168044
|
try {
|
|
167438
168045
|
return getChannelDisplayName(channelId);
|
|
167439
168046
|
} catch {
|
|
@@ -167516,7 +168123,7 @@ function isSlackMentionControlCommand(msg, command) {
|
|
|
167516
168123
|
return command.raw.startsWith("!") || isSlackMentionSlashCommand(msg, command);
|
|
167517
168124
|
}
|
|
167518
168125
|
function buildChannelHelpMessage(channelId) {
|
|
167519
|
-
const displayName =
|
|
168126
|
+
const displayName = channelDisplayName3(channelId);
|
|
167520
168127
|
if (channelId === "slack") {
|
|
167521
168128
|
return [
|
|
167522
168129
|
`${displayName} is connected to Letta Code.`,
|
|
@@ -167548,7 +168155,7 @@ function buildChannelHelpMessage(channelId) {
|
|
|
167548
168155
|
`);
|
|
167549
168156
|
}
|
|
167550
168157
|
function buildUnsupportedChannelCommandMessage(channelId, command) {
|
|
167551
|
-
const displayName =
|
|
168158
|
+
const displayName = channelDisplayName3(channelId);
|
|
167552
168159
|
const isBang = command.raw.startsWith("!");
|
|
167553
168160
|
const commandKind = isBang ? "bang" : "slash";
|
|
167554
168161
|
const supportedCommands = isBang ? supportedBangCommandsText() : channelId === "slack" ? supportedSlackMentionSlashCommandsText() : supportedCommandsText();
|
|
@@ -167562,7 +168169,7 @@ function buildUnsupportedChannelCommandMessage(channelId, command) {
|
|
|
167562
168169
|
`);
|
|
167563
168170
|
}
|
|
167564
168171
|
function buildChannelStatusMessage(msg, context3) {
|
|
167565
|
-
const displayName =
|
|
168172
|
+
const displayName = channelDisplayName3(msg.channel);
|
|
167566
168173
|
const route = context3.route;
|
|
167567
168174
|
const routeStatus = route ? "Connected to a Letta agent conversation." : "No route is connected for this chat yet.";
|
|
167568
168175
|
const accountStatus = !context3.accountConfigured ? "No channel account is configured for this receiver." : context3.accountEnabled === false ? "Channel account is configured but disabled." : "Channel account is configured and enabled.";
|
|
@@ -167590,7 +168197,7 @@ function buildChannelStatusMessage(msg, context3) {
|
|
|
167590
168197
|
`);
|
|
167591
168198
|
}
|
|
167592
168199
|
function buildChannelNoRouteMessage(channelId) {
|
|
167593
|
-
const displayName =
|
|
168200
|
+
const displayName = channelDisplayName3(channelId);
|
|
167594
168201
|
return [
|
|
167595
168202
|
`${displayName} could not find an existing route for this chat.`,
|
|
167596
168203
|
"Send a normal message first and follow the pairing instructions, then try again."
|
|
@@ -167599,23 +168206,23 @@ function buildChannelNoRouteMessage(channelId) {
|
|
|
167599
168206
|
`);
|
|
167600
168207
|
}
|
|
167601
168208
|
function buildChannelPausedMessage(channelId, route) {
|
|
167602
|
-
const displayName =
|
|
168209
|
+
const displayName = channelDisplayName3(channelId);
|
|
167603
168210
|
const conversation = route.conversationId ? ` Conversation: ${route.conversationId}.` : "";
|
|
167604
168211
|
return `${displayName} paused agent routing for this chat.${conversation} Send /resume here to turn replies back on.`;
|
|
167605
168212
|
}
|
|
167606
168213
|
function buildChannelAlreadyPausedMessage(channelId) {
|
|
167607
|
-
return `${
|
|
168214
|
+
return `${channelDisplayName3(channelId)} agent routing is already paused for this chat. Send /resume here to turn replies back on.`;
|
|
167608
168215
|
}
|
|
167609
168216
|
function buildChannelResumedMessage(channelId, route) {
|
|
167610
|
-
const displayName =
|
|
168217
|
+
const displayName = channelDisplayName3(channelId);
|
|
167611
168218
|
const conversation = route.conversationId ? ` Conversation: ${route.conversationId}.` : "";
|
|
167612
168219
|
return `${displayName} resumed agent routing for this chat.${conversation} Normal messages here will go to the connected agent again.`;
|
|
167613
168220
|
}
|
|
167614
168221
|
function buildChannelAlreadyActiveMessage(channelId) {
|
|
167615
|
-
return `${
|
|
168222
|
+
return `${channelDisplayName3(channelId)} agent routing is already active for this chat.`;
|
|
167616
168223
|
}
|
|
167617
168224
|
function buildChannelCancelUnavailableMessage(channelId) {
|
|
167618
|
-
const displayName =
|
|
168225
|
+
const displayName = channelDisplayName3(channelId);
|
|
167619
168226
|
return [
|
|
167620
168227
|
`${displayName} received /cancel, but this chat is not connected to an active Letta Code conversation yet.`,
|
|
167621
168228
|
"Send a normal message first to connect this chat to an agent."
|
|
@@ -167624,15 +168231,15 @@ function buildChannelCancelUnavailableMessage(channelId) {
|
|
|
167624
168231
|
`);
|
|
167625
168232
|
}
|
|
167626
168233
|
function buildChannelCancelNoActiveTurnMessage(channelId) {
|
|
167627
|
-
const displayName =
|
|
168234
|
+
const displayName = channelDisplayName3(channelId);
|
|
167628
168235
|
return `${displayName} received /cancel, but there is no in-progress agent turn to cancel for this chat.`;
|
|
167629
168236
|
}
|
|
167630
168237
|
function buildChannelCancelAcceptedMessage(channelId) {
|
|
167631
|
-
const displayName =
|
|
168238
|
+
const displayName = channelDisplayName3(channelId);
|
|
167632
168239
|
return `${displayName} cancelled the in-progress agent turn for this chat.`;
|
|
167633
168240
|
}
|
|
167634
168241
|
function buildChannelChatLinkMessage(channelId, route, chatUrl) {
|
|
167635
|
-
const displayName =
|
|
168242
|
+
const displayName = channelDisplayName3(channelId);
|
|
167636
168243
|
return [
|
|
167637
168244
|
`${displayName} chat for this route: ${chatUrl}`,
|
|
167638
168245
|
`Agent: ${route.agentId}.`,
|
|
@@ -167641,27 +168248,27 @@ function buildChannelChatLinkMessage(channelId, route, chatUrl) {
|
|
|
167641
168248
|
`);
|
|
167642
168249
|
}
|
|
167643
168250
|
function buildChannelChatUnavailableMessage(channelId, route) {
|
|
167644
|
-
const displayName =
|
|
168251
|
+
const displayName = channelDisplayName3(channelId);
|
|
167645
168252
|
return `${displayName} chat UI is not available for local backend agent ${route.agentId}.`;
|
|
167646
168253
|
}
|
|
167647
168254
|
function buildChannelDetachUnsupportedMessage(channelId) {
|
|
167648
|
-
const displayName =
|
|
168255
|
+
const displayName = channelDisplayName3(channelId);
|
|
167649
168256
|
return `${displayName} can only detach Slack channel threads.`;
|
|
167650
168257
|
}
|
|
167651
168258
|
function buildChannelDetachedMessage(channelId) {
|
|
167652
|
-
const displayName =
|
|
168259
|
+
const displayName = channelDisplayName3(channelId);
|
|
167653
168260
|
return `${displayName} detached this thread. I will ignore follow-up replies here until someone mentions the app again.`;
|
|
167654
168261
|
}
|
|
167655
168262
|
function buildChannelAlreadyDetachedMessage(channelId) {
|
|
167656
|
-
const displayName =
|
|
168263
|
+
const displayName = channelDisplayName3(channelId);
|
|
167657
168264
|
return `${displayName} is already detached from this thread. Mention the app again to reattach.`;
|
|
167658
168265
|
}
|
|
167659
168266
|
function buildChannelNewConversationMessage(channelId, route) {
|
|
167660
|
-
const displayName =
|
|
168267
|
+
const displayName = channelDisplayName3(channelId);
|
|
167661
168268
|
return `${displayName} started a new conversation for this chat. Conversation: ${route.conversationId}.`;
|
|
167662
168269
|
}
|
|
167663
168270
|
function buildChannelNewConversationUnavailableMessage(channelId) {
|
|
167664
|
-
const displayName =
|
|
168271
|
+
const displayName = channelDisplayName3(channelId);
|
|
167665
168272
|
return `${displayName} cannot start a new conversation for this chat because no agent is configured.`;
|
|
167666
168273
|
}
|
|
167667
168274
|
function getModelEntryRank(entry) {
|
|
@@ -167720,7 +168327,7 @@ function buildChannelModelNotFoundText(channelId) {
|
|
|
167720
168327
|
return `Model not found. Use ${modelCommandPrefix(channelId)} list to see available models.`;
|
|
167721
168328
|
}
|
|
167722
168329
|
function buildChannelCurrentModelMessage(channelId, params) {
|
|
167723
|
-
const displayName =
|
|
168330
|
+
const displayName = channelDisplayName3(channelId);
|
|
167724
168331
|
const scope = params.scope === "agent" ? "agent" : "conversation";
|
|
167725
168332
|
const handleText = params.modelHandle && params.modelHandle !== params.modelLabel ? ` (${params.modelHandle})` : "";
|
|
167726
168333
|
const switchCommand = modelCommandPrefix(channelId);
|
|
@@ -167748,7 +168355,7 @@ function appendModelEntrySection(lines, channelId, title, entries, limit3) {
|
|
|
167748
168355
|
}
|
|
167749
168356
|
}
|
|
167750
168357
|
function buildChannelModelListMessage(channelId, params) {
|
|
167751
|
-
const displayName =
|
|
168358
|
+
const displayName = channelDisplayName3(channelId);
|
|
167752
168359
|
const limit3 = params.limit ?? DEFAULT_CHANNEL_MODEL_LIST_LIMIT;
|
|
167753
168360
|
const entries = params.entries;
|
|
167754
168361
|
const byHandle = buildModelEntriesByHandle(entries);
|
|
@@ -167781,33 +168388,33 @@ function buildChannelModelListMessage(channelId, params) {
|
|
|
167781
168388
|
`);
|
|
167782
168389
|
}
|
|
167783
168390
|
function buildChannelModelListUnavailableMessage(channelId, error54) {
|
|
167784
|
-
const displayName =
|
|
168391
|
+
const displayName = channelDisplayName3(channelId);
|
|
167785
168392
|
return `${displayName} could not load the model list: ${error54}`;
|
|
167786
168393
|
}
|
|
167787
168394
|
function buildChannelCurrentModelUnavailableMessage(channelId, error54) {
|
|
167788
|
-
const displayName =
|
|
168395
|
+
const displayName = channelDisplayName3(channelId);
|
|
167789
168396
|
return `${displayName} could not load the current model: ${error54}`;
|
|
167790
168397
|
}
|
|
167791
168398
|
function buildChannelModelUpdatedMessage(channelId, params) {
|
|
167792
|
-
const displayName =
|
|
168399
|
+
const displayName = channelDisplayName3(channelId);
|
|
167793
168400
|
const scope = params.appliedTo === "agent" ? "agent" : "conversation";
|
|
167794
168401
|
const handleText = params.modelHandle === params.modelLabel ? "" : ` (${params.modelHandle})`;
|
|
167795
168402
|
return `${displayName} updated this ${scope}'s model to ${params.modelLabel}${handleText}.`;
|
|
167796
168403
|
}
|
|
167797
168404
|
function buildChannelModelUpdateFailedMessage(channelId, identifier2, error54) {
|
|
167798
|
-
const displayName =
|
|
168405
|
+
const displayName = channelDisplayName3(channelId);
|
|
167799
168406
|
return `${displayName} could not switch this chat's routed model to ${identifier2}: ${error54}`;
|
|
167800
168407
|
}
|
|
167801
168408
|
function buildChannelModelUnavailableMessage(channelId) {
|
|
167802
|
-
const displayName =
|
|
168409
|
+
const displayName = channelDisplayName3(channelId);
|
|
167803
168410
|
return `${displayName} cannot use /model because the listener is not ready yet. Try again in a moment.`;
|
|
167804
168411
|
}
|
|
167805
168412
|
function buildChannelReflectionUnavailableMessage(channelId) {
|
|
167806
|
-
const displayName =
|
|
168413
|
+
const displayName = channelDisplayName3(channelId);
|
|
167807
168414
|
return `${displayName} cannot start reflection for this chat because the listener is not ready yet. Try again in a moment.`;
|
|
167808
168415
|
}
|
|
167809
168416
|
function buildChannelReloadUnavailableMessage(channelId) {
|
|
167810
|
-
const displayName =
|
|
168417
|
+
const displayName = channelDisplayName3(channelId);
|
|
167811
168418
|
return `${displayName} cannot reload listener settings for this chat because the listener is not ready yet. Try again in a moment.`;
|
|
167812
168419
|
}
|
|
167813
168420
|
async function handleScopedCommand(params) {
|
|
@@ -167844,10 +168451,17 @@ async function tryHandleChannelSlashCommand(adapter, msg, options3 = {}) {
|
|
|
167844
168451
|
await adapter.sendDirectReply(msg.chatId, buildUnsupportedChannelCommandMessage(msg.channel, command), msg.threadId ? { replyToMessageId: msg.threadId } : undefined);
|
|
167845
168452
|
return true;
|
|
167846
168453
|
}
|
|
168454
|
+
const canonicalName = canonicalizeChannelCommandName(command.name);
|
|
168455
|
+
if (options3.commandGate && !canRunChannelCommand(options3.commandGate, canonicalName)) {
|
|
168456
|
+
await adapter.sendDirectReply(msg.chatId, buildChannelCommandDeniedMessage(msg.channel, canonicalName, options3.commandGate), msg.threadId ? { replyToMessageId: msg.threadId } : undefined);
|
|
168457
|
+
return true;
|
|
168458
|
+
}
|
|
167847
168459
|
const reply = normalizeDirectReplyPayload(await (async () => {
|
|
167848
168460
|
switch (command.name) {
|
|
167849
168461
|
case "help":
|
|
167850
168462
|
return buildChannelHelpMessage(msg.channel);
|
|
168463
|
+
case "whoami":
|
|
168464
|
+
return buildChannelWhoamiMessage(msg, options3.commandGate);
|
|
167851
168465
|
case "status":
|
|
167852
168466
|
return buildChannelStatusMessage(msg, options3.statusContext ?? {
|
|
167853
168467
|
adapterRunning: adapter.isRunning(),
|
|
@@ -167941,6 +168555,7 @@ async function tryHandleChannelSlashCommand(adapter, msg, options3 = {}) {
|
|
|
167941
168555
|
}
|
|
167942
168556
|
var CHANNEL_SLASH_COMMANDS, SLACK_MENTION_COMMAND_NAMES, SLACK_MENTION_SLASH_COMMAND_EXAMPLES, DEFAULT_CHANNEL_MODEL_LIST_LIMIT = 8;
|
|
167943
168557
|
var init_commands = __esm(() => {
|
|
168558
|
+
init_access_control();
|
|
167944
168559
|
init_feedback2();
|
|
167945
168560
|
init_plugin_registry();
|
|
167946
168561
|
CHANNEL_SLASH_COMMANDS = [
|
|
@@ -167954,6 +168569,11 @@ var init_commands = __esm(() => {
|
|
|
167954
168569
|
kind: "direct",
|
|
167955
168570
|
summary: "Show this chat's channel connection status."
|
|
167956
168571
|
},
|
|
168572
|
+
{
|
|
168573
|
+
name: "whoami",
|
|
168574
|
+
kind: "direct",
|
|
168575
|
+
summary: "Show your access tier and runnable commands here."
|
|
168576
|
+
},
|
|
167957
168577
|
{
|
|
167958
168578
|
name: "pause",
|
|
167959
168579
|
kind: "direct",
|
|
@@ -168001,6 +168621,7 @@ var init_commands = __esm(() => {
|
|
|
168001
168621
|
SLACK_MENTION_SLASH_COMMAND_EXAMPLES = [
|
|
168002
168622
|
"@agent /help",
|
|
168003
168623
|
"@agent /status",
|
|
168624
|
+
"@agent /whoami",
|
|
168004
168625
|
"@agent /model",
|
|
168005
168626
|
"@agent /model list",
|
|
168006
168627
|
"@agent /model <handle-or-id>",
|
|
@@ -168100,19 +168721,24 @@ Handle: ${escapeSlackMrkdwn(params.current.modelHandle)}` : "";
|
|
|
168100
168721
|
text: {
|
|
168101
168722
|
type: "mrkdwn",
|
|
168102
168723
|
text: "Choose a model for this routed conversation:"
|
|
168103
|
-
},
|
|
168104
|
-
accessory: {
|
|
168105
|
-
type: "static_select",
|
|
168106
|
-
action_id: SLACK_MODEL_SELECT_ACTION_ID,
|
|
168107
|
-
placeholder: {
|
|
168108
|
-
type: "plain_text",
|
|
168109
|
-
text: "Select a model",
|
|
168110
|
-
emoji: true
|
|
168111
|
-
},
|
|
168112
|
-
options: options3,
|
|
168113
|
-
...initialOption ? { initial_option: initialOption } : {}
|
|
168114
168724
|
}
|
|
168115
168725
|
},
|
|
168726
|
+
{
|
|
168727
|
+
type: "actions",
|
|
168728
|
+
elements: [
|
|
168729
|
+
{
|
|
168730
|
+
type: "static_select",
|
|
168731
|
+
action_id: SLACK_MODEL_SELECT_ACTION_ID,
|
|
168732
|
+
placeholder: {
|
|
168733
|
+
type: "plain_text",
|
|
168734
|
+
text: "Select a model",
|
|
168735
|
+
emoji: true
|
|
168736
|
+
},
|
|
168737
|
+
options: options3,
|
|
168738
|
+
...initialOption ? { initial_option: initialOption } : {}
|
|
168739
|
+
}
|
|
168740
|
+
]
|
|
168741
|
+
},
|
|
168116
168742
|
{
|
|
168117
168743
|
type: "context",
|
|
168118
168744
|
elements: [
|
|
@@ -170977,9 +171603,9 @@ __export(exports_process_manager, {
|
|
|
170977
171603
|
import {
|
|
170978
171604
|
appendFileSync as appendFileSync2,
|
|
170979
171605
|
chmodSync as chmodSync2,
|
|
170980
|
-
mkdirSync as
|
|
171606
|
+
mkdirSync as mkdirSync6,
|
|
170981
171607
|
mkdtempSync as mkdtempSync2,
|
|
170982
|
-
writeFileSync as
|
|
171608
|
+
writeFileSync as writeFileSync4
|
|
170983
171609
|
} from "node:fs";
|
|
170984
171610
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
170985
171611
|
import { join as join13 } from "node:path";
|
|
@@ -171138,7 +171764,7 @@ function __resetBackgroundOutputDirForTests() {
|
|
|
171138
171764
|
backgroundOutputDir = undefined;
|
|
171139
171765
|
}
|
|
171140
171766
|
function ensureBackgroundOutputDir(dir) {
|
|
171141
|
-
|
|
171767
|
+
mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
171142
171768
|
if (!process.env.LETTA_SCRATCHPAD) {
|
|
171143
171769
|
chmodSync2(dir, 448);
|
|
171144
171770
|
}
|
|
@@ -171147,7 +171773,7 @@ function createBackgroundOutputFile(id2) {
|
|
|
171147
171773
|
const dir = getBackgroundOutputDir();
|
|
171148
171774
|
ensureBackgroundOutputDir(dir);
|
|
171149
171775
|
const filePath = join13(dir, `${id2}.log`);
|
|
171150
|
-
|
|
171776
|
+
writeFileSync4(filePath, "", { mode: 384 });
|
|
171151
171777
|
chmodSync2(filePath, 384);
|
|
171152
171778
|
return filePath;
|
|
171153
171779
|
}
|
|
@@ -171240,12 +171866,12 @@ var init_attachment_task = __esm(() => {
|
|
|
171240
171866
|
});
|
|
171241
171867
|
|
|
171242
171868
|
// src/channels/targets.ts
|
|
171243
|
-
import { existsSync as
|
|
171244
|
-
function
|
|
171245
|
-
let store =
|
|
171869
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
171870
|
+
function getStore3(channelId) {
|
|
171871
|
+
let store = stores3.get(channelId);
|
|
171246
171872
|
if (!store) {
|
|
171247
171873
|
store = { targets: [] };
|
|
171248
|
-
|
|
171874
|
+
stores3.set(channelId, store);
|
|
171249
171875
|
}
|
|
171250
171876
|
return store;
|
|
171251
171877
|
}
|
|
@@ -171255,35 +171881,35 @@ function loadTargetStore(channelId) {
|
|
|
171255
171881
|
return;
|
|
171256
171882
|
}
|
|
171257
171883
|
const path5 = getChannelTargetsPath(channelId);
|
|
171258
|
-
if (!
|
|
171884
|
+
if (!existsSync9(path5)) {
|
|
171259
171885
|
return;
|
|
171260
171886
|
}
|
|
171261
171887
|
try {
|
|
171262
|
-
const text =
|
|
171888
|
+
const text = readFileSync8(path5, "utf-8");
|
|
171263
171889
|
const parsed = JSON.parse(text);
|
|
171264
|
-
|
|
171890
|
+
stores3.set(channelId, {
|
|
171265
171891
|
targets: parsed.targets ?? []
|
|
171266
171892
|
});
|
|
171267
171893
|
} catch {}
|
|
171268
171894
|
}
|
|
171269
171895
|
function saveTargetStore(channelId) {
|
|
171270
171896
|
if (saveTargetStoreOverride) {
|
|
171271
|
-
saveTargetStoreOverride(channelId,
|
|
171897
|
+
saveTargetStoreOverride(channelId, getStore3(channelId));
|
|
171272
171898
|
return;
|
|
171273
171899
|
}
|
|
171274
171900
|
const dir = getChannelDir(channelId);
|
|
171275
|
-
|
|
171276
|
-
|
|
171901
|
+
mkdirSync7(dir, { recursive: true });
|
|
171902
|
+
writeFileSync5(getChannelTargetsPath(channelId), `${JSON.stringify(getStore3(channelId), null, 2)}
|
|
171277
171903
|
`, "utf-8");
|
|
171278
171904
|
}
|
|
171279
171905
|
function listChannelTargets(channelId, accountId) {
|
|
171280
|
-
const normalizedAccountId = accountId === undefined ? undefined :
|
|
171281
|
-
return
|
|
171906
|
+
const normalizedAccountId = accountId === undefined ? undefined : normalizeAccountId2(accountId);
|
|
171907
|
+
return getStore3(channelId).targets.filter((target2) => normalizedAccountId === undefined || normalizeAccountId2(target2.accountId) === normalizedAccountId);
|
|
171282
171908
|
}
|
|
171283
171909
|
function upsertChannelTarget(channelId, target2) {
|
|
171284
|
-
const store =
|
|
171285
|
-
const normalizedAccountId =
|
|
171286
|
-
const existingIndex = store.targets.findIndex((candidate) => candidate.targetId === target2.targetId &&
|
|
171910
|
+
const store = getStore3(channelId);
|
|
171911
|
+
const normalizedAccountId = normalizeAccountId2(target2.accountId);
|
|
171912
|
+
const existingIndex = store.targets.findIndex((candidate) => candidate.targetId === target2.targetId && normalizeAccountId2(candidate.accountId) === normalizedAccountId);
|
|
171287
171913
|
if (existingIndex >= 0) {
|
|
171288
171914
|
const existing = store.targets[existingIndex];
|
|
171289
171915
|
if (!existing) {
|
|
@@ -171311,9 +171937,9 @@ function upsertChannelTarget(channelId, target2) {
|
|
|
171311
171937
|
};
|
|
171312
171938
|
}
|
|
171313
171939
|
function removeChannelTarget(channelId, targetId, accountId) {
|
|
171314
|
-
const store =
|
|
171315
|
-
const normalizedAccountId =
|
|
171316
|
-
const nextTargets = store.targets.filter((target2) => !(target2.targetId === targetId &&
|
|
171940
|
+
const store = getStore3(channelId);
|
|
171941
|
+
const normalizedAccountId = normalizeAccountId2(accountId);
|
|
171942
|
+
const nextTargets = store.targets.filter((target2) => !(target2.targetId === targetId && normalizeAccountId2(target2.accountId) === normalizedAccountId));
|
|
171317
171943
|
if (nextTargets.length === store.targets.length) {
|
|
171318
171944
|
return false;
|
|
171319
171945
|
}
|
|
@@ -171322,9 +171948,9 @@ function removeChannelTarget(channelId, targetId, accountId) {
|
|
|
171322
171948
|
return true;
|
|
171323
171949
|
}
|
|
171324
171950
|
function removeChannelTargetsForAccount(channelId, accountId) {
|
|
171325
|
-
const store =
|
|
171326
|
-
const normalizedAccountId =
|
|
171327
|
-
const nextTargets = store.targets.filter((target2) =>
|
|
171951
|
+
const store = getStore3(channelId);
|
|
171952
|
+
const normalizedAccountId = normalizeAccountId2(accountId);
|
|
171953
|
+
const nextTargets = store.targets.filter((target2) => normalizeAccountId2(target2.accountId) !== normalizedAccountId);
|
|
171328
171954
|
const removed = store.targets.length - nextTargets.length;
|
|
171329
171955
|
if (removed === 0) {
|
|
171330
171956
|
return 0;
|
|
@@ -171333,14 +171959,14 @@ function removeChannelTargetsForAccount(channelId, accountId) {
|
|
|
171333
171959
|
saveTargetStore(channelId);
|
|
171334
171960
|
return removed;
|
|
171335
171961
|
}
|
|
171336
|
-
function
|
|
171962
|
+
function normalizeAccountId2(accountId) {
|
|
171337
171963
|
return accountId ?? LEGACY_CHANNEL_ACCOUNT_ID;
|
|
171338
171964
|
}
|
|
171339
|
-
var
|
|
171965
|
+
var stores3, loadTargetStoreOverride = null, saveTargetStoreOverride = null;
|
|
171340
171966
|
var init_targets = __esm(() => {
|
|
171341
171967
|
init_accounts();
|
|
171342
171968
|
init_config2();
|
|
171343
|
-
|
|
171969
|
+
stores3 = new Map;
|
|
171344
171970
|
});
|
|
171345
171971
|
|
|
171346
171972
|
// src/channels/slack/target-resolution.ts
|
|
@@ -171933,12 +172559,12 @@ function resolveDiscordChannelMode(channelId, parentChannelId, isThread, allowed
|
|
|
171933
172559
|
|
|
171934
172560
|
// src/channels/discord/media.ts
|
|
171935
172561
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
171936
|
-
import { mkdirSync as
|
|
172562
|
+
import { mkdirSync as mkdirSync8 } from "node:fs";
|
|
171937
172563
|
import { writeFile as writeFile6 } from "node:fs/promises";
|
|
171938
172564
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
171939
172565
|
import { join as join14 } from "node:path";
|
|
171940
172566
|
function ensureAttachmentsDir() {
|
|
171941
|
-
|
|
172567
|
+
mkdirSync8(DISCORD_ATTACHMENTS_DIR, { recursive: true });
|
|
171942
172568
|
return DISCORD_ATTACHMENTS_DIR;
|
|
171943
172569
|
}
|
|
171944
172570
|
function sanitizeDiscordPathSegment(input) {
|
|
@@ -173183,107 +173809,6 @@ var init_plugin4 = __esm(() => {
|
|
|
173183
173809
|
};
|
|
173184
173810
|
});
|
|
173185
173811
|
|
|
173186
|
-
// src/channels/whatsapp/jid.ts
|
|
173187
|
-
function stripDeviceSuffix(jid) {
|
|
173188
|
-
if (!jid)
|
|
173189
|
-
return "";
|
|
173190
|
-
return jid.replace(/:\d+(@|$)/, "$1");
|
|
173191
|
-
}
|
|
173192
|
-
function isLidJid(jid) {
|
|
173193
|
-
return !!jid && stripDeviceSuffix(jid).endsWith(WHATSAPP_LID_SUFFIX);
|
|
173194
|
-
}
|
|
173195
|
-
function isGroupJid(jid) {
|
|
173196
|
-
return !!jid && stripDeviceSuffix(jid).endsWith(WHATSAPP_GROUP_SUFFIX);
|
|
173197
|
-
}
|
|
173198
|
-
function isStatusOrBroadcastJid(jid) {
|
|
173199
|
-
if (!jid)
|
|
173200
|
-
return true;
|
|
173201
|
-
const normalized = stripDeviceSuffix(jid);
|
|
173202
|
-
return normalized === "status@broadcast" || normalized.endsWith("@broadcast") || normalized.endsWith("@newsletter");
|
|
173203
|
-
}
|
|
173204
|
-
function jidToDigits(jid) {
|
|
173205
|
-
if (!jid)
|
|
173206
|
-
return "";
|
|
173207
|
-
const base2 = stripDeviceSuffix(jid).split("@")[0] ?? "";
|
|
173208
|
-
return base2.replace(/\D/g, "");
|
|
173209
|
-
}
|
|
173210
|
-
function normalizePhoneLike(value) {
|
|
173211
|
-
if (!value)
|
|
173212
|
-
return "";
|
|
173213
|
-
return jidToDigits(value.trim());
|
|
173214
|
-
}
|
|
173215
|
-
function phoneDigitsToJid(phoneDigits) {
|
|
173216
|
-
const digits = normalizePhoneLike(phoneDigits);
|
|
173217
|
-
return digits ? `${digits}${WHATSAPP_PHONE_SUFFIX}` : "";
|
|
173218
|
-
}
|
|
173219
|
-
function normalizeMaybePhoneJid(value) {
|
|
173220
|
-
if (!value)
|
|
173221
|
-
return null;
|
|
173222
|
-
const trimmed = value.trim();
|
|
173223
|
-
if (!trimmed)
|
|
173224
|
-
return null;
|
|
173225
|
-
if (isLidJid(trimmed))
|
|
173226
|
-
return null;
|
|
173227
|
-
if (trimmed.includes("@"))
|
|
173228
|
-
return stripDeviceSuffix(trimmed);
|
|
173229
|
-
return phoneDigitsToJid(trimmed) || null;
|
|
173230
|
-
}
|
|
173231
|
-
function isSelfChat(remoteJid, selfPhoneJid, selfLid) {
|
|
173232
|
-
const remote = stripDeviceSuffix(remoteJid);
|
|
173233
|
-
if (!remote)
|
|
173234
|
-
return false;
|
|
173235
|
-
const phone = stripDeviceSuffix(selfPhoneJid);
|
|
173236
|
-
if (phone && remote === phone)
|
|
173237
|
-
return true;
|
|
173238
|
-
const lid = stripDeviceSuffix(selfLid);
|
|
173239
|
-
if (lid && remote === lid)
|
|
173240
|
-
return true;
|
|
173241
|
-
return false;
|
|
173242
|
-
}
|
|
173243
|
-
function senderIdFromJid(jid) {
|
|
173244
|
-
return jidToDigits(jid);
|
|
173245
|
-
}
|
|
173246
|
-
function allowedUsersIncludes(allowedUsers, senderId) {
|
|
173247
|
-
const normalizedSender = normalizePhoneLike(senderId);
|
|
173248
|
-
return allowedUsers.some((entry) => normalizePhoneLike(entry) === normalizedSender);
|
|
173249
|
-
}
|
|
173250
|
-
function resolveLidToPhoneJid(params) {
|
|
173251
|
-
const { lidJid, message, sock } = params;
|
|
173252
|
-
if (!isLidJid(lidJid))
|
|
173253
|
-
return normalizeMaybePhoneJid(lidJid);
|
|
173254
|
-
const msg = message;
|
|
173255
|
-
const senderPn = normalizeMaybePhoneJid(msg?.key?.senderPn ?? undefined);
|
|
173256
|
-
if (senderPn)
|
|
173257
|
-
return senderPn;
|
|
173258
|
-
const repo = sock?.signalRepository;
|
|
173259
|
-
const mapped3 = normalizeMaybePhoneJid(repo?.lidMapping?.get(stripDeviceSuffix(lidJid)));
|
|
173260
|
-
if (mapped3)
|
|
173261
|
-
return mapped3;
|
|
173262
|
-
return null;
|
|
173263
|
-
}
|
|
173264
|
-
function resolveSendJid(params) {
|
|
173265
|
-
const { chatId, selfPhoneJid, selfLid, lidToJid, sock } = params;
|
|
173266
|
-
if (!isLidJid(chatId))
|
|
173267
|
-
return stripDeviceSuffix(chatId);
|
|
173268
|
-
const normalized = stripDeviceSuffix(chatId);
|
|
173269
|
-
if (selfLid && normalized === stripDeviceSuffix(selfLid) && selfPhoneJid) {
|
|
173270
|
-
return stripDeviceSuffix(selfPhoneJid);
|
|
173271
|
-
}
|
|
173272
|
-
const mapped3 = normalizeMaybePhoneJid(lidToJid?.get(normalized));
|
|
173273
|
-
if (mapped3)
|
|
173274
|
-
return mapped3;
|
|
173275
|
-
const repo = sock?.signalRepository;
|
|
173276
|
-
const signalMapped = normalizeMaybePhoneJid(repo?.lidMapping?.get(normalized));
|
|
173277
|
-
if (signalMapped)
|
|
173278
|
-
return signalMapped;
|
|
173279
|
-
throw new Error(`Cannot send to unresolved WhatsApp LID: ${chatId}`);
|
|
173280
|
-
}
|
|
173281
|
-
function sanitizePathSegment(input) {
|
|
173282
|
-
const cleaned = input.replace(/[^A-Za-z0-9._-]/g, "_").replace(/^_+|_+$/g, "");
|
|
173283
|
-
return cleaned || "whatsapp";
|
|
173284
|
-
}
|
|
173285
|
-
var WHATSAPP_PHONE_SUFFIX = "@s.whatsapp.net", WHATSAPP_LID_SUFFIX = "@lid", WHATSAPP_GROUP_SUFFIX = "@g.us";
|
|
173286
|
-
|
|
173287
173812
|
// src/channels/whatsapp/media.ts
|
|
173288
173813
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
173289
173814
|
import { mkdir as mkdir5, writeFile as writeFile7 } from "node:fs/promises";
|
|
@@ -173612,7 +174137,7 @@ var init_state = __esm(() => {
|
|
|
173612
174137
|
});
|
|
173613
174138
|
|
|
173614
174139
|
// src/channels/whatsapp/session.ts
|
|
173615
|
-
import { mkdirSync as
|
|
174140
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
173616
174141
|
import { homedir as homedir6 } from "node:os";
|
|
173617
174142
|
import { join as join16 } from "node:path";
|
|
173618
174143
|
function shouldDropLine(line) {
|
|
@@ -173686,7 +174211,7 @@ function defaultIsProcessAlive(pid) {
|
|
|
173686
174211
|
}
|
|
173687
174212
|
function readLeaseOwner(lockDir) {
|
|
173688
174213
|
try {
|
|
173689
|
-
const owner = JSON.parse(
|
|
174214
|
+
const owner = JSON.parse(readFileSync9(join16(lockDir, "owner.json"), "utf8"));
|
|
173690
174215
|
return {
|
|
173691
174216
|
pid: typeof owner.pid === "number" ? owner.pid : undefined,
|
|
173692
174217
|
command: typeof owner.command === "string" ? owner.command : undefined
|
|
@@ -173705,8 +174230,8 @@ function acquireWhatsAppSessionLease(accountId, options3 = {}) {
|
|
|
173705
174230
|
}
|
|
173706
174231
|
for (let attempt = 0;attempt < 2; attempt += 1) {
|
|
173707
174232
|
try {
|
|
173708
|
-
|
|
173709
|
-
|
|
174233
|
+
mkdirSync9(lockDir);
|
|
174234
|
+
writeFileSync6(join16(lockDir, "owner.json"), `${JSON.stringify({
|
|
173710
174235
|
accountId,
|
|
173711
174236
|
pid,
|
|
173712
174237
|
command: process.argv.join(" "),
|
|
@@ -173784,7 +174309,7 @@ function renderQrTerminal(qrMod, input) {
|
|
|
173784
174309
|
async function createWhatsAppSocket(params) {
|
|
173785
174310
|
installWhatsAppConsoleFilters();
|
|
173786
174311
|
const authDir = getWhatsAppAuthDir(params.accountId);
|
|
173787
|
-
|
|
174312
|
+
mkdirSync9(authDir, { recursive: true });
|
|
173788
174313
|
const sessionLease = acquireWhatsAppSessionLease(params.accountId);
|
|
173789
174314
|
setWhatsAppConnectionState(params.accountId, { status: "connecting" });
|
|
173790
174315
|
try {
|
|
@@ -174584,126 +175109,6 @@ var init_plugin5 = __esm(() => {
|
|
|
174584
175109
|
};
|
|
174585
175110
|
});
|
|
174586
175111
|
|
|
174587
|
-
// src/channels/signal/target.ts
|
|
174588
|
-
function assertSignalHttpProtocol(url2) {
|
|
174589
|
-
if (url2.protocol !== "http:" && url2.protocol !== "https:") {
|
|
174590
|
-
throw new Error(`Signal base URL protocol must be http or https, got ${url2.protocol}`);
|
|
174591
|
-
}
|
|
174592
|
-
}
|
|
174593
|
-
function trimPrefix(value, prefix) {
|
|
174594
|
-
if (!value.toLowerCase().startsWith(prefix)) {
|
|
174595
|
-
return null;
|
|
174596
|
-
}
|
|
174597
|
-
return value.slice(prefix.length).trim();
|
|
174598
|
-
}
|
|
174599
|
-
function normalizeSignalBaseUrl(input) {
|
|
174600
|
-
const trimmed = input.trim();
|
|
174601
|
-
if (!trimmed) {
|
|
174602
|
-
return "";
|
|
174603
|
-
}
|
|
174604
|
-
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed);
|
|
174605
|
-
const withScheme = hasScheme ? trimmed : `http://${trimmed}`;
|
|
174606
|
-
const parsed = new URL(withScheme);
|
|
174607
|
-
assertSignalHttpProtocol(parsed);
|
|
174608
|
-
if (parsed.username || parsed.password) {
|
|
174609
|
-
throw new Error("Signal base URL must not include credentials.");
|
|
174610
|
-
}
|
|
174611
|
-
return withScheme.replace(/\/+$/, "");
|
|
174612
|
-
}
|
|
174613
|
-
function parseSignalTarget(input) {
|
|
174614
|
-
const trimmed = input.trim();
|
|
174615
|
-
if (!trimmed) {
|
|
174616
|
-
throw new Error("Signal target is required.");
|
|
174617
|
-
}
|
|
174618
|
-
const signalTarget = trimPrefix(trimmed, "signal:");
|
|
174619
|
-
const value = signalTarget !== null ? signalTarget : trimmed;
|
|
174620
|
-
if (!value) {
|
|
174621
|
-
throw new Error("Signal target is required.");
|
|
174622
|
-
}
|
|
174623
|
-
const groupId = trimPrefix(value, "group:");
|
|
174624
|
-
if (groupId) {
|
|
174625
|
-
return { kind: "group", groupId };
|
|
174626
|
-
}
|
|
174627
|
-
const username = trimPrefix(value, "username:");
|
|
174628
|
-
if (username) {
|
|
174629
|
-
return { kind: "username", username };
|
|
174630
|
-
}
|
|
174631
|
-
const usernameAlias = trimPrefix(value, "u:");
|
|
174632
|
-
if (usernameAlias) {
|
|
174633
|
-
return { kind: "username", username: `u:${usernameAlias}` };
|
|
174634
|
-
}
|
|
174635
|
-
const recipient = value;
|
|
174636
|
-
if (!recipient) {
|
|
174637
|
-
throw new Error("Signal recipient is required.");
|
|
174638
|
-
}
|
|
174639
|
-
return { kind: "recipient", recipient };
|
|
174640
|
-
}
|
|
174641
|
-
function signalTargetToSendRpcParams(target2) {
|
|
174642
|
-
switch (target2.kind) {
|
|
174643
|
-
case "group":
|
|
174644
|
-
return { groupId: target2.groupId };
|
|
174645
|
-
case "username":
|
|
174646
|
-
return { username: [target2.username] };
|
|
174647
|
-
case "recipient":
|
|
174648
|
-
return { recipient: [target2.recipient] };
|
|
174649
|
-
}
|
|
174650
|
-
}
|
|
174651
|
-
function signalTargetToReactionRpcParams(target2) {
|
|
174652
|
-
switch (target2.kind) {
|
|
174653
|
-
case "group":
|
|
174654
|
-
return { groupIds: [target2.groupId] };
|
|
174655
|
-
case "recipient":
|
|
174656
|
-
return { recipients: [target2.recipient] };
|
|
174657
|
-
case "username":
|
|
174658
|
-
throw new Error("Signal reactions require a recipient or group target.");
|
|
174659
|
-
}
|
|
174660
|
-
}
|
|
174661
|
-
function normalizeSignalSenderId(value) {
|
|
174662
|
-
if (!value) {
|
|
174663
|
-
return "";
|
|
174664
|
-
}
|
|
174665
|
-
const trimmed = value.trim();
|
|
174666
|
-
if (!trimmed) {
|
|
174667
|
-
return "";
|
|
174668
|
-
}
|
|
174669
|
-
return trimmed.toLowerCase();
|
|
174670
|
-
}
|
|
174671
|
-
function normalizeSignalPhone(value) {
|
|
174672
|
-
if (!value) {
|
|
174673
|
-
return "";
|
|
174674
|
-
}
|
|
174675
|
-
const trimmed = value.trim();
|
|
174676
|
-
if (!trimmed) {
|
|
174677
|
-
return "";
|
|
174678
|
-
}
|
|
174679
|
-
const withoutPrefix = trimmed.toLowerCase().startsWith("signal:") ? trimmed.slice("signal:".length) : trimmed;
|
|
174680
|
-
return withoutPrefix.replace(/[^0-9+]/g, "");
|
|
174681
|
-
}
|
|
174682
|
-
function signalAllowedUsersIncludes(allowedUsers, senderId) {
|
|
174683
|
-
const normalizedSender = normalizeSignalSenderId(senderId);
|
|
174684
|
-
const senderPhone = normalizeSignalPhone(senderId);
|
|
174685
|
-
return allowedUsers.some((entry) => {
|
|
174686
|
-
const normalizedEntry = normalizeSignalSenderId(entry);
|
|
174687
|
-
if (normalizedEntry === normalizedSender) {
|
|
174688
|
-
return true;
|
|
174689
|
-
}
|
|
174690
|
-
const entryPhone = normalizeSignalPhone(entry);
|
|
174691
|
-
return !!senderPhone && !!entryPhone && senderPhone === entryPhone;
|
|
174692
|
-
});
|
|
174693
|
-
}
|
|
174694
|
-
function isSignalGroupAllowed(allowedGroups, groupId) {
|
|
174695
|
-
if (!allowedGroups || allowedGroups.length === 0) {
|
|
174696
|
-
return true;
|
|
174697
|
-
}
|
|
174698
|
-
const normalized = groupId.trim();
|
|
174699
|
-
return allowedGroups.some((entry) => entry.trim() === normalized);
|
|
174700
|
-
}
|
|
174701
|
-
function matchesSignalMentionPatterns(text, mentionPatterns) {
|
|
174702
|
-
const normalizedText = text.toLowerCase();
|
|
174703
|
-
return (mentionPatterns ?? []).map((entry) => entry.trim().toLowerCase()).filter(Boolean).some((entry) => normalizedText.includes(entry));
|
|
174704
|
-
}
|
|
174705
|
-
var init_target2 = () => {};
|
|
174706
|
-
|
|
174707
175112
|
// src/channels/signal/client.ts
|
|
174708
175113
|
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
174709
175114
|
import { request as httpRequest } from "node:http";
|
|
@@ -175054,9 +175459,9 @@ var init_client5 = __esm(() => {
|
|
|
175054
175459
|
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
175055
175460
|
import {
|
|
175056
175461
|
copyFileSync,
|
|
175057
|
-
mkdirSync as
|
|
175462
|
+
mkdirSync as mkdirSync10,
|
|
175058
175463
|
readdirSync as readdirSync2,
|
|
175059
|
-
readFileSync as
|
|
175464
|
+
readFileSync as readFileSync10,
|
|
175060
175465
|
realpathSync as realpathSync2,
|
|
175061
175466
|
statSync as statSync3
|
|
175062
175467
|
} from "node:fs";
|
|
@@ -175334,7 +175739,7 @@ function copySignalAttachment(params) {
|
|
|
175334
175739
|
const mimeType = normalizeSignalMimeType(params.attachment.contentType) ?? inferSignalMimeTypeFromName(fileName);
|
|
175335
175740
|
const kind = inferSignalAttachmentKind({ mimeType, fileName });
|
|
175336
175741
|
const inboundDir = join17(getChannelDir("signal"), "inbound", sanitizeSignalPathSegment(params.accountId));
|
|
175337
|
-
|
|
175742
|
+
mkdirSync10(inboundDir, { recursive: true });
|
|
175338
175743
|
const localPath = join17(inboundDir, `${Date.now()}-${randomUUID15()}-${sanitizeSignalPathSegment(fileName)}`);
|
|
175339
175744
|
copyFileSync(params.sourcePath, localPath);
|
|
175340
175745
|
const attachment = {
|
|
@@ -175346,7 +175751,7 @@ function copySignalAttachment(params) {
|
|
|
175346
175751
|
localPath
|
|
175347
175752
|
};
|
|
175348
175753
|
if (kind === "image" && sizeBytes <= MAX_SIGNAL_INLINE_IMAGE_BYTES) {
|
|
175349
|
-
attachment.imageDataBase64 =
|
|
175754
|
+
attachment.imageDataBase64 = readFileSync10(localPath).toString("base64");
|
|
175350
175755
|
}
|
|
175351
175756
|
return attachment;
|
|
175352
175757
|
}
|
|
@@ -176034,7 +176439,7 @@ var init_runtime5 = __esm(() => {
|
|
|
176034
176439
|
|
|
176035
176440
|
// src/channels/signal/setup-runtime.ts
|
|
176036
176441
|
import { execFileSync as execFileSync2, spawn as spawn2 } from "node:child_process";
|
|
176037
|
-
import { existsSync as
|
|
176442
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
176038
176443
|
function getSignalDockerRunCommand() {
|
|
176039
176444
|
return [
|
|
176040
176445
|
"docker run -d",
|
|
@@ -176076,7 +176481,7 @@ function detectNativeSignalCliConfigDir() {
|
|
|
176076
176481
|
}
|
|
176077
176482
|
} catch {}
|
|
176078
176483
|
const defaultDir = getDefaultSignalCliConfigDir();
|
|
176079
|
-
return defaultDir &&
|
|
176484
|
+
return defaultDir && existsSync10(defaultDir) ? defaultDir : null;
|
|
176080
176485
|
}
|
|
176081
176486
|
function runNativeSignalCli(args) {
|
|
176082
176487
|
try {
|
|
@@ -176842,7 +177247,7 @@ __export(exports_plugin_registry, {
|
|
|
176842
177247
|
getChannelDisplayName: () => getChannelDisplayName,
|
|
176843
177248
|
__testClearUserChannelPluginCache: () => __testClearUserChannelPluginCache
|
|
176844
177249
|
});
|
|
176845
|
-
import { existsSync as
|
|
177250
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync11 } from "node:fs";
|
|
176846
177251
|
import { resolve as resolve4, sep } from "node:path";
|
|
176847
177252
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
176848
177253
|
function isValidChannelId(value) {
|
|
@@ -176853,11 +177258,11 @@ function readStringArray(value) {
|
|
|
176853
177258
|
}
|
|
176854
177259
|
function readChannelManifest(channelDir) {
|
|
176855
177260
|
const manifestPath = resolve4(channelDir, "channel.json");
|
|
176856
|
-
if (!
|
|
177261
|
+
if (!existsSync11(manifestPath)) {
|
|
176857
177262
|
return null;
|
|
176858
177263
|
}
|
|
176859
177264
|
try {
|
|
176860
|
-
const parsed = JSON.parse(
|
|
177265
|
+
const parsed = JSON.parse(readFileSync11(manifestPath, "utf-8"));
|
|
176861
177266
|
if (!isRecord(parsed)) {
|
|
176862
177267
|
return null;
|
|
176863
177268
|
}
|
|
@@ -176933,7 +177338,7 @@ function createUserChannelRegistration(manifest) {
|
|
|
176933
177338
|
function discoverUserChannelRegistrations() {
|
|
176934
177339
|
const registrations = new Map;
|
|
176935
177340
|
const channelsRoot = getChannelsRoot();
|
|
176936
|
-
if (!
|
|
177341
|
+
if (!existsSync11(channelsRoot)) {
|
|
176937
177342
|
return registrations;
|
|
176938
177343
|
}
|
|
176939
177344
|
let entries;
|
|
@@ -177096,186 +177501,6 @@ var init_plugin_registry = __esm(() => {
|
|
|
177096
177501
|
loadedUserPlugins = new Map;
|
|
177097
177502
|
});
|
|
177098
177503
|
|
|
177099
|
-
// src/channels/pairing.ts
|
|
177100
|
-
var exports_pairing = {};
|
|
177101
|
-
__export(exports_pairing, {
|
|
177102
|
-
rollbackPairingApproval: () => rollbackPairingApproval,
|
|
177103
|
-
removePairingStateForAccount: () => removePairingStateForAccount,
|
|
177104
|
-
loadPairingStore: () => loadPairingStore,
|
|
177105
|
-
isUserApproved: () => isUserApproved,
|
|
177106
|
-
getPendingPairings: () => getPendingPairings,
|
|
177107
|
-
getApprovedUsers: () => getApprovedUsers,
|
|
177108
|
-
createPairingCode: () => createPairingCode,
|
|
177109
|
-
consumePairingCode: () => consumePairingCode,
|
|
177110
|
-
clearPairingStores: () => clearPairingStores,
|
|
177111
|
-
__testOverrideSavePairingStore: () => __testOverrideSavePairingStore,
|
|
177112
|
-
__testOverrideLoadPairingStore: () => __testOverrideLoadPairingStore
|
|
177113
|
-
});
|
|
177114
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
|
|
177115
|
-
function normalizeAccountId2(accountId) {
|
|
177116
|
-
return accountId ?? LEGACY_CHANNEL_ACCOUNT_ID;
|
|
177117
|
-
}
|
|
177118
|
-
function getStore3(channelId) {
|
|
177119
|
-
let store = stores3.get(channelId);
|
|
177120
|
-
if (!store) {
|
|
177121
|
-
store = { pending: [], approved: [] };
|
|
177122
|
-
stores3.set(channelId, store);
|
|
177123
|
-
}
|
|
177124
|
-
return store;
|
|
177125
|
-
}
|
|
177126
|
-
function loadPairingStore(channelId) {
|
|
177127
|
-
if (loadPairingStoreOverride) {
|
|
177128
|
-
const overridden = loadPairingStoreOverride(channelId);
|
|
177129
|
-
if (overridden === null) {
|
|
177130
|
-
return;
|
|
177131
|
-
}
|
|
177132
|
-
stores3.set(channelId, {
|
|
177133
|
-
pending: [...overridden.pending],
|
|
177134
|
-
approved: [...overridden.approved]
|
|
177135
|
-
});
|
|
177136
|
-
return;
|
|
177137
|
-
}
|
|
177138
|
-
const path5 = getChannelPairingPath(channelId);
|
|
177139
|
-
if (!existsSync11(path5))
|
|
177140
|
-
return;
|
|
177141
|
-
try {
|
|
177142
|
-
const text = readFileSync11(path5, "utf-8");
|
|
177143
|
-
const parsed = JSON.parse(text);
|
|
177144
|
-
stores3.set(channelId, {
|
|
177145
|
-
pending: parsed.pending ?? [],
|
|
177146
|
-
approved: parsed.approved ?? []
|
|
177147
|
-
});
|
|
177148
|
-
} catch {}
|
|
177149
|
-
}
|
|
177150
|
-
function savePairingStore(channelId) {
|
|
177151
|
-
const store = getStore3(channelId);
|
|
177152
|
-
if (savePairingStoreOverride) {
|
|
177153
|
-
savePairingStoreOverride(channelId, {
|
|
177154
|
-
pending: [...store.pending],
|
|
177155
|
-
approved: [...store.approved]
|
|
177156
|
-
});
|
|
177157
|
-
return;
|
|
177158
|
-
}
|
|
177159
|
-
const dir = getChannelDir(channelId);
|
|
177160
|
-
mkdirSync10(dir, { recursive: true });
|
|
177161
|
-
writeFileSync6(getChannelPairingPath(channelId), `${JSON.stringify(store, null, 2)}
|
|
177162
|
-
`, "utf-8");
|
|
177163
|
-
}
|
|
177164
|
-
function generateCode(length = 6) {
|
|
177165
|
-
let code2 = "";
|
|
177166
|
-
for (let i2 = 0;i2 < length; i2++) {
|
|
177167
|
-
code2 += CODE_CHARS[Math.floor(Math.random() * CODE_CHARS.length)];
|
|
177168
|
-
}
|
|
177169
|
-
return code2;
|
|
177170
|
-
}
|
|
177171
|
-
function isUserApproved(channelId, userId, accountId) {
|
|
177172
|
-
const store = getStore3(channelId);
|
|
177173
|
-
const normalizedAccountId = normalizeAccountId2(accountId);
|
|
177174
|
-
return store.approved.some((u) => u.senderId === userId && normalizeAccountId2(u.accountId) === normalizedAccountId);
|
|
177175
|
-
}
|
|
177176
|
-
function createPairingCode(channelId, userId, chatId, username, accountId) {
|
|
177177
|
-
const store = getStore3(channelId);
|
|
177178
|
-
const normalizedAccountId = normalizeAccountId2(accountId);
|
|
177179
|
-
store.pending = store.pending.filter((p) => !(p.senderId === userId && normalizeAccountId2(p.accountId) === normalizedAccountId));
|
|
177180
|
-
const now = Date.now();
|
|
177181
|
-
store.pending = store.pending.filter((p) => new Date(p.expiresAt).getTime() > now);
|
|
177182
|
-
while (store.pending.length >= MAX_PENDING_CODES) {
|
|
177183
|
-
store.pending.shift();
|
|
177184
|
-
}
|
|
177185
|
-
const code2 = generateCode();
|
|
177186
|
-
const pending = {
|
|
177187
|
-
accountId: normalizedAccountId,
|
|
177188
|
-
code: code2,
|
|
177189
|
-
senderId: userId,
|
|
177190
|
-
senderName: username,
|
|
177191
|
-
chatId,
|
|
177192
|
-
createdAt: new Date().toISOString(),
|
|
177193
|
-
expiresAt: new Date(now + PAIRING_CODE_TTL_MS).toISOString()
|
|
177194
|
-
};
|
|
177195
|
-
store.pending.push(pending);
|
|
177196
|
-
savePairingStore(channelId);
|
|
177197
|
-
return code2;
|
|
177198
|
-
}
|
|
177199
|
-
function consumePairingCode(channelId, code2, accountId) {
|
|
177200
|
-
const store = getStore3(channelId);
|
|
177201
|
-
const upperCode = code2.toUpperCase();
|
|
177202
|
-
const normalizedAccountId = accountId === undefined ? undefined : normalizeAccountId2(accountId);
|
|
177203
|
-
const matches = store.pending.map((pending2, index2) => ({ pending: pending2, index: index2 })).filter(({ pending: pending2 }) => pending2.code === upperCode && (normalizedAccountId === undefined || normalizeAccountId2(pending2.accountId) === normalizedAccountId));
|
|
177204
|
-
if (matches.length > 1) {
|
|
177205
|
-
return null;
|
|
177206
|
-
}
|
|
177207
|
-
const index = matches[0]?.index ?? -1;
|
|
177208
|
-
if (index === -1)
|
|
177209
|
-
return null;
|
|
177210
|
-
const pending = store.pending[index];
|
|
177211
|
-
const pendingAccountId = normalizeAccountId2(pending.accountId);
|
|
177212
|
-
if (new Date(pending.expiresAt).getTime() < Date.now()) {
|
|
177213
|
-
store.pending.splice(index, 1);
|
|
177214
|
-
savePairingStore(channelId);
|
|
177215
|
-
return null;
|
|
177216
|
-
}
|
|
177217
|
-
store.pending.splice(index, 1);
|
|
177218
|
-
if (!store.approved.some((u) => u.senderId === pending.senderId && normalizeAccountId2(u.accountId) === pendingAccountId)) {
|
|
177219
|
-
const approved = {
|
|
177220
|
-
accountId: pendingAccountId,
|
|
177221
|
-
senderId: pending.senderId,
|
|
177222
|
-
senderName: pending.senderName,
|
|
177223
|
-
approvedAt: new Date().toISOString()
|
|
177224
|
-
};
|
|
177225
|
-
store.approved.push(approved);
|
|
177226
|
-
}
|
|
177227
|
-
savePairingStore(channelId);
|
|
177228
|
-
return pending;
|
|
177229
|
-
}
|
|
177230
|
-
function getPendingPairings(channelId, accountId) {
|
|
177231
|
-
const store = getStore3(channelId);
|
|
177232
|
-
const now = Date.now();
|
|
177233
|
-
const normalizedAccountId = accountId === undefined ? undefined : normalizeAccountId2(accountId);
|
|
177234
|
-
return store.pending.filter((p) => new Date(p.expiresAt).getTime() > now && (normalizedAccountId === undefined || normalizeAccountId2(p.accountId) === normalizedAccountId));
|
|
177235
|
-
}
|
|
177236
|
-
function getApprovedUsers(channelId, accountId) {
|
|
177237
|
-
const normalizedAccountId = accountId === undefined ? undefined : normalizeAccountId2(accountId);
|
|
177238
|
-
return getStore3(channelId).approved.filter((user) => normalizedAccountId === undefined || normalizeAccountId2(user.accountId) === normalizedAccountId);
|
|
177239
|
-
}
|
|
177240
|
-
function rollbackPairingApproval(channelId, pending) {
|
|
177241
|
-
const store = getStore3(channelId);
|
|
177242
|
-
const normalizedAccountId = normalizeAccountId2(pending.accountId);
|
|
177243
|
-
store.approved = store.approved.filter((u) => !(u.senderId === pending.senderId && normalizeAccountId2(u.accountId) === normalizedAccountId));
|
|
177244
|
-
store.pending.push(pending);
|
|
177245
|
-
savePairingStore(channelId);
|
|
177246
|
-
}
|
|
177247
|
-
function removePairingStateForAccount(channelId, accountId) {
|
|
177248
|
-
const store = getStore3(channelId);
|
|
177249
|
-
const normalizedAccountId = normalizeAccountId2(accountId);
|
|
177250
|
-
const nextPending = store.pending.filter((pending) => normalizeAccountId2(pending.accountId) !== normalizedAccountId);
|
|
177251
|
-
const nextApproved = store.approved.filter((approved) => normalizeAccountId2(approved.accountId) !== normalizedAccountId);
|
|
177252
|
-
const pendingRemoved = store.pending.length - nextPending.length;
|
|
177253
|
-
const approvedRemoved = store.approved.length - nextApproved.length;
|
|
177254
|
-
if (pendingRemoved === 0 && approvedRemoved === 0) {
|
|
177255
|
-
return { pendingRemoved, approvedRemoved };
|
|
177256
|
-
}
|
|
177257
|
-
store.pending = nextPending;
|
|
177258
|
-
store.approved = nextApproved;
|
|
177259
|
-
savePairingStore(channelId);
|
|
177260
|
-
return { pendingRemoved, approvedRemoved };
|
|
177261
|
-
}
|
|
177262
|
-
function clearPairingStores() {
|
|
177263
|
-
stores3.clear();
|
|
177264
|
-
}
|
|
177265
|
-
function __testOverrideLoadPairingStore(fn) {
|
|
177266
|
-
loadPairingStoreOverride = fn;
|
|
177267
|
-
}
|
|
177268
|
-
function __testOverrideSavePairingStore(fn) {
|
|
177269
|
-
savePairingStoreOverride = fn;
|
|
177270
|
-
}
|
|
177271
|
-
var PAIRING_CODE_TTL_MS, MAX_PENDING_CODES = 50, CODE_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789", stores3, loadPairingStoreOverride = null, savePairingStoreOverride = null;
|
|
177272
|
-
var init_pairing = __esm(() => {
|
|
177273
|
-
init_accounts();
|
|
177274
|
-
init_config2();
|
|
177275
|
-
PAIRING_CODE_TTL_MS = 15 * 60 * 1000;
|
|
177276
|
-
stores3 = new Map;
|
|
177277
|
-
});
|
|
177278
|
-
|
|
177279
177504
|
// src/cli/helpers/app-urls.ts
|
|
177280
177505
|
function isLocalAgentId2(agentId) {
|
|
177281
177506
|
return isLocalAgentId(agentId);
|
|
@@ -177320,7 +177545,7 @@ var init_app_urls = __esm(() => {
|
|
|
177320
177545
|
});
|
|
177321
177546
|
|
|
177322
177547
|
// src/channels/registry-presentation.ts
|
|
177323
|
-
function
|
|
177548
|
+
function channelDisplayName4(channelId) {
|
|
177324
177549
|
try {
|
|
177325
177550
|
return getChannelDisplayName(channelId);
|
|
177326
177551
|
} catch {
|
|
@@ -177338,7 +177563,7 @@ function getConfiguredAgentId(config3) {
|
|
|
177338
177563
|
return normalizeAgentId(source2.agentId) ?? normalizeAgentId(source2.binding?.agentId);
|
|
177339
177564
|
}
|
|
177340
177565
|
function buildPairingInstructions(channelId, code2, options3 = {}) {
|
|
177341
|
-
const displayName =
|
|
177566
|
+
const displayName = channelDisplayName4(channelId);
|
|
177342
177567
|
const configuredAgentId = normalizeAgentId(options3.agentId);
|
|
177343
177568
|
const pairingCommand = `letta channels pair --channel ${channelId} --code ${code2} --agent ${configuredAgentId ?? "<agent-id>"}`;
|
|
177344
177569
|
const agentLookupLines = configuredAgentId ? [] : ["Find the target agent with: letta agents list"];
|
|
@@ -177372,7 +177597,7 @@ function buildPairingInstructions(channelId, code2, options3 = {}) {
|
|
|
177372
177597
|
`);
|
|
177373
177598
|
}
|
|
177374
177599
|
function buildUnboundRouteInstructions(channelId, chatId) {
|
|
177375
|
-
const displayName =
|
|
177600
|
+
const displayName = channelDisplayName4(channelId);
|
|
177376
177601
|
if (!isFirstPartyChannelPlugin(channelId)) {
|
|
177377
177602
|
return `This chat isn't connected to a Letta agent yet.
|
|
177378
177603
|
|
|
@@ -178220,6 +178445,9 @@ class ChannelControlRequests {
|
|
|
178220
178445
|
this.requestIdByScope.delete(scopeKey);
|
|
178221
178446
|
return false;
|
|
178222
178447
|
}
|
|
178448
|
+
if (pending.event.source.senderId && pending.event.source.senderId !== msg.senderId) {
|
|
178449
|
+
return false;
|
|
178450
|
+
}
|
|
178223
178451
|
if (msg.channel === "slack" && pending.event.kind === "generic_tool_approval") {
|
|
178224
178452
|
return false;
|
|
178225
178453
|
}
|
|
@@ -178789,10 +179017,27 @@ function createChannelInboundRouter(deps) {
|
|
|
178789
179017
|
const adapter = deps.getAdapter(msg.channel, accountId);
|
|
178790
179018
|
if (!adapter)
|
|
178791
179019
|
return;
|
|
179020
|
+
const config3 = getChannelAccount(msg.channel, accountId);
|
|
179021
|
+
const senderAccess = config3 ? evaluateChannelSenderAccess({
|
|
179022
|
+
account: config3,
|
|
179023
|
+
channelId: msg.channel,
|
|
179024
|
+
senderId: msg.senderId,
|
|
179025
|
+
chatType: msg.chatType
|
|
179026
|
+
}) : null;
|
|
179027
|
+
if (senderAccess === "deny") {
|
|
179028
|
+
if (msg.reaction) {
|
|
179029
|
+
return;
|
|
179030
|
+
}
|
|
179031
|
+
if (resolveChannelAccessScope(msg.chatType) === "dm") {
|
|
179032
|
+
await adapter.sendDirectReply(msg.chatId, buildChannelAccessDeniedMessage(msg.channel));
|
|
179033
|
+
} else {
|
|
179034
|
+
console.log(`[channels] Dropped ${msg.channel} group message from unauthorized sender ${msg.senderId} in chat ${msg.chatId}`);
|
|
179035
|
+
}
|
|
179036
|
+
return;
|
|
179037
|
+
}
|
|
178792
179038
|
if (await deps.controls.tryHandleInbound(adapter, msg)) {
|
|
178793
179039
|
return;
|
|
178794
179040
|
}
|
|
178795
|
-
const config3 = getChannelAccount(msg.channel, accountId);
|
|
178796
179041
|
if (deps.commands.shouldDropUnroutedSlackThreadInput(msg, accountId, config3)) {
|
|
178797
179042
|
return;
|
|
178798
179043
|
}
|
|
@@ -178822,7 +179067,12 @@ function createChannelInboundRouter(deps) {
|
|
|
178822
179067
|
reload: async (_command, commandMsg) => deps.commands.handleReloadSlashCommand(commandMsg),
|
|
178823
179068
|
resume: async () => deps.commands.handlePauseResumeSlashCommand("resume", msg)
|
|
178824
179069
|
},
|
|
178825
|
-
enableBangCommands: msg.channel === "slack" && msg.isMention === true
|
|
179070
|
+
enableBangCommands: msg.channel === "slack" && msg.isMention === true,
|
|
179071
|
+
commandGate: config3 ? senderAccess === "pair" ? floorOnlyChannelCommandGate() : resolveChannelCommandGate({
|
|
179072
|
+
account: config3,
|
|
179073
|
+
channelId: msg.channel,
|
|
179074
|
+
senderId: msg.senderId
|
|
179075
|
+
}) : undefined
|
|
178826
179076
|
})) {
|
|
178827
179077
|
return;
|
|
178828
179078
|
}
|
|
@@ -178937,32 +179187,19 @@ function createChannelInboundRouter(deps) {
|
|
|
178937
179187
|
});
|
|
178938
179188
|
return;
|
|
178939
179189
|
}
|
|
178940
|
-
if (
|
|
178941
|
-
if (
|
|
178942
|
-
if (msg.reaction) {
|
|
178943
|
-
return;
|
|
178944
|
-
}
|
|
178945
|
-
await adapter.sendDirectReply(msg.chatId, "You are not on the allowed users list for this bot.");
|
|
178946
|
-
return;
|
|
178947
|
-
}
|
|
178948
|
-
} else if (config3.dmPolicy === "pairing") {
|
|
178949
|
-
if (!isUserApproved(msg.channel, msg.senderId, accountId)) {
|
|
178950
|
-
loadPairingStore(msg.channel);
|
|
178951
|
-
}
|
|
178952
|
-
if (!isUserApproved(msg.channel, msg.senderId, accountId)) {
|
|
178953
|
-
if (msg.reaction) {
|
|
178954
|
-
return;
|
|
178955
|
-
}
|
|
178956
|
-
const code2 = createPairingCode(msg.channel, msg.senderId, msg.chatId, msg.senderName, accountId);
|
|
178957
|
-
deps.emitEvent({
|
|
178958
|
-
type: "pairings_updated",
|
|
178959
|
-
channelId: msg.channel
|
|
178960
|
-
});
|
|
178961
|
-
await adapter.sendDirectReply(msg.chatId, buildPairingInstructions(msg.channel, code2, {
|
|
178962
|
-
agentId: getConfiguredAgentId(config3)
|
|
178963
|
-
}));
|
|
179190
|
+
if (senderAccess === "pair") {
|
|
179191
|
+
if (msg.reaction) {
|
|
178964
179192
|
return;
|
|
178965
179193
|
}
|
|
179194
|
+
const code2 = createPairingCode(msg.channel, msg.senderId, msg.chatId, msg.senderName, accountId);
|
|
179195
|
+
deps.emitEvent({
|
|
179196
|
+
type: "pairings_updated",
|
|
179197
|
+
channelId: msg.channel
|
|
179198
|
+
});
|
|
179199
|
+
await adapter.sendDirectReply(msg.chatId, buildPairingInstructions(msg.channel, code2, {
|
|
179200
|
+
agentId: getConfiguredAgentId(config3)
|
|
179201
|
+
}));
|
|
179202
|
+
return;
|
|
178966
179203
|
}
|
|
178967
179204
|
let route = getRoute(msg.channel, msg.chatId, accountId, msg.threadId);
|
|
178968
179205
|
if (!route) {
|
|
@@ -178984,6 +179221,7 @@ function createChannelInboundRouter(deps) {
|
|
|
178984
179221
|
return { handleInboundMessage };
|
|
178985
179222
|
}
|
|
178986
179223
|
var init_registry_inbound = __esm(() => {
|
|
179224
|
+
init_access_control();
|
|
178987
179225
|
init_accounts();
|
|
178988
179226
|
init_commands();
|
|
178989
179227
|
init_pairing();
|
|
@@ -179040,12 +179278,6 @@ function createChannelRouteProvisioner(deps) {
|
|
|
179040
179278
|
await adapter.sendDirectReply(msg.chatId, buildSlackAppSetupInstructions(), buildDirectReplyOptions(msg));
|
|
179041
179279
|
return null;
|
|
179042
179280
|
}
|
|
179043
|
-
if (msg.chatType === "direct") {
|
|
179044
|
-
if (config3.dmPolicy === "allowlist" && !config3.allowedUsers.includes(msg.senderId)) {
|
|
179045
|
-
await adapter.sendDirectReply(msg.chatId, "You are not on the allowed users list for this Slack app.", buildDirectReplyOptions(msg));
|
|
179046
|
-
return null;
|
|
179047
|
-
}
|
|
179048
|
-
}
|
|
179049
179281
|
const accountId = msg.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID;
|
|
179050
179282
|
const routeThreadId = msg.channel === "slack" ? msg.threadId ?? null : null;
|
|
179051
179283
|
let route = getRoute(msg.channel, msg.chatId, accountId, routeThreadId);
|
|
@@ -179194,10 +179426,6 @@ function createChannelRouteProvisioner(deps) {
|
|
|
179194
179426
|
}
|
|
179195
179427
|
return null;
|
|
179196
179428
|
}
|
|
179197
|
-
if (msg.chatType === "direct" && config3.dmPolicy === "allowlist" && !config3.allowedUsers.includes(msg.senderId)) {
|
|
179198
|
-
await adapter.sendDirectReply(msg.chatId, "You are not on the allowed users list for this Discord bot.");
|
|
179199
|
-
return null;
|
|
179200
|
-
}
|
|
179201
179429
|
const accountId = msg.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID;
|
|
179202
179430
|
const routeThreadId = msg.threadId ?? null;
|
|
179203
179431
|
let route = getRoute(msg.channel, msg.chatId, accountId, routeThreadId);
|
|
@@ -179245,10 +179473,6 @@ function createChannelRouteProvisioner(deps) {
|
|
|
179245
179473
|
}
|
|
179246
179474
|
return null;
|
|
179247
179475
|
}
|
|
179248
|
-
if (msg.chatType === "direct" && config3.dmPolicy === "allowlist" && !allowedUsersIncludes(config3.allowedUsers, msg.senderId)) {
|
|
179249
|
-
await adapter.sendDirectReply(msg.chatId, "You are not on the allowed users list for this WhatsApp account.");
|
|
179250
|
-
return null;
|
|
179251
|
-
}
|
|
179252
179476
|
const accountId = msg.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID;
|
|
179253
179477
|
let route = getRoute(msg.channel, msg.chatId, accountId, null);
|
|
179254
179478
|
if (!route) {
|
|
@@ -179310,12 +179534,6 @@ function createChannelRouteProvisioner(deps) {
|
|
|
179310
179534
|
}
|
|
179311
179535
|
return null;
|
|
179312
179536
|
}
|
|
179313
|
-
if (msg.chatType === "direct" && config3.dmPolicy === "allowlist" && !signalAllowedUsersIncludes(config3.allowedUsers, msg.senderId)) {
|
|
179314
|
-
if (!msg.reaction) {
|
|
179315
|
-
await adapter.sendDirectReply(msg.chatId, "You are not on the allowed users list for this Signal account.");
|
|
179316
|
-
}
|
|
179317
|
-
return null;
|
|
179318
|
-
}
|
|
179319
179537
|
const accountId = msg.accountId ?? LEGACY_CHANNEL_ACCOUNT_ID;
|
|
179320
179538
|
let route = getRoute(msg.channel, msg.chatId, accountId, null);
|
|
179321
179539
|
if (!route) {
|
|
@@ -179363,7 +179581,6 @@ var init_registry_routes = __esm(() => {
|
|
|
179363
179581
|
init_accounts();
|
|
179364
179582
|
init_registry_presentation();
|
|
179365
179583
|
init_routing();
|
|
179366
|
-
init_target2();
|
|
179367
179584
|
init_targets();
|
|
179368
179585
|
});
|
|
179369
179586
|
|
|
@@ -441753,6 +441970,115 @@ var init_PasteAwareTextInput = __esm(async () => {
|
|
|
441753
441970
|
OPTION_RIGHT_PATTERN = /^\u001b\[(?:1;)?(?:3|4|7|8|9)C$/;
|
|
441754
441971
|
});
|
|
441755
441972
|
|
|
441973
|
+
// src/agent/favorites.ts
|
|
441974
|
+
function generateFavoriteTag(ownerId) {
|
|
441975
|
+
return `${LETTA_CHAT_FAVORITE_TAG_PREFIX}${ownerId}`;
|
|
441976
|
+
}
|
|
441977
|
+
async function fetchCurrentUserMetadata() {
|
|
441978
|
+
if (currentUserMetadataFetcherOverride) {
|
|
441979
|
+
return currentUserMetadataFetcherOverride();
|
|
441980
|
+
}
|
|
441981
|
+
return apiRequest("GET", "/v1/metadata/user");
|
|
441982
|
+
}
|
|
441983
|
+
async function getCurrentCloudFavoriteTag() {
|
|
441984
|
+
try {
|
|
441985
|
+
const user = await fetchCurrentUserMetadata();
|
|
441986
|
+
return typeof user.id === "string" && user.id ? generateFavoriteTag(user.id) : null;
|
|
441987
|
+
} catch {
|
|
441988
|
+
return null;
|
|
441989
|
+
}
|
|
441990
|
+
}
|
|
441991
|
+
async function getFavoriteTagForAgent(agentId) {
|
|
441992
|
+
if (isLocalAgentId(agentId)) {
|
|
441993
|
+
return LOCAL_DESKTOP_FAVORITE_TAG;
|
|
441994
|
+
}
|
|
441995
|
+
return getCurrentCloudFavoriteTag();
|
|
441996
|
+
}
|
|
441997
|
+
function getAgentTags(agent2) {
|
|
441998
|
+
return Array.isArray(agent2.tags) ? agent2.tags : [];
|
|
441999
|
+
}
|
|
442000
|
+
function addFavoriteTag(tags, favoriteTag) {
|
|
442001
|
+
if (tags.includes(favoriteTag))
|
|
442002
|
+
return tags;
|
|
442003
|
+
return [favoriteTag, ...tags];
|
|
442004
|
+
}
|
|
442005
|
+
function removeFavoriteTag(tags, favoriteTag) {
|
|
442006
|
+
return tags.filter((tag) => tag !== favoriteTag && tag !== LETTA_CHAT_FAVORITE_TAG_BASE);
|
|
442007
|
+
}
|
|
442008
|
+
function arraysEqual2(a2, b3) {
|
|
442009
|
+
return a2.length === b3.length && a2.every((value, index) => value === b3[index]);
|
|
442010
|
+
}
|
|
442011
|
+
async function setAgentFavoriteTag(backend4, agentId) {
|
|
442012
|
+
const favoriteTag = await getFavoriteTagForAgent(agentId);
|
|
442013
|
+
if (!favoriteTag)
|
|
442014
|
+
return "unavailable";
|
|
442015
|
+
const agent2 = await backend4.retrieveAgent(agentId, {
|
|
442016
|
+
include: ["agent.tags"]
|
|
442017
|
+
});
|
|
442018
|
+
const tags = getAgentTags(agent2);
|
|
442019
|
+
const nextTags = addFavoriteTag(tags, favoriteTag);
|
|
442020
|
+
if (arraysEqual2(tags, nextTags))
|
|
442021
|
+
return "unchanged";
|
|
442022
|
+
await backend4.updateAgent(agentId, { tags: nextTags });
|
|
442023
|
+
return "changed";
|
|
442024
|
+
}
|
|
442025
|
+
async function unsetAgentFavoriteTag(backend4, agentId) {
|
|
442026
|
+
const favoriteTag = await getFavoriteTagForAgent(agentId);
|
|
442027
|
+
if (!favoriteTag)
|
|
442028
|
+
return "unavailable";
|
|
442029
|
+
const agent2 = await backend4.retrieveAgent(agentId, {
|
|
442030
|
+
include: ["agent.tags"]
|
|
442031
|
+
});
|
|
442032
|
+
const tags = getAgentTags(agent2);
|
|
442033
|
+
const nextTags = removeFavoriteTag(tags, favoriteTag);
|
|
442034
|
+
if (arraysEqual2(tags, nextTags))
|
|
442035
|
+
return "unchanged";
|
|
442036
|
+
await backend4.updateAgent(agentId, { tags: nextTags });
|
|
442037
|
+
return "changed";
|
|
442038
|
+
}
|
|
442039
|
+
async function pinAgentForCurrentUser(agentId, backend4 = getBackend()) {
|
|
442040
|
+
const wasPinnedInSettings = settingsManager.isAgentPinned(agentId);
|
|
442041
|
+
try {
|
|
442042
|
+
const result = await setAgentFavoriteTag(backend4, agentId);
|
|
442043
|
+
if (result !== "unavailable") {
|
|
442044
|
+
if (wasPinnedInSettings) {
|
|
442045
|
+
settingsManager.unpinAgent(agentId);
|
|
442046
|
+
}
|
|
442047
|
+
return result === "unchanged" || wasPinnedInSettings ? "already-pinned" : "pinned";
|
|
442048
|
+
}
|
|
442049
|
+
} catch {}
|
|
442050
|
+
if (wasPinnedInSettings)
|
|
442051
|
+
return "already-pinned";
|
|
442052
|
+
settingsManager.pinAgent(agentId);
|
|
442053
|
+
return "pinned";
|
|
442054
|
+
}
|
|
442055
|
+
async function unpinAgentForCurrentUser(agentId, backend4 = getBackend()) {
|
|
442056
|
+
const wasPinnedInSettings = settingsManager.isAgentPinned(agentId);
|
|
442057
|
+
try {
|
|
442058
|
+
const result = await unsetAgentFavoriteTag(backend4, agentId);
|
|
442059
|
+
if (result !== "unavailable") {
|
|
442060
|
+
if (wasPinnedInSettings) {
|
|
442061
|
+
settingsManager.unpinAgent(agentId);
|
|
442062
|
+
}
|
|
442063
|
+
return result === "changed" || wasPinnedInSettings ? "unpinned" : "not-pinned";
|
|
442064
|
+
}
|
|
442065
|
+
} catch (error54) {
|
|
442066
|
+
if (!wasPinnedInSettings)
|
|
442067
|
+
throw error54;
|
|
442068
|
+
}
|
|
442069
|
+
if (!wasPinnedInSettings)
|
|
442070
|
+
return "not-pinned";
|
|
442071
|
+
settingsManager.unpinAgent(agentId);
|
|
442072
|
+
return "unpinned";
|
|
442073
|
+
}
|
|
442074
|
+
var LETTA_CHAT_FAVORITE_TAG_BASE = "view:letta-chat", LETTA_CHAT_FAVORITE_TAG_PREFIX = "favorite:user:", LOCAL_FAVORITE_OWNER_ID = "local", LOCAL_DESKTOP_FAVORITE_TAG, currentUserMetadataFetcherOverride = null;
|
|
442075
|
+
var init_favorites = __esm(() => {
|
|
442076
|
+
init_backend2();
|
|
442077
|
+
init_request();
|
|
442078
|
+
init_settings_manager();
|
|
442079
|
+
LOCAL_DESKTOP_FAVORITE_TAG = generateFavoriteTag(LOCAL_FAVORITE_OWNER_ID);
|
|
442080
|
+
});
|
|
442081
|
+
|
|
441756
442082
|
// src/backend/local/index.ts
|
|
441757
442083
|
var init_local = __esm(() => {
|
|
441758
442084
|
init_context_window_overflow();
|
|
@@ -441796,6 +442122,78 @@ var init_local_agent_listing = __esm(() => {
|
|
|
441796
442122
|
init_paths();
|
|
441797
442123
|
});
|
|
441798
442124
|
|
|
442125
|
+
// src/cli/helpers/pinned-agent-listing.ts
|
|
442126
|
+
function hasCloudCredentials2() {
|
|
442127
|
+
if (process.env.LETTA_API_KEY)
|
|
442128
|
+
return true;
|
|
442129
|
+
const settings3 = settingsManager.getSettings();
|
|
442130
|
+
const cached3 = settingsManager.getCachedSecureTokens();
|
|
442131
|
+
return Boolean(cached3.apiKey || cached3.refreshToken || settings3.refreshToken || settings3.env?.LETTA_API_KEY);
|
|
442132
|
+
}
|
|
442133
|
+
async function listCloudFavoriteAgents2() {
|
|
442134
|
+
if (!hasCloudCredentials2())
|
|
442135
|
+
return [];
|
|
442136
|
+
try {
|
|
442137
|
+
const favoriteTag = await getCurrentCloudFavoriteTag();
|
|
442138
|
+
if (!favoriteTag)
|
|
442139
|
+
return [];
|
|
442140
|
+
const page = await getBackendForMode("api").listAgents({
|
|
442141
|
+
limit: PINNED_AGENT_LIMIT2,
|
|
442142
|
+
include: ["agent.blocks"],
|
|
442143
|
+
order: "desc",
|
|
442144
|
+
order_by: "last_run_completion",
|
|
442145
|
+
tags: [favoriteTag]
|
|
442146
|
+
});
|
|
442147
|
+
return Array.isArray(page) ? page : page.items ?? [];
|
|
442148
|
+
} catch {
|
|
442149
|
+
return [];
|
|
442150
|
+
}
|
|
442151
|
+
}
|
|
442152
|
+
async function retrieveLegacyPin2(agentId, backendMode) {
|
|
442153
|
+
if (backendMode === "api" && !hasCloudCredentials2()) {
|
|
442154
|
+
return { agentId, agent: null, error: "Not signed in", backendMode };
|
|
442155
|
+
}
|
|
442156
|
+
try {
|
|
442157
|
+
const agent2 = await getBackendForMode(backendMode).retrieveAgent(agentId, {
|
|
442158
|
+
include: ["agent.blocks"]
|
|
442159
|
+
});
|
|
442160
|
+
return { agentId, agent: agent2, error: null, backendMode };
|
|
442161
|
+
} catch {
|
|
442162
|
+
return { agentId, agent: null, error: "Agent not found", backendMode };
|
|
442163
|
+
}
|
|
442164
|
+
}
|
|
442165
|
+
async function listPinnedAgentsForCurrentUser2(backendModes = ["api", "local"]) {
|
|
442166
|
+
const modes = new Set(backendModes);
|
|
442167
|
+
const legacyPins = [...modes].flatMap((backendMode) => settingsManager.getPinnedAgentsForBackendMode(backendMode).map((agentId) => ({ agentId, backendMode })));
|
|
442168
|
+
const seen = new Set(legacyPins.map(({ agentId, backendMode }) => `${backendMode}:${agentId}`));
|
|
442169
|
+
const legacyData = await Promise.all(legacyPins.map(({ agentId, backendMode }) => retrieveLegacyPin2(agentId, backendMode)));
|
|
442170
|
+
const favoriteAgents = [];
|
|
442171
|
+
if (modes.has("local")) {
|
|
442172
|
+
favoriteAgents.push(...listLocalAgentsFromDisk().filter((agent2) => getAgentTags(agent2).includes(LOCAL_DESKTOP_FAVORITE_TAG)).map((agent2) => ({ agent: agent2, backendMode: "local" })));
|
|
442173
|
+
}
|
|
442174
|
+
if (modes.has("api")) {
|
|
442175
|
+
favoriteAgents.push(...(await listCloudFavoriteAgents2()).map((agent2) => ({
|
|
442176
|
+
agent: agent2,
|
|
442177
|
+
backendMode: "api"
|
|
442178
|
+
})));
|
|
442179
|
+
}
|
|
442180
|
+
const favoriteData = favoriteAgents.flatMap(({ agent: agent2, backendMode }) => {
|
|
442181
|
+
const key2 = `${backendMode}:${agent2.id}`;
|
|
442182
|
+
if (seen.has(key2))
|
|
442183
|
+
return [];
|
|
442184
|
+
seen.add(key2);
|
|
442185
|
+
return [{ agentId: agent2.id, agent: agent2, error: null, backendMode }];
|
|
442186
|
+
});
|
|
442187
|
+
return [...legacyData, ...favoriteData];
|
|
442188
|
+
}
|
|
442189
|
+
var PINNED_AGENT_LIMIT2 = 100;
|
|
442190
|
+
var init_pinned_agent_listing = __esm(() => {
|
|
442191
|
+
init_favorites();
|
|
442192
|
+
init_backend();
|
|
442193
|
+
init_settings_manager();
|
|
442194
|
+
init_local_agent_listing();
|
|
442195
|
+
});
|
|
442196
|
+
|
|
441799
442197
|
// src/cli/components/ModelReasoningSelector.tsx
|
|
441800
442198
|
function formatEffortLabel(effort, hasDistinctMaxTier) {
|
|
441801
442199
|
if (effort === "none")
|
|
@@ -448069,6 +448467,100 @@ var init_cron = __esm(async () => {
|
|
|
448069
448467
|
await init_scheduler();
|
|
448070
448468
|
});
|
|
448071
448469
|
|
|
448470
|
+
// src/backend/api/environments.ts
|
|
448471
|
+
var exports_environments2 = {};
|
|
448472
|
+
__export(exports_environments2, {
|
|
448473
|
+
sendEnvironmentMessage: () => sendEnvironmentMessage,
|
|
448474
|
+
resolveEnvironmentConnectionId: () => resolveEnvironmentConnectionId,
|
|
448475
|
+
resolveAgentSandboxConnectionId: () => resolveAgentSandboxConnectionId,
|
|
448476
|
+
listEnvironments: () => listEnvironments,
|
|
448477
|
+
isEnvironmentOnline: () => isEnvironmentOnline,
|
|
448478
|
+
getEnvironmentConnection: () => getEnvironmentConnection,
|
|
448479
|
+
describeEnvironment: () => describeEnvironment,
|
|
448480
|
+
createAgentSandbox: () => createAgentSandbox
|
|
448481
|
+
});
|
|
448482
|
+
async function listEnvironments(options3 = {}) {
|
|
448483
|
+
return apiRequest("GET", "/v1/environments", undefined, {
|
|
448484
|
+
query: {
|
|
448485
|
+
limit: options3.limit,
|
|
448486
|
+
after: options3.after,
|
|
448487
|
+
onlineOnly: options3.onlineOnly
|
|
448488
|
+
}
|
|
448489
|
+
});
|
|
448490
|
+
}
|
|
448491
|
+
async function sendEnvironmentMessage(connectionId, body3) {
|
|
448492
|
+
return apiRequest("POST", `/v1/environments/${encodeURIComponent(connectionId)}/messages`, body3);
|
|
448493
|
+
}
|
|
448494
|
+
async function getEnvironmentConnection(deviceId) {
|
|
448495
|
+
return apiRequest("GET", `/v1/environments/${encodeURIComponent(deviceId)}`);
|
|
448496
|
+
}
|
|
448497
|
+
async function createAgentSandbox(agentId) {
|
|
448498
|
+
return apiRequest("POST", `/v1/agents/${encodeURIComponent(agentId)}/sandboxes`, {});
|
|
448499
|
+
}
|
|
448500
|
+
function isEnvironmentOnline(environment2) {
|
|
448501
|
+
return typeof environment2.connectionId === "string" && environment2.connectionId.length > 0 && typeof environment2.lastHeartbeat === "number" && Date.now() - environment2.lastHeartbeat < 120000;
|
|
448502
|
+
}
|
|
448503
|
+
function describeEnvironment(environment2) {
|
|
448504
|
+
const status = isEnvironmentOnline(environment2) ? "online" : "offline";
|
|
448505
|
+
return `${environment2.connectionName} (${environment2.deviceId}, ${status})`;
|
|
448506
|
+
}
|
|
448507
|
+
async function resolveEnvironmentConnectionId(selector) {
|
|
448508
|
+
const trimmed = selector.trim();
|
|
448509
|
+
if (!trimmed) {
|
|
448510
|
+
throw new Error("Environment selector must not be empty");
|
|
448511
|
+
}
|
|
448512
|
+
const response = await listEnvironments({ limit: 100 });
|
|
448513
|
+
const matches2 = response.connections.filter((environment3) => {
|
|
448514
|
+
return environment3.connectionId === trimmed || environment3.id === trimmed || environment3.deviceId === trimmed || environment3.connectionName === trimmed;
|
|
448515
|
+
});
|
|
448516
|
+
if (matches2.length === 0) {
|
|
448517
|
+
throw new Error(`Environment "${trimmed}" not found. Run \`letta environments list\` to discover available environments.`);
|
|
448518
|
+
}
|
|
448519
|
+
const onlineMatches = matches2.filter(isEnvironmentOnline);
|
|
448520
|
+
if (onlineMatches.length === 0) {
|
|
448521
|
+
throw new Error(`Environment "${trimmed}" is offline. Matched: ${matches2.map(describeEnvironment).join(", ")}`);
|
|
448522
|
+
}
|
|
448523
|
+
if (onlineMatches.length > 1) {
|
|
448524
|
+
throw new Error(`Environment "${trimmed}" is ambiguous. Matched: ${onlineMatches.map(describeEnvironment).join(", ")}`);
|
|
448525
|
+
}
|
|
448526
|
+
const environment2 = onlineMatches[0];
|
|
448527
|
+
if (!environment2) {
|
|
448528
|
+
throw new Error(`Environment "${trimmed}" is offline`);
|
|
448529
|
+
}
|
|
448530
|
+
if (!environment2.connectionId) {
|
|
448531
|
+
throw new Error(`Environment "${trimmed}" has no active connection id`);
|
|
448532
|
+
}
|
|
448533
|
+
return { connectionId: environment2.connectionId, environment: environment2 };
|
|
448534
|
+
}
|
|
448535
|
+
async function resolveAgentSandboxConnectionId(agentId, options3 = {}) {
|
|
448536
|
+
const timeoutMs = options3.timeoutMs ?? 3 * 60000;
|
|
448537
|
+
const pollIntervalMs = options3.pollIntervalMs ?? 2000;
|
|
448538
|
+
const sandbox = await createAgentSandbox(agentId);
|
|
448539
|
+
const deviceId = sandbox.deviceId || `sandbox-${agentId}`;
|
|
448540
|
+
const deadline = Date.now() + timeoutMs;
|
|
448541
|
+
let lastEnvironment = null;
|
|
448542
|
+
let lastError = null;
|
|
448543
|
+
while (Date.now() < deadline) {
|
|
448544
|
+
try {
|
|
448545
|
+
const environment2 = await getEnvironmentConnection(deviceId);
|
|
448546
|
+
lastEnvironment = environment2;
|
|
448547
|
+
if (isEnvironmentOnline(environment2) && environment2.connectionId) {
|
|
448548
|
+
return { connectionId: environment2.connectionId, environment: environment2 };
|
|
448549
|
+
}
|
|
448550
|
+
} catch (error54) {
|
|
448551
|
+
lastError = error54;
|
|
448552
|
+
}
|
|
448553
|
+
await new Promise((resolve30) => setTimeout(resolve30, pollIntervalMs));
|
|
448554
|
+
}
|
|
448555
|
+
if (lastEnvironment) {
|
|
448556
|
+
throw new Error(`Timed out waiting for cloud sandbox ${sandbox.connectionName} to come online. Last status: ${describeEnvironment(lastEnvironment)}`);
|
|
448557
|
+
}
|
|
448558
|
+
throw new Error(`Timed out waiting for cloud sandbox ${sandbox.connectionName} to register${lastError instanceof Error ? `: ${lastError.message}` : ""}`);
|
|
448559
|
+
}
|
|
448560
|
+
var init_environments2 = __esm(() => {
|
|
448561
|
+
init_request();
|
|
448562
|
+
});
|
|
448563
|
+
|
|
448072
448564
|
// src/backend/api/search.ts
|
|
448073
448565
|
async function warmSearchCache(body3) {
|
|
448074
448566
|
return apiRequest("POST", "/v1/_internal_search/cache-warm", body3);
|
|
@@ -450426,89 +450918,6 @@ var init_reflection_launcher = __esm(() => {
|
|
|
450426
450918
|
pendingReflectionLaunches = new Map;
|
|
450427
450919
|
});
|
|
450428
450920
|
|
|
450429
|
-
// src/backend/api/environments.ts
|
|
450430
|
-
async function listEnvironments(options3 = {}) {
|
|
450431
|
-
return apiRequest("GET", "/v1/environments", undefined, {
|
|
450432
|
-
query: {
|
|
450433
|
-
limit: options3.limit,
|
|
450434
|
-
after: options3.after,
|
|
450435
|
-
onlineOnly: options3.onlineOnly
|
|
450436
|
-
}
|
|
450437
|
-
});
|
|
450438
|
-
}
|
|
450439
|
-
async function sendEnvironmentMessage(connectionId, body3) {
|
|
450440
|
-
return apiRequest("POST", `/v1/environments/${encodeURIComponent(connectionId)}/messages`, body3);
|
|
450441
|
-
}
|
|
450442
|
-
async function getEnvironmentConnection(deviceId) {
|
|
450443
|
-
return apiRequest("GET", `/v1/environments/${encodeURIComponent(deviceId)}`);
|
|
450444
|
-
}
|
|
450445
|
-
async function createAgentSandbox(agentId) {
|
|
450446
|
-
return apiRequest("POST", `/v1/agents/${encodeURIComponent(agentId)}/sandboxes`, {});
|
|
450447
|
-
}
|
|
450448
|
-
function isEnvironmentOnline(environment2) {
|
|
450449
|
-
return typeof environment2.connectionId === "string" && environment2.connectionId.length > 0 && typeof environment2.lastHeartbeat === "number" && Date.now() - environment2.lastHeartbeat < 120000;
|
|
450450
|
-
}
|
|
450451
|
-
function describeEnvironment(environment2) {
|
|
450452
|
-
const status = isEnvironmentOnline(environment2) ? "online" : "offline";
|
|
450453
|
-
return `${environment2.connectionName} (${environment2.deviceId}, ${status})`;
|
|
450454
|
-
}
|
|
450455
|
-
async function resolveEnvironmentConnectionId(selector) {
|
|
450456
|
-
const trimmed = selector.trim();
|
|
450457
|
-
if (!trimmed) {
|
|
450458
|
-
throw new Error("Environment selector must not be empty");
|
|
450459
|
-
}
|
|
450460
|
-
const response = await listEnvironments({ limit: 100 });
|
|
450461
|
-
const matches2 = response.connections.filter((environment3) => {
|
|
450462
|
-
return environment3.connectionId === trimmed || environment3.id === trimmed || environment3.deviceId === trimmed || environment3.connectionName === trimmed;
|
|
450463
|
-
});
|
|
450464
|
-
if (matches2.length === 0) {
|
|
450465
|
-
throw new Error(`Environment "${trimmed}" not found. Run \`letta environments list\` to discover available environments.`);
|
|
450466
|
-
}
|
|
450467
|
-
const onlineMatches = matches2.filter(isEnvironmentOnline);
|
|
450468
|
-
if (onlineMatches.length === 0) {
|
|
450469
|
-
throw new Error(`Environment "${trimmed}" is offline. Matched: ${matches2.map(describeEnvironment).join(", ")}`);
|
|
450470
|
-
}
|
|
450471
|
-
if (onlineMatches.length > 1) {
|
|
450472
|
-
throw new Error(`Environment "${trimmed}" is ambiguous. Matched: ${onlineMatches.map(describeEnvironment).join(", ")}`);
|
|
450473
|
-
}
|
|
450474
|
-
const environment2 = onlineMatches[0];
|
|
450475
|
-
if (!environment2) {
|
|
450476
|
-
throw new Error(`Environment "${trimmed}" is offline`);
|
|
450477
|
-
}
|
|
450478
|
-
if (!environment2.connectionId) {
|
|
450479
|
-
throw new Error(`Environment "${trimmed}" has no active connection id`);
|
|
450480
|
-
}
|
|
450481
|
-
return { connectionId: environment2.connectionId, environment: environment2 };
|
|
450482
|
-
}
|
|
450483
|
-
async function resolveAgentSandboxConnectionId(agentId, options3 = {}) {
|
|
450484
|
-
const timeoutMs = options3.timeoutMs ?? 3 * 60000;
|
|
450485
|
-
const pollIntervalMs = options3.pollIntervalMs ?? 2000;
|
|
450486
|
-
const sandbox = await createAgentSandbox(agentId);
|
|
450487
|
-
const deviceId = sandbox.deviceId || `sandbox-${agentId}`;
|
|
450488
|
-
const deadline = Date.now() + timeoutMs;
|
|
450489
|
-
let lastEnvironment = null;
|
|
450490
|
-
let lastError = null;
|
|
450491
|
-
while (Date.now() < deadline) {
|
|
450492
|
-
try {
|
|
450493
|
-
const environment2 = await getEnvironmentConnection(deviceId);
|
|
450494
|
-
lastEnvironment = environment2;
|
|
450495
|
-
if (isEnvironmentOnline(environment2) && environment2.connectionId) {
|
|
450496
|
-
return { connectionId: environment2.connectionId, environment: environment2 };
|
|
450497
|
-
}
|
|
450498
|
-
} catch (error54) {
|
|
450499
|
-
lastError = error54;
|
|
450500
|
-
}
|
|
450501
|
-
await new Promise((resolve31) => setTimeout(resolve31, pollIntervalMs));
|
|
450502
|
-
}
|
|
450503
|
-
if (lastEnvironment) {
|
|
450504
|
-
throw new Error(`Timed out waiting for cloud sandbox ${sandbox.connectionName} to come online. Last status: ${describeEnvironment(lastEnvironment)}`);
|
|
450505
|
-
}
|
|
450506
|
-
throw new Error(`Timed out waiting for cloud sandbox ${sandbox.connectionName} to register${lastError instanceof Error ? `: ${lastError.message}` : ""}`);
|
|
450507
|
-
}
|
|
450508
|
-
var init_environments2 = __esm(() => {
|
|
450509
|
-
init_request();
|
|
450510
|
-
});
|
|
450511
|
-
|
|
450512
450921
|
// node_modules/cli-spinners/spinners.json
|
|
450513
450922
|
var require_spinners = __commonJS((exports, module3) => {
|
|
450514
450923
|
module3.exports = {
|
|
@@ -473059,115 +473468,6 @@ var init_setup_ui = __esm(async () => {
|
|
|
473059
473468
|
jsx_dev_runtime14 = __toESM(require_jsx_dev_runtime(), 1);
|
|
473060
473469
|
});
|
|
473061
473470
|
|
|
473062
|
-
// src/agent/favorites.ts
|
|
473063
|
-
function generateFavoriteTag(ownerId) {
|
|
473064
|
-
return `${LETTA_CHAT_FAVORITE_TAG_PREFIX}${ownerId}`;
|
|
473065
|
-
}
|
|
473066
|
-
async function fetchCurrentUserMetadata() {
|
|
473067
|
-
if (currentUserMetadataFetcherOverride) {
|
|
473068
|
-
return currentUserMetadataFetcherOverride();
|
|
473069
|
-
}
|
|
473070
|
-
return apiRequest("GET", "/v1/metadata/user");
|
|
473071
|
-
}
|
|
473072
|
-
async function getCurrentCloudFavoriteTag() {
|
|
473073
|
-
try {
|
|
473074
|
-
const user = await fetchCurrentUserMetadata();
|
|
473075
|
-
return typeof user.id === "string" && user.id ? generateFavoriteTag(user.id) : null;
|
|
473076
|
-
} catch {
|
|
473077
|
-
return null;
|
|
473078
|
-
}
|
|
473079
|
-
}
|
|
473080
|
-
async function getFavoriteTagForAgent(agentId) {
|
|
473081
|
-
if (isLocalAgentId(agentId)) {
|
|
473082
|
-
return LOCAL_DESKTOP_FAVORITE_TAG;
|
|
473083
|
-
}
|
|
473084
|
-
return getCurrentCloudFavoriteTag();
|
|
473085
|
-
}
|
|
473086
|
-
function getAgentTags(agent2) {
|
|
473087
|
-
return Array.isArray(agent2.tags) ? agent2.tags : [];
|
|
473088
|
-
}
|
|
473089
|
-
function addFavoriteTag(tags, favoriteTag) {
|
|
473090
|
-
if (tags.includes(favoriteTag))
|
|
473091
|
-
return tags;
|
|
473092
|
-
return [favoriteTag, ...tags];
|
|
473093
|
-
}
|
|
473094
|
-
function removeFavoriteTag(tags, favoriteTag) {
|
|
473095
|
-
return tags.filter((tag) => tag !== favoriteTag && tag !== LETTA_CHAT_FAVORITE_TAG_BASE);
|
|
473096
|
-
}
|
|
473097
|
-
function arraysEqual2(a2, b3) {
|
|
473098
|
-
return a2.length === b3.length && a2.every((value, index) => value === b3[index]);
|
|
473099
|
-
}
|
|
473100
|
-
async function setAgentFavoriteTag(backend4, agentId) {
|
|
473101
|
-
const favoriteTag = await getFavoriteTagForAgent(agentId);
|
|
473102
|
-
if (!favoriteTag)
|
|
473103
|
-
return "unavailable";
|
|
473104
|
-
const agent2 = await backend4.retrieveAgent(agentId, {
|
|
473105
|
-
include: ["agent.tags"]
|
|
473106
|
-
});
|
|
473107
|
-
const tags = getAgentTags(agent2);
|
|
473108
|
-
const nextTags = addFavoriteTag(tags, favoriteTag);
|
|
473109
|
-
if (arraysEqual2(tags, nextTags))
|
|
473110
|
-
return "unchanged";
|
|
473111
|
-
await backend4.updateAgent(agentId, { tags: nextTags });
|
|
473112
|
-
return "changed";
|
|
473113
|
-
}
|
|
473114
|
-
async function unsetAgentFavoriteTag(backend4, agentId) {
|
|
473115
|
-
const favoriteTag = await getFavoriteTagForAgent(agentId);
|
|
473116
|
-
if (!favoriteTag)
|
|
473117
|
-
return "unavailable";
|
|
473118
|
-
const agent2 = await backend4.retrieveAgent(agentId, {
|
|
473119
|
-
include: ["agent.tags"]
|
|
473120
|
-
});
|
|
473121
|
-
const tags = getAgentTags(agent2);
|
|
473122
|
-
const nextTags = removeFavoriteTag(tags, favoriteTag);
|
|
473123
|
-
if (arraysEqual2(tags, nextTags))
|
|
473124
|
-
return "unchanged";
|
|
473125
|
-
await backend4.updateAgent(agentId, { tags: nextTags });
|
|
473126
|
-
return "changed";
|
|
473127
|
-
}
|
|
473128
|
-
async function pinAgentForCurrentUser(agentId, backend4 = getBackend()) {
|
|
473129
|
-
const wasPinnedInSettings = settingsManager.isAgentPinned(agentId);
|
|
473130
|
-
try {
|
|
473131
|
-
const result = await setAgentFavoriteTag(backend4, agentId);
|
|
473132
|
-
if (result !== "unavailable") {
|
|
473133
|
-
if (wasPinnedInSettings) {
|
|
473134
|
-
settingsManager.unpinAgent(agentId);
|
|
473135
|
-
}
|
|
473136
|
-
return result === "unchanged" || wasPinnedInSettings ? "already-pinned" : "pinned";
|
|
473137
|
-
}
|
|
473138
|
-
} catch {}
|
|
473139
|
-
if (wasPinnedInSettings)
|
|
473140
|
-
return "already-pinned";
|
|
473141
|
-
settingsManager.pinAgent(agentId);
|
|
473142
|
-
return "pinned";
|
|
473143
|
-
}
|
|
473144
|
-
async function unpinAgentForCurrentUser(agentId, backend4 = getBackend()) {
|
|
473145
|
-
const wasPinnedInSettings = settingsManager.isAgentPinned(agentId);
|
|
473146
|
-
try {
|
|
473147
|
-
const result = await unsetAgentFavoriteTag(backend4, agentId);
|
|
473148
|
-
if (result !== "unavailable") {
|
|
473149
|
-
if (wasPinnedInSettings) {
|
|
473150
|
-
settingsManager.unpinAgent(agentId);
|
|
473151
|
-
}
|
|
473152
|
-
return result === "changed" || wasPinnedInSettings ? "unpinned" : "not-pinned";
|
|
473153
|
-
}
|
|
473154
|
-
} catch (error54) {
|
|
473155
|
-
if (!wasPinnedInSettings)
|
|
473156
|
-
throw error54;
|
|
473157
|
-
}
|
|
473158
|
-
if (!wasPinnedInSettings)
|
|
473159
|
-
return "not-pinned";
|
|
473160
|
-
settingsManager.unpinAgent(agentId);
|
|
473161
|
-
return "unpinned";
|
|
473162
|
-
}
|
|
473163
|
-
var LETTA_CHAT_FAVORITE_TAG_BASE = "view:letta-chat", LETTA_CHAT_FAVORITE_TAG_PREFIX = "favorite:user:", LOCAL_FAVORITE_OWNER_ID = "local", LOCAL_DESKTOP_FAVORITE_TAG, currentUserMetadataFetcherOverride = null;
|
|
473164
|
-
var init_favorites = __esm(() => {
|
|
473165
|
-
init_backend2();
|
|
473166
|
-
init_request();
|
|
473167
|
-
init_settings_manager();
|
|
473168
|
-
LOCAL_DESKTOP_FAVORITE_TAG = generateFavoriteTag(LOCAL_FAVORITE_OWNER_ID);
|
|
473169
|
-
});
|
|
473170
|
-
|
|
473171
473471
|
// src/cli/components/OverlayShell.tsx
|
|
473172
473472
|
function OverlayShell({
|
|
473173
473473
|
command,
|
|
@@ -473457,20 +473757,8 @@ var init_TabBar = __esm(async () => {
|
|
|
473457
473757
|
// src/cli/components/AgentSelector.tsx
|
|
473458
473758
|
var exports_AgentSelector = {};
|
|
473459
473759
|
__export(exports_AgentSelector, {
|
|
473460
|
-
getPinnedAgentBackendMode: () => getPinnedAgentBackendMode,
|
|
473461
473760
|
AgentSelector: () => AgentSelector
|
|
473462
473761
|
});
|
|
473463
|
-
function getPinnedAgentBackendMode(agentId) {
|
|
473464
|
-
return isLocalAgentId(agentId) ? "local" : "api";
|
|
473465
|
-
}
|
|
473466
|
-
function hasCloudCredentials() {
|
|
473467
|
-
const apiKey = process.env.LETTA_API_KEY;
|
|
473468
|
-
if (apiKey)
|
|
473469
|
-
return true;
|
|
473470
|
-
const settings3 = settingsManager.getSettings();
|
|
473471
|
-
const cached3 = settingsManager.getCachedSecureTokens();
|
|
473472
|
-
return Boolean(cached3.apiKey || cached3.refreshToken || settings3.refreshToken || settings3.env?.LETTA_API_KEY);
|
|
473473
|
-
}
|
|
473474
473762
|
function formatRelativeTime3(dateStr) {
|
|
473475
473763
|
if (!dateStr)
|
|
473476
473764
|
return "Never";
|
|
@@ -473576,77 +473864,7 @@ function AgentSelector({
|
|
|
473576
473864
|
const loadPinnedAgents = import_react38.useCallback(async () => {
|
|
473577
473865
|
setPinnedLoading(true);
|
|
473578
473866
|
try {
|
|
473579
|
-
const
|
|
473580
|
-
const pinnedIdSet = new Set(pinnedIds);
|
|
473581
|
-
const localFavoriteAgents = listLocalAgentsFromDisk().filter((agent2) => agent2.tags.includes(LOCAL_DESKTOP_FAVORITE_TAG));
|
|
473582
|
-
let cloudFavoriteAgents = [];
|
|
473583
|
-
if (hasCloudCredentials()) {
|
|
473584
|
-
try {
|
|
473585
|
-
const favoriteTag = await getCurrentCloudFavoriteTag();
|
|
473586
|
-
if (favoriteTag) {
|
|
473587
|
-
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
|
|
473588
|
-
const client = await getClient2();
|
|
473589
|
-
const agentList = await client.agents.list({
|
|
473590
|
-
limit: FETCH_PAGE_SIZE2,
|
|
473591
|
-
include: ["agent.blocks"],
|
|
473592
|
-
order: "desc",
|
|
473593
|
-
order_by: "last_run_completion",
|
|
473594
|
-
tags: [favoriteTag]
|
|
473595
|
-
});
|
|
473596
|
-
cloudFavoriteAgents = agentList.items;
|
|
473597
|
-
}
|
|
473598
|
-
} catch {}
|
|
473599
|
-
}
|
|
473600
|
-
let pinnedData = [];
|
|
473601
|
-
if (pinnedIds.length > 0) {
|
|
473602
|
-
pinnedData = await Promise.all(pinnedIds.map(async (agentId) => {
|
|
473603
|
-
const backendMode = getPinnedAgentBackendMode(agentId);
|
|
473604
|
-
try {
|
|
473605
|
-
if (backendMode === "api" && !hasCloudCredentials()) {
|
|
473606
|
-
return {
|
|
473607
|
-
agentId,
|
|
473608
|
-
agent: null,
|
|
473609
|
-
error: "Not signed in",
|
|
473610
|
-
backendMode
|
|
473611
|
-
};
|
|
473612
|
-
}
|
|
473613
|
-
const agentBackend = getBackendForMode(backendMode);
|
|
473614
|
-
const agent2 = await agentBackend.retrieveAgent(agentId, {
|
|
473615
|
-
include: ["agent.blocks"]
|
|
473616
|
-
});
|
|
473617
|
-
return {
|
|
473618
|
-
agentId,
|
|
473619
|
-
agent: agent2,
|
|
473620
|
-
error: null,
|
|
473621
|
-
backendMode
|
|
473622
|
-
};
|
|
473623
|
-
} catch {
|
|
473624
|
-
return {
|
|
473625
|
-
agentId,
|
|
473626
|
-
agent: null,
|
|
473627
|
-
error: "Agent not found",
|
|
473628
|
-
backendMode
|
|
473629
|
-
};
|
|
473630
|
-
}
|
|
473631
|
-
}));
|
|
473632
|
-
}
|
|
473633
|
-
const favoriteAgents = [
|
|
473634
|
-
...localFavoriteAgents.map((agent2) => ({
|
|
473635
|
-
agent: agent2,
|
|
473636
|
-
backendMode: "local"
|
|
473637
|
-
})),
|
|
473638
|
-
...cloudFavoriteAgents.map((agent2) => ({
|
|
473639
|
-
agent: agent2,
|
|
473640
|
-
backendMode: "api"
|
|
473641
|
-
}))
|
|
473642
|
-
];
|
|
473643
|
-
const favoriteData = favoriteAgents.filter(({ agent: agent2 }) => !pinnedIdSet.has(agent2.id)).map((agent2) => ({
|
|
473644
|
-
agentId: agent2.agent.id,
|
|
473645
|
-
agent: agent2.agent,
|
|
473646
|
-
error: null,
|
|
473647
|
-
backendMode: agent2.backendMode
|
|
473648
|
-
}));
|
|
473649
|
-
pinnedData = [...pinnedData, ...favoriteData];
|
|
473867
|
+
const pinnedData = await listPinnedAgentsForCurrentUser2();
|
|
473650
473868
|
const validPinnedData = pinnedData.filter((p2) => p2.agent !== null);
|
|
473651
473869
|
if (validPinnedData.length === 0) {
|
|
473652
473870
|
setPinnedAgents([]);
|
|
@@ -473728,7 +473946,7 @@ function AgentSelector({
|
|
|
473728
473946
|
activeQuery
|
|
473729
473947
|
]);
|
|
473730
473948
|
import_react38.useEffect(() => {
|
|
473731
|
-
setHasCloudAuth(
|
|
473949
|
+
setHasCloudAuth(hasCloudCredentials2());
|
|
473732
473950
|
}, []);
|
|
473733
473951
|
import_react38.useEffect(() => {
|
|
473734
473952
|
loadPinnedAgents();
|
|
@@ -473985,13 +474203,13 @@ function AgentSelector({
|
|
|
473985
474203
|
});
|
|
473986
474204
|
const renderAgentItem = (agent2, _index, isSelected, extra) => {
|
|
473987
474205
|
const isCurrent = agent2.id === currentAgentId;
|
|
473988
|
-
const
|
|
474206
|
+
const isLocalAgent2 = isLocalAgentId(agent2.id);
|
|
473989
474207
|
const relativeTime = formatRelativeTime3(agent2.last_run_completion);
|
|
473990
474208
|
const blockCount = agent2.blocks?.length ?? 0;
|
|
473991
474209
|
const modelStr = formatModel2(agent2);
|
|
473992
474210
|
const metadataParts = [
|
|
473993
474211
|
relativeTime,
|
|
473994
|
-
...
|
|
474212
|
+
...isLocalAgent2 ? [] : [`${blockCount} memory block${blockCount === 1 ? "" : "s"}`],
|
|
473995
474213
|
modelStr
|
|
473996
474214
|
];
|
|
473997
474215
|
const nameLen = (agent2.name || "Unnamed").length;
|
|
@@ -474458,9 +474676,9 @@ var init_AgentSelector = __esm(async () => {
|
|
|
474458
474676
|
init_model();
|
|
474459
474677
|
init_backend();
|
|
474460
474678
|
init_local_agent_listing();
|
|
474679
|
+
init_pinned_agent_listing();
|
|
474461
474680
|
init_use_terminal_width();
|
|
474462
474681
|
init_constants2();
|
|
474463
|
-
init_settings_manager();
|
|
474464
474682
|
init_colors();
|
|
474465
474683
|
await __promiseAll([
|
|
474466
474684
|
init_build4(),
|
|
@@ -481969,7 +482187,7 @@ var init_reflection_arena_hf_upload = __esm(() => {
|
|
|
481969
482187
|
|
|
481970
482188
|
// src/cli/helpers/reflection-arena.ts
|
|
481971
482189
|
import { execFile as execFileCb6 } from "node:child_process";
|
|
481972
|
-
import { randomInt, randomUUID as randomUUID32 } from "node:crypto";
|
|
482190
|
+
import { randomInt as randomInt2, randomUUID as randomUUID32 } from "node:crypto";
|
|
481973
482191
|
import { appendFile as appendFile3, mkdir as mkdir18, readFile as readFile26, writeFile as writeFile20 } from "node:fs/promises";
|
|
481974
482192
|
import { homedir as homedir39 } from "node:os";
|
|
481975
482193
|
import { join as join71 } from "node:path";
|
|
@@ -481980,7 +482198,7 @@ function sampleReflectionArenaComparisonModel(excludedModels = []) {
|
|
|
481980
482198
|
const totalWeight = candidates2.reduce((sum, entry) => sum + Math.max(0, entry.weight), 0);
|
|
481981
482199
|
if (totalWeight <= 0)
|
|
481982
482200
|
return "letta/auto";
|
|
481983
|
-
let offset =
|
|
482201
|
+
let offset = randomInt2(totalWeight);
|
|
481984
482202
|
for (const candidate of candidates2) {
|
|
481985
482203
|
offset -= Math.max(0, candidate.weight);
|
|
481986
482204
|
if (offset < 0)
|
|
@@ -482045,7 +482263,7 @@ async function updateReflectionArenaRun(runId, update2) {
|
|
|
482045
482263
|
return updated;
|
|
482046
482264
|
}
|
|
482047
482265
|
function shuffledLabels() {
|
|
482048
|
-
return
|
|
482266
|
+
return randomInt2(2) === 0 ? ["1", "2"] : ["2", "1"];
|
|
482049
482267
|
}
|
|
482050
482268
|
function truncateReport(report) {
|
|
482051
482269
|
if (!report?.trim())
|
|
@@ -492784,8 +493002,8 @@ var init_AgentInfoBar = __esm(async () => {
|
|
|
492784
493002
|
return settingsManager.isAgentPinned(agentId);
|
|
492785
493003
|
}, [agentId]);
|
|
492786
493004
|
const isCloudUser = serverUrl?.includes("api.letta.com");
|
|
492787
|
-
const
|
|
492788
|
-
const showCloudLinks = Boolean(isCloudUser && agentId && !
|
|
493005
|
+
const isLocalAgent2 = agentId ? isLocalAgentId2(agentId) : false;
|
|
493006
|
+
const showCloudLinks = Boolean(isCloudUser && agentId && !isLocalAgent2);
|
|
492789
493007
|
const adeConversationUrl = showCloudLinks && agentId && agentId !== "loading" ? buildChatUrl(agentId, { conversationId }) : "";
|
|
492790
493008
|
const usageUrl = buildChatWebUrl("/preferences/usage");
|
|
492791
493009
|
const showBottomBar = agentId && agentId !== "loading";
|
|
@@ -501169,8 +501387,8 @@ function MemfsTreeViewer({
|
|
|
501169
501387
|
const terminalWidth = useTerminalWidth();
|
|
501170
501388
|
const solidLine = SOLID_LINE15.repeat(Math.max(terminalWidth, 10));
|
|
501171
501389
|
const isTmux = Boolean(process.env.TMUX);
|
|
501172
|
-
const
|
|
501173
|
-
const adeUrl =
|
|
501390
|
+
const isLocalAgent2 = isLocalAgentId2(agentId);
|
|
501391
|
+
const adeUrl = isLocalAgent2 ? null : buildChatUrl(agentId, { view: "memory", conversationId });
|
|
501174
501392
|
const [selectedIndex, setSelectedIndex] = import_react90.useState(0);
|
|
501175
501393
|
const [treeScrollOffset, setTreeScrollOffset] = import_react90.useState(0);
|
|
501176
501394
|
const [viewMode, setViewMode] = import_react90.useState("split");
|
|
@@ -501661,8 +501879,8 @@ function MemoryTabViewer({
|
|
|
501661
501879
|
const terminalWidth = useTerminalWidth();
|
|
501662
501880
|
const solidLine = SOLID_LINE16.repeat(Math.max(terminalWidth, 10));
|
|
501663
501881
|
const isTmux = Boolean(process.env.TMUX);
|
|
501664
|
-
const
|
|
501665
|
-
const adeUrl =
|
|
501882
|
+
const isLocalAgent2 = isLocalAgentId2(agentId);
|
|
501883
|
+
const adeUrl = isLocalAgent2 ? null : buildChatUrl(agentId, { view: "memory", conversationId });
|
|
501666
501884
|
const [selectedTabIndex, setSelectedTabIndex] = import_react91.useState(0);
|
|
501667
501885
|
const [scrollOffset, setScrollOffset] = import_react91.useState(0);
|
|
501668
501886
|
const [freshBlocks, setFreshBlocks] = import_react91.useState(null);
|
|
@@ -537640,7 +537858,7 @@ function buildStartupCommandHints(options3) {
|
|
|
537640
537858
|
isPinned,
|
|
537641
537859
|
isLocalBackend,
|
|
537642
537860
|
hasMessages,
|
|
537643
|
-
hasCloudCredentials:
|
|
537861
|
+
hasCloudCredentials: hasCloudCredentials3,
|
|
537644
537862
|
hasAvailableLocalModels
|
|
537645
537863
|
} = options3;
|
|
537646
537864
|
const baseHints = isResumingConversation ? [
|
|
@@ -537669,7 +537887,7 @@ function buildStartupCommandHints(options3) {
|
|
|
537669
537887
|
if (!hasMessages) {
|
|
537670
537888
|
onboardingHints.push("→ **/rename** name your agent", "→ **/init** initialize your agent's memory");
|
|
537671
537889
|
}
|
|
537672
|
-
if (!
|
|
537890
|
+
if (!hasCloudCredentials3) {
|
|
537673
537891
|
onboardingHints.push("→ **/login** sign in to Constellation");
|
|
537674
537892
|
}
|
|
537675
537893
|
const dedupedHints = [];
|
|
@@ -541602,24 +541820,22 @@ function resolveStartupTarget(input) {
|
|
|
541602
541820
|
if (input.forceNew) {
|
|
541603
541821
|
return { action: "create", trigger: "force-new" };
|
|
541604
541822
|
}
|
|
541605
|
-
if (input.
|
|
541606
|
-
const conversationId = input.pinnedAgentId === input.localAgentId ? input.localConversationId ?? undefined : undefined;
|
|
541823
|
+
if (input.localAgentId && input.localAgentExists) {
|
|
541607
541824
|
return {
|
|
541608
541825
|
action: "resume",
|
|
541609
|
-
agentId: input.
|
|
541610
|
-
|
|
541826
|
+
agentId: input.localAgentId,
|
|
541827
|
+
conversationId: input.localConversationId ?? undefined
|
|
541611
541828
|
};
|
|
541612
541829
|
}
|
|
541613
|
-
if (input.
|
|
541614
|
-
return { action: "select" };
|
|
541615
|
-
}
|
|
541616
|
-
if (input.localAgentId && input.localAgentExists) {
|
|
541830
|
+
if (input.pinnedAgentId && input.pinnedAgentExists) {
|
|
541617
541831
|
return {
|
|
541618
541832
|
action: "resume",
|
|
541619
|
-
agentId: input.
|
|
541620
|
-
conversationId: input.localConversationId ?? undefined
|
|
541833
|
+
agentId: input.pinnedAgentId
|
|
541621
541834
|
};
|
|
541622
541835
|
}
|
|
541836
|
+
if (input.existingPinnedCount > 1) {
|
|
541837
|
+
return { action: "select" };
|
|
541838
|
+
}
|
|
541623
541839
|
if (input.globalAgentId && input.globalAgentExists) {
|
|
541624
541840
|
return {
|
|
541625
541841
|
action: "resume",
|
|
@@ -546400,6 +546616,76 @@ async function ensureFdPath() {
|
|
|
546400
546616
|
return pendingFdPath;
|
|
546401
546617
|
}
|
|
546402
546618
|
|
|
546619
|
+
// src/cli/helpers/pinned-agent-listing.ts
|
|
546620
|
+
init_favorites();
|
|
546621
|
+
init_backend();
|
|
546622
|
+
init_settings_manager();
|
|
546623
|
+
init_local_agent_listing();
|
|
546624
|
+
var PINNED_AGENT_LIMIT = 100;
|
|
546625
|
+
function hasCloudCredentials() {
|
|
546626
|
+
if (process.env.LETTA_API_KEY)
|
|
546627
|
+
return true;
|
|
546628
|
+
const settings3 = settingsManager.getSettings();
|
|
546629
|
+
const cached3 = settingsManager.getCachedSecureTokens();
|
|
546630
|
+
return Boolean(cached3.apiKey || cached3.refreshToken || settings3.refreshToken || settings3.env?.LETTA_API_KEY);
|
|
546631
|
+
}
|
|
546632
|
+
async function listCloudFavoriteAgents() {
|
|
546633
|
+
if (!hasCloudCredentials())
|
|
546634
|
+
return [];
|
|
546635
|
+
try {
|
|
546636
|
+
const favoriteTag = await getCurrentCloudFavoriteTag();
|
|
546637
|
+
if (!favoriteTag)
|
|
546638
|
+
return [];
|
|
546639
|
+
const page = await getBackendForMode("api").listAgents({
|
|
546640
|
+
limit: PINNED_AGENT_LIMIT,
|
|
546641
|
+
include: ["agent.blocks"],
|
|
546642
|
+
order: "desc",
|
|
546643
|
+
order_by: "last_run_completion",
|
|
546644
|
+
tags: [favoriteTag]
|
|
546645
|
+
});
|
|
546646
|
+
return Array.isArray(page) ? page : page.items ?? [];
|
|
546647
|
+
} catch {
|
|
546648
|
+
return [];
|
|
546649
|
+
}
|
|
546650
|
+
}
|
|
546651
|
+
async function retrieveLegacyPin(agentId, backendMode) {
|
|
546652
|
+
if (backendMode === "api" && !hasCloudCredentials()) {
|
|
546653
|
+
return { agentId, agent: null, error: "Not signed in", backendMode };
|
|
546654
|
+
}
|
|
546655
|
+
try {
|
|
546656
|
+
const agent2 = await getBackendForMode(backendMode).retrieveAgent(agentId, {
|
|
546657
|
+
include: ["agent.blocks"]
|
|
546658
|
+
});
|
|
546659
|
+
return { agentId, agent: agent2, error: null, backendMode };
|
|
546660
|
+
} catch {
|
|
546661
|
+
return { agentId, agent: null, error: "Agent not found", backendMode };
|
|
546662
|
+
}
|
|
546663
|
+
}
|
|
546664
|
+
async function listPinnedAgentsForCurrentUser(backendModes = ["api", "local"]) {
|
|
546665
|
+
const modes = new Set(backendModes);
|
|
546666
|
+
const legacyPins = [...modes].flatMap((backendMode) => settingsManager.getPinnedAgentsForBackendMode(backendMode).map((agentId) => ({ agentId, backendMode })));
|
|
546667
|
+
const seen = new Set(legacyPins.map(({ agentId, backendMode }) => `${backendMode}:${agentId}`));
|
|
546668
|
+
const legacyData = await Promise.all(legacyPins.map(({ agentId, backendMode }) => retrieveLegacyPin(agentId, backendMode)));
|
|
546669
|
+
const favoriteAgents = [];
|
|
546670
|
+
if (modes.has("local")) {
|
|
546671
|
+
favoriteAgents.push(...listLocalAgentsFromDisk().filter((agent2) => getAgentTags(agent2).includes(LOCAL_DESKTOP_FAVORITE_TAG)).map((agent2) => ({ agent: agent2, backendMode: "local" })));
|
|
546672
|
+
}
|
|
546673
|
+
if (modes.has("api")) {
|
|
546674
|
+
favoriteAgents.push(...(await listCloudFavoriteAgents()).map((agent2) => ({
|
|
546675
|
+
agent: agent2,
|
|
546676
|
+
backendMode: "api"
|
|
546677
|
+
})));
|
|
546678
|
+
}
|
|
546679
|
+
const favoriteData = favoriteAgents.flatMap(({ agent: agent2, backendMode }) => {
|
|
546680
|
+
const key2 = `${backendMode}:${agent2.id}`;
|
|
546681
|
+
if (seen.has(key2))
|
|
546682
|
+
return [];
|
|
546683
|
+
seen.add(key2);
|
|
546684
|
+
return [{ agentId: agent2.id, agent: agent2, error: null, backendMode }];
|
|
546685
|
+
});
|
|
546686
|
+
return [...legacyData, ...favoriteData];
|
|
546687
|
+
}
|
|
546688
|
+
|
|
546403
546689
|
// src/cli/helpers/terminal-theme.ts
|
|
546404
546690
|
var TERMINAL_THEME_STATE_KEY2 = Symbol.for("letta.terminalThemeState");
|
|
546405
546691
|
function getTerminalThemeState2() {
|
|
@@ -546502,6 +546788,7 @@ async function initTerminalTheme() {
|
|
|
546502
546788
|
// src/cli/profile-selection.tsx
|
|
546503
546789
|
init_model();
|
|
546504
546790
|
init_backend2();
|
|
546791
|
+
init_pinned_agent_listing();
|
|
546505
546792
|
await init_build4();
|
|
546506
546793
|
var import_react30 = __toESM(require_react(), 1);
|
|
546507
546794
|
|
|
@@ -546603,32 +546890,6 @@ function getLabel(option2, _freshRepoMode) {
|
|
|
546603
546890
|
parts.push("pinned");
|
|
546604
546891
|
return parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
546605
546892
|
}
|
|
546606
|
-
function buildInitialProfileOptions(lruAgentId) {
|
|
546607
|
-
const pinned = settingsManager.getPinnedAgents();
|
|
546608
|
-
const options3 = [];
|
|
546609
|
-
const seenAgentIds = new Set;
|
|
546610
|
-
if (lruAgentId) {
|
|
546611
|
-
options3.push({
|
|
546612
|
-
name: null,
|
|
546613
|
-
agentId: lruAgentId,
|
|
546614
|
-
isLru: true,
|
|
546615
|
-
agent: null
|
|
546616
|
-
});
|
|
546617
|
-
seenAgentIds.add(lruAgentId);
|
|
546618
|
-
}
|
|
546619
|
-
for (const agentId of pinned) {
|
|
546620
|
-
if (seenAgentIds.has(agentId))
|
|
546621
|
-
continue;
|
|
546622
|
-
options3.push({
|
|
546623
|
-
name: null,
|
|
546624
|
-
agentId,
|
|
546625
|
-
isLru: false,
|
|
546626
|
-
agent: null
|
|
546627
|
-
});
|
|
546628
|
-
seenAgentIds.add(agentId);
|
|
546629
|
-
}
|
|
546630
|
-
return options3;
|
|
546631
|
-
}
|
|
546632
546893
|
function ProfileSelectionUI({
|
|
546633
546894
|
lruAgentId,
|
|
546634
546895
|
externalLoading,
|
|
@@ -546639,8 +546900,9 @@ function ProfileSelectionUI({
|
|
|
546639
546900
|
serverBaseUrl,
|
|
546640
546901
|
onComplete
|
|
546641
546902
|
}) {
|
|
546642
|
-
const [options3, setOptions] = import_react30.useState(
|
|
546643
|
-
const
|
|
546903
|
+
const [options3, setOptions] = import_react30.useState([]);
|
|
546904
|
+
const [optionsLoading, setOptionsLoading] = import_react30.useState(true);
|
|
546905
|
+
const loading = externalLoading || optionsLoading;
|
|
546644
546906
|
const [selectedIndex, setSelectedIndex] = import_react30.useState(0);
|
|
546645
546907
|
const [showAll, setShowAll] = import_react30.useState(false);
|
|
546646
546908
|
const [selectingModel, setSelectingModel] = import_react30.useState(!!(serverModelsForNewAgent && serverModelsForNewAgent.length > 0));
|
|
@@ -546648,45 +546910,45 @@ function ProfileSelectionUI({
|
|
|
546648
546910
|
const [modelSearchQuery, setModelSearchQuery] = import_react30.useState("");
|
|
546649
546911
|
const [modelReasoningPrompt, setModelReasoningPrompt] = import_react30.useState(null);
|
|
546650
546912
|
const loadOptions = import_react30.useCallback(async () => {
|
|
546913
|
+
setOptionsLoading(true);
|
|
546651
546914
|
try {
|
|
546652
|
-
const pinned =
|
|
546653
|
-
|
|
546915
|
+
const pinned = await listPinnedAgentsForCurrentUser2();
|
|
546916
|
+
let fetchedOptions = [];
|
|
546654
546917
|
const seenAgentIds = new Set;
|
|
546655
546918
|
if (lruAgentId) {
|
|
546656
|
-
|
|
546657
|
-
|
|
546658
|
-
|
|
546659
|
-
|
|
546660
|
-
|
|
546661
|
-
|
|
546919
|
+
const pinnedLru = pinned.find(({ agentId, agent: agent3 }) => agentId === lruAgentId && agent3);
|
|
546920
|
+
let agent2 = pinnedLru?.agent ?? null;
|
|
546921
|
+
if (!agent2) {
|
|
546922
|
+
try {
|
|
546923
|
+
const backend4 = getBackendForMode(isLocalAgentId(lruAgentId) ? "local" : "api");
|
|
546924
|
+
agent2 = await backend4.retrieveAgent(lruAgentId, {
|
|
546925
|
+
include: ["agent.blocks"]
|
|
546926
|
+
});
|
|
546927
|
+
} catch {
|
|
546928
|
+
agent2 = null;
|
|
546929
|
+
}
|
|
546930
|
+
}
|
|
546931
|
+
if (agent2) {
|
|
546932
|
+
fetchedOptions.push({
|
|
546933
|
+
name: agent2.name,
|
|
546934
|
+
agentId: lruAgentId,
|
|
546935
|
+
isLru: true,
|
|
546936
|
+
agent: agent2
|
|
546937
|
+
});
|
|
546938
|
+
}
|
|
546662
546939
|
seenAgentIds.add(lruAgentId);
|
|
546663
546940
|
}
|
|
546664
|
-
for (const agentId of pinned) {
|
|
546665
|
-
if (!seenAgentIds.has(agentId)) {
|
|
546666
|
-
|
|
546667
|
-
name:
|
|
546941
|
+
for (const { agentId, agent: agent2 } of pinned) {
|
|
546942
|
+
if (agent2 && !seenAgentIds.has(agentId)) {
|
|
546943
|
+
fetchedOptions.push({
|
|
546944
|
+
name: agent2.name,
|
|
546668
546945
|
agentId,
|
|
546669
546946
|
isLru: false,
|
|
546670
|
-
agent:
|
|
546947
|
+
agent: agent2
|
|
546671
546948
|
});
|
|
546672
546949
|
seenAgentIds.add(agentId);
|
|
546673
546950
|
}
|
|
546674
546951
|
}
|
|
546675
|
-
let fetchedOptions = await Promise.all(optionsToFetch.map(async (opt) => {
|
|
546676
|
-
if (opt.agent) {
|
|
546677
|
-
return opt;
|
|
546678
|
-
}
|
|
546679
|
-
try {
|
|
546680
|
-
const backend4 = getBackendForMode(isLocalAgentId(opt.agentId) ? "local" : "api");
|
|
546681
|
-
const agent2 = await backend4.retrieveAgent(opt.agentId, {
|
|
546682
|
-
include: ["agent.blocks"]
|
|
546683
|
-
});
|
|
546684
|
-
return { ...opt, agent: agent2 };
|
|
546685
|
-
} catch {
|
|
546686
|
-
return { ...opt, agent: null };
|
|
546687
|
-
}
|
|
546688
|
-
}));
|
|
546689
|
-
fetchedOptions = fetchedOptions.filter((opt) => opt.agent !== null);
|
|
546690
546952
|
if (fetchedOptions.length === 0) {
|
|
546691
546953
|
const recentAgents = await getRecentAgentOptions({
|
|
546692
546954
|
includeLocal: false,
|
|
@@ -546703,14 +546965,15 @@ function ProfileSelectionUI({
|
|
|
546703
546965
|
setOptions(fetchedOptions);
|
|
546704
546966
|
} catch {
|
|
546705
546967
|
setOptions([]);
|
|
546968
|
+
} finally {
|
|
546969
|
+
setOptionsLoading(false);
|
|
546706
546970
|
}
|
|
546707
546971
|
}, [lruAgentId]);
|
|
546708
546972
|
import_react30.useEffect(() => {
|
|
546709
546973
|
if (externalLoading)
|
|
546710
546974
|
return;
|
|
546711
|
-
setOptions((current) => current.length > 0 ? current : buildInitialProfileOptions(lruAgentId));
|
|
546712
546975
|
loadOptions();
|
|
546713
|
-
}, [externalLoading, loadOptions
|
|
546976
|
+
}, [externalLoading, loadOptions]);
|
|
546714
546977
|
const displayOptions = showAll ? options3 : options3.slice(0, MAX_DISPLAY);
|
|
546715
546978
|
const hasMore = options3.length > MAX_DISPLAY;
|
|
546716
546979
|
const totalItems = displayOptions.length + 1 + (hasMore && !showAll ? 1 : 0);
|
|
@@ -547589,8 +547852,8 @@ async function resolveStartupBackendDisplay() {
|
|
|
547589
547852
|
}
|
|
547590
547853
|
const apiKey = process.env.LETTA_API_KEY || settings3.env?.LETTA_API_KEY;
|
|
547591
547854
|
const baseURL = process.env.LETTA_BASE_URL || settings3.env?.LETTA_BASE_URL || LETTA_CLOUD_API_URL;
|
|
547592
|
-
const
|
|
547593
|
-
if (baseURL === LETTA_CLOUD_API_URL && !
|
|
547855
|
+
const hasCloudCredentials3 = Boolean(apiKey || settings3.refreshToken);
|
|
547856
|
+
if (baseURL === LETTA_CLOUD_API_URL && !hasCloudCredentials3) {
|
|
547594
547857
|
return "setup";
|
|
547595
547858
|
}
|
|
547596
547859
|
return "api";
|
|
@@ -548398,8 +548661,142 @@ Provider '${providerName}' saved.`);
|
|
|
548398
548661
|
}
|
|
548399
548662
|
|
|
548400
548663
|
// src/cli/subcommands/cron.ts
|
|
548401
|
-
|
|
548664
|
+
init_request();
|
|
548402
548665
|
import { parseArgs as parseArgs5 } from "node:util";
|
|
548666
|
+
|
|
548667
|
+
// src/backend/api/schedules.ts
|
|
548668
|
+
init_request();
|
|
548669
|
+
function schedulePath(agentId) {
|
|
548670
|
+
return `/v1/agents/${encodeURIComponent(agentId)}/schedule`;
|
|
548671
|
+
}
|
|
548672
|
+
async function createCloudSchedule(agentId, input) {
|
|
548673
|
+
return apiRequest("POST", schedulePath(agentId), {
|
|
548674
|
+
...input,
|
|
548675
|
+
use_sandbox: true
|
|
548676
|
+
});
|
|
548677
|
+
}
|
|
548678
|
+
async function listCloudSchedules(agentId, options3 = {}) {
|
|
548679
|
+
return apiRequest("GET", schedulePath(agentId), undefined, {
|
|
548680
|
+
query: {
|
|
548681
|
+
limit: options3.limit ?? 100,
|
|
548682
|
+
after: options3.after
|
|
548683
|
+
}
|
|
548684
|
+
});
|
|
548685
|
+
}
|
|
548686
|
+
async function getCloudSchedule(agentId, scheduleId) {
|
|
548687
|
+
return apiRequest("GET", `${schedulePath(agentId)}/${encodeURIComponent(scheduleId)}`);
|
|
548688
|
+
}
|
|
548689
|
+
async function deleteCloudSchedule(agentId, scheduleId) {
|
|
548690
|
+
await apiRequest("DELETE", `${schedulePath(agentId)}/${encodeURIComponent(scheduleId)}`, {});
|
|
548691
|
+
}
|
|
548692
|
+
async function listCloudScheduleHistory(agentId, scheduleId, options3 = {}) {
|
|
548693
|
+
return apiRequest("GET", `${schedulePath(agentId)}/${encodeURIComponent(scheduleId)}/history`, undefined, {
|
|
548694
|
+
query: {
|
|
548695
|
+
limit: options3.limit,
|
|
548696
|
+
offset: options3.offset
|
|
548697
|
+
}
|
|
548698
|
+
});
|
|
548699
|
+
}
|
|
548700
|
+
|
|
548701
|
+
// src/cli/subcommands/cron.ts
|
|
548702
|
+
init_backend_mode();
|
|
548703
|
+
await init_cron();
|
|
548704
|
+
|
|
548705
|
+
// src/cli/subcommands/cron-runner.ts
|
|
548706
|
+
var CLOUD_EXECUTION_TARGET = "cloud-sandbox";
|
|
548707
|
+
function isLocalAgent(agentId) {
|
|
548708
|
+
return agentId.startsWith("agent-local-");
|
|
548709
|
+
}
|
|
548710
|
+
function resolveCronRunner(params) {
|
|
548711
|
+
const { explicit, agentId, backendMode, cloudSchedulesSupported } = params;
|
|
548712
|
+
if (explicit !== undefined && explicit !== "local" && explicit !== "cloud") {
|
|
548713
|
+
return {
|
|
548714
|
+
error: `invalid --runner "${explicit}". Expected "local" or "cloud".`
|
|
548715
|
+
};
|
|
548716
|
+
}
|
|
548717
|
+
if (explicit === "local") {
|
|
548718
|
+
return { runner: "local", reason: "explicit --runner local" };
|
|
548719
|
+
}
|
|
548720
|
+
if (backendMode === "local" || isLocalAgent(agentId)) {
|
|
548721
|
+
if (explicit === "cloud") {
|
|
548722
|
+
return {
|
|
548723
|
+
error: "Cloud schedules are not available for local-backend agents. Use --runner local."
|
|
548724
|
+
};
|
|
548725
|
+
}
|
|
548726
|
+
return { runner: "local", reason: "local-backend agent" };
|
|
548727
|
+
}
|
|
548728
|
+
if (cloudSchedulesSupported === false) {
|
|
548729
|
+
if (explicit === "cloud") {
|
|
548730
|
+
return {
|
|
548731
|
+
error: "This Letta server does not serve Cloud schedule routes (self-hosted?). Use --runner local."
|
|
548732
|
+
};
|
|
548733
|
+
}
|
|
548734
|
+
return {
|
|
548735
|
+
runner: "local",
|
|
548736
|
+
reason: "server does not support Cloud schedules"
|
|
548737
|
+
};
|
|
548738
|
+
}
|
|
548739
|
+
return {
|
|
548740
|
+
runner: "cloud",
|
|
548741
|
+
reason: explicit === "cloud" ? "explicit --runner cloud" : "cloud agent defaults to durable Cloud schedules"
|
|
548742
|
+
};
|
|
548743
|
+
}
|
|
548744
|
+
var SYNTHETIC_CLOUD_DEVICE_ID = "__letta_cloud__";
|
|
548745
|
+
var SYNTHETIC_LOCAL_PLACEHOLDER_ID = "local";
|
|
548746
|
+
function validateTargetDevice(deviceId, environment2) {
|
|
548747
|
+
if (deviceId === SYNTHETIC_CLOUD_DEVICE_ID) {
|
|
548748
|
+
return {
|
|
548749
|
+
ok: false,
|
|
548750
|
+
error: `"Cloud" is the default execution target, not a computer. Omit --computer to run in the agent's cloud sandbox.`
|
|
548751
|
+
};
|
|
548752
|
+
}
|
|
548753
|
+
if (deviceId === SYNTHETIC_LOCAL_PLACEHOLDER_ID) {
|
|
548754
|
+
return {
|
|
548755
|
+
ok: false,
|
|
548756
|
+
error: '"local" is a placeholder entry, not a connected computer. Run `letta server` on the machine you want to target, then use its deviceId.'
|
|
548757
|
+
};
|
|
548758
|
+
}
|
|
548759
|
+
if (environment2?.organizationId === "local") {
|
|
548760
|
+
return {
|
|
548761
|
+
ok: false,
|
|
548762
|
+
error: `Device ${deviceId} is this computer's local desktop connection, not a computer connected to your Letta account. Cloud schedules can only target connected computers — run \`letta server\` on that machine (or enable remote access in the desktop app) to connect it.`
|
|
548763
|
+
};
|
|
548764
|
+
}
|
|
548765
|
+
return { ok: true };
|
|
548766
|
+
}
|
|
548767
|
+
var CLOUD_CRON_UTC_NOTE = "Recurring Cloud schedules currently interpret cron expressions in UTC (timezone support is tracked in LET-9815).";
|
|
548768
|
+
var CLOUD_DEVICE_FALLBACK_NOTE = "If the target computer is offline when the schedule fires, execution falls back to the agent's cloud sandbox.";
|
|
548769
|
+
function buildCloudScheduleInput(params) {
|
|
548770
|
+
const notes = [];
|
|
548771
|
+
let schedule;
|
|
548772
|
+
if (params.recurring) {
|
|
548773
|
+
schedule = { type: "recurring", cron_expression: params.cron };
|
|
548774
|
+
notes.push(CLOUD_CRON_UTC_NOTE);
|
|
548775
|
+
} else {
|
|
548776
|
+
const scheduledAt = params.scheduledFor?.getTime();
|
|
548777
|
+
if (!scheduledAt || Number.isNaN(scheduledAt)) {
|
|
548778
|
+
throw new Error("One-shot Cloud schedules require a resolved --at time.");
|
|
548779
|
+
}
|
|
548780
|
+
schedule = { type: "one-time", scheduled_at: scheduledAt };
|
|
548781
|
+
}
|
|
548782
|
+
const targetDeviceId = params.targetDeviceId?.trim();
|
|
548783
|
+
if (targetDeviceId) {
|
|
548784
|
+
notes.push(CLOUD_DEVICE_FALLBACK_NOTE);
|
|
548785
|
+
}
|
|
548786
|
+
return {
|
|
548787
|
+
input: {
|
|
548788
|
+
name: params.name,
|
|
548789
|
+
description: params.description,
|
|
548790
|
+
...params.conversationId && { conversation_id: params.conversationId },
|
|
548791
|
+
messages: [{ role: "user", content: params.prompt }],
|
|
548792
|
+
schedule,
|
|
548793
|
+
...targetDeviceId && { target_device_id: targetDeviceId }
|
|
548794
|
+
},
|
|
548795
|
+
notes
|
|
548796
|
+
};
|
|
548797
|
+
}
|
|
548798
|
+
|
|
548799
|
+
// src/cli/subcommands/cron.ts
|
|
548403
548800
|
function printUsage4() {
|
|
548404
548801
|
console.log(`
|
|
548405
548802
|
Usage:
|
|
@@ -548407,10 +548804,10 @@ Usage:
|
|
|
548407
548804
|
letta cron add --prompt <text> --at <time> [--once] [options]
|
|
548408
548805
|
letta cron add --prompt <text> --cron <expr> [options]
|
|
548409
548806
|
letta cron list [options]
|
|
548410
|
-
letta cron get <id>
|
|
548411
|
-
letta cron runs --id <id> [--limit <n>]
|
|
548412
|
-
letta cron delete <id>
|
|
548413
|
-
letta cron delete --all [--agent <id>]
|
|
548807
|
+
letta cron get <id> [--runner local|cloud]
|
|
548808
|
+
letta cron runs --id <id> [--limit <n>] [--runner local|cloud]
|
|
548809
|
+
letta cron delete <id> [--runner local|cloud]
|
|
548810
|
+
letta cron delete --all [--agent <id>] [--runner local|cloud]
|
|
548414
548811
|
|
|
548415
548812
|
Add options:
|
|
548416
548813
|
--prompt <text> Prompt to send to the agent (required)
|
|
@@ -548420,10 +548817,23 @@ Add options:
|
|
|
548420
548817
|
--cron <expr> Raw 5-field cron expression
|
|
548421
548818
|
--agent <id> Agent ID (defaults to LETTA_AGENT_ID)
|
|
548422
548819
|
--conversation <id> Conversation ID (defaults to LETTA_CONVERSATION_ID or "default")
|
|
548820
|
+
--runner <runner> Where the schedule lives and fires:
|
|
548821
|
+
cloud - durable Cloud schedule; executes in the
|
|
548822
|
+
agent's managed cloud sandbox (default for
|
|
548823
|
+
cloud agents)
|
|
548824
|
+
local - this device's scheduler (~/.letta/crons.json);
|
|
548825
|
+
only fires while a session runs here (default
|
|
548826
|
+
for local-backend agents / self-hosted)
|
|
548827
|
+
--computer <id> (cloud runner only) Execute on one of your
|
|
548828
|
+
connected computers (deviceId from
|
|
548829
|
+
\`letta environments list\`) instead of the agent's
|
|
548830
|
+
cloud sandbox. Falls back to the sandbox if the
|
|
548831
|
+
computer is offline at fire time.
|
|
548423
548832
|
|
|
548424
548833
|
List/filter options:
|
|
548425
548834
|
--agent <id> Filter by agent ID
|
|
548426
548835
|
--conversation <id> Filter by conversation ID
|
|
548836
|
+
--runner <runner> Only show tasks owned by this runner
|
|
548427
548837
|
|
|
548428
548838
|
Delete options:
|
|
548429
548839
|
--all Delete all tasks for the given agent
|
|
@@ -548445,7 +548855,9 @@ var CRON_OPTIONS = {
|
|
|
548445
548855
|
all: { type: "boolean" },
|
|
548446
548856
|
id: { type: "string" },
|
|
548447
548857
|
limit: { type: "string" },
|
|
548448
|
-
"run-id": { type: "string" }
|
|
548858
|
+
"run-id": { type: "string" },
|
|
548859
|
+
runner: { type: "string" },
|
|
548860
|
+
computer: { type: "string" }
|
|
548449
548861
|
};
|
|
548450
548862
|
function parseCronArgs(argv) {
|
|
548451
548863
|
return parseArgs5({
|
|
@@ -548461,7 +548873,75 @@ function getAgentId3(fromArgs) {
|
|
|
548461
548873
|
function getConversationId3(fromArgs) {
|
|
548462
548874
|
return fromArgs || process.env.LETTA_CONVERSATION_ID || "default";
|
|
548463
548875
|
}
|
|
548464
|
-
function
|
|
548876
|
+
async function probeCloudScheduleSupport(agentId) {
|
|
548877
|
+
try {
|
|
548878
|
+
await listCloudSchedules(agentId, { limit: 1 });
|
|
548879
|
+
return true;
|
|
548880
|
+
} catch (err) {
|
|
548881
|
+
if (err instanceof ApiRequestError && (err.status === 404 || err.status === 405)) {
|
|
548882
|
+
return false;
|
|
548883
|
+
}
|
|
548884
|
+
return true;
|
|
548885
|
+
}
|
|
548886
|
+
}
|
|
548887
|
+
async function ensureSettingsForCloud() {
|
|
548888
|
+
const { settingsManager: settingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), exports_settings_manager));
|
|
548889
|
+
await settingsManager2.initialize();
|
|
548890
|
+
}
|
|
548891
|
+
async function getRunnerForAgent(explicit, agentId) {
|
|
548892
|
+
const backendMode = resolveBackendMode();
|
|
548893
|
+
const preliminary = resolveCronRunner({ explicit, agentId, backendMode });
|
|
548894
|
+
if ("error" in preliminary || preliminary.runner === "local") {
|
|
548895
|
+
return preliminary;
|
|
548896
|
+
}
|
|
548897
|
+
await ensureSettingsForCloud();
|
|
548898
|
+
const cloudSchedulesSupported = await probeCloudScheduleSupport(agentId);
|
|
548899
|
+
return resolveCronRunner({
|
|
548900
|
+
explicit,
|
|
548901
|
+
agentId,
|
|
548902
|
+
backendMode,
|
|
548903
|
+
cloudSchedulesSupported
|
|
548904
|
+
});
|
|
548905
|
+
}
|
|
548906
|
+
function isRunnerFlagValid(value) {
|
|
548907
|
+
return value === undefined || value === "local" || value === "cloud";
|
|
548908
|
+
}
|
|
548909
|
+
async function lookupEnvironmentForTarget(deviceId) {
|
|
548910
|
+
try {
|
|
548911
|
+
const { getEnvironmentConnection: getEnvironmentConnection2 } = await Promise.resolve().then(() => (init_environments2(), exports_environments2));
|
|
548912
|
+
return await getEnvironmentConnection2(deviceId);
|
|
548913
|
+
} catch {
|
|
548914
|
+
return null;
|
|
548915
|
+
}
|
|
548916
|
+
}
|
|
548917
|
+
function extractPromptFromCloudSchedule(schedule) {
|
|
548918
|
+
const messages = schedule.message?.messages;
|
|
548919
|
+
if (!Array.isArray(messages))
|
|
548920
|
+
return null;
|
|
548921
|
+
const first = messages[0];
|
|
548922
|
+
if (!first || typeof first.content !== "string")
|
|
548923
|
+
return null;
|
|
548924
|
+
return first.content;
|
|
548925
|
+
}
|
|
548926
|
+
function formatCloudScheduleOutput(schedule) {
|
|
548927
|
+
const targetDeviceId = schedule.target_device_id ?? null;
|
|
548928
|
+
return {
|
|
548929
|
+
id: schedule.id,
|
|
548930
|
+
runner: "cloud",
|
|
548931
|
+
execution_target: targetDeviceId ?? CLOUD_EXECUTION_TARGET,
|
|
548932
|
+
...targetDeviceId && { target_device_id: targetDeviceId },
|
|
548933
|
+
agent_id: schedule.agent_id,
|
|
548934
|
+
conversation_id: schedule.conversation_id ?? "default",
|
|
548935
|
+
name: schedule.name ?? null,
|
|
548936
|
+
description: schedule.description ?? null,
|
|
548937
|
+
prompt: extractPromptFromCloudSchedule(schedule),
|
|
548938
|
+
schedule: schedule.schedule,
|
|
548939
|
+
recurring: schedule.schedule.type === "recurring",
|
|
548940
|
+
next_scheduled_time: schedule.next_scheduled_time,
|
|
548941
|
+
created_at: schedule.created_at ?? null
|
|
548942
|
+
};
|
|
548943
|
+
}
|
|
548944
|
+
async function handleAdd(values2) {
|
|
548465
548945
|
const name = values2.name;
|
|
548466
548946
|
if (!name || typeof name !== "string") {
|
|
548467
548947
|
console.error("Error: --name is required.");
|
|
@@ -548533,6 +549013,37 @@ function handleAdd(values2) {
|
|
|
548533
549013
|
console.error("Error: no schedule specified.");
|
|
548534
549014
|
return 1;
|
|
548535
549015
|
}
|
|
549016
|
+
const targetDeviceId = values2.computer?.trim() || undefined;
|
|
549017
|
+
const resolved = await getRunnerForAgent(values2.runner, agentId);
|
|
549018
|
+
if ("error" in resolved) {
|
|
549019
|
+
console.error(`Error: ${resolved.error}`);
|
|
549020
|
+
return 1;
|
|
549021
|
+
}
|
|
549022
|
+
if (targetDeviceId && resolved.runner !== "cloud") {
|
|
549023
|
+
console.error("Error: --computer requires the cloud runner. Run `letta cron add` on the target computer itself (with --runner local) to schedule there locally.");
|
|
549024
|
+
return 1;
|
|
549025
|
+
}
|
|
549026
|
+
if (targetDeviceId) {
|
|
549027
|
+
const validity = validateTargetDevice(targetDeviceId, await lookupEnvironmentForTarget(targetDeviceId));
|
|
549028
|
+
if (!validity.ok) {
|
|
549029
|
+
console.error(`Error: ${validity.error}`);
|
|
549030
|
+
return 1;
|
|
549031
|
+
}
|
|
549032
|
+
}
|
|
549033
|
+
if (resolved.runner === "cloud") {
|
|
549034
|
+
return handleCloudAdd({
|
|
549035
|
+
agentId,
|
|
549036
|
+
conversationId,
|
|
549037
|
+
name,
|
|
549038
|
+
description,
|
|
549039
|
+
prompt,
|
|
549040
|
+
cron,
|
|
549041
|
+
recurring,
|
|
549042
|
+
scheduledFor,
|
|
549043
|
+
note,
|
|
549044
|
+
targetDeviceId
|
|
549045
|
+
});
|
|
549046
|
+
}
|
|
548536
549047
|
try {
|
|
548537
549048
|
const result = addTask({
|
|
548538
549049
|
agent_id: agentId,
|
|
@@ -548546,6 +549057,7 @@ function handleAdd(values2) {
|
|
|
548546
549057
|
});
|
|
548547
549058
|
const output = {
|
|
548548
549059
|
id: result.task.id,
|
|
549060
|
+
runner: "local",
|
|
548549
549061
|
status: result.task.status,
|
|
548550
549062
|
cron: result.task.cron,
|
|
548551
549063
|
recurring: result.task.recurring,
|
|
@@ -548566,37 +549078,152 @@ function handleAdd(values2) {
|
|
|
548566
549078
|
output.warning = result.warning;
|
|
548567
549079
|
}
|
|
548568
549080
|
console.log(JSON.stringify(output, null, 2));
|
|
549081
|
+
console.error("Created local schedule: it only fires while a Letta session is running on this device.");
|
|
548569
549082
|
return 0;
|
|
548570
549083
|
} catch (err) {
|
|
548571
549084
|
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
548572
549085
|
return 1;
|
|
548573
549086
|
}
|
|
548574
549087
|
}
|
|
548575
|
-
function
|
|
549088
|
+
async function handleCloudAdd(params) {
|
|
549089
|
+
let built;
|
|
549090
|
+
try {
|
|
549091
|
+
built = buildCloudScheduleInput({
|
|
549092
|
+
name: params.name,
|
|
549093
|
+
description: params.description,
|
|
549094
|
+
prompt: params.prompt,
|
|
549095
|
+
conversationId: params.conversationId,
|
|
549096
|
+
cron: params.cron,
|
|
549097
|
+
recurring: params.recurring,
|
|
549098
|
+
scheduledFor: params.scheduledFor,
|
|
549099
|
+
targetDeviceId: params.targetDeviceId
|
|
549100
|
+
});
|
|
549101
|
+
} catch (err) {
|
|
549102
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
549103
|
+
return 1;
|
|
549104
|
+
}
|
|
549105
|
+
try {
|
|
549106
|
+
const result = await createCloudSchedule(params.agentId, built.input);
|
|
549107
|
+
const targetDeviceId = result.target_device_id ?? built.input.target_device_id ?? null;
|
|
549108
|
+
const output = {
|
|
549109
|
+
id: result.id,
|
|
549110
|
+
runner: "cloud",
|
|
549111
|
+
execution_target: targetDeviceId ?? CLOUD_EXECUTION_TARGET,
|
|
549112
|
+
...targetDeviceId && { target_device_id: targetDeviceId },
|
|
549113
|
+
agent_id: params.agentId,
|
|
549114
|
+
conversation_id: params.conversationId,
|
|
549115
|
+
recurring: params.recurring,
|
|
549116
|
+
schedule: built.input.schedule,
|
|
549117
|
+
...result.next_scheduled_at && {
|
|
549118
|
+
next_scheduled_at: result.next_scheduled_at
|
|
549119
|
+
}
|
|
549120
|
+
};
|
|
549121
|
+
const notes = [...built.notes];
|
|
549122
|
+
if (params.note)
|
|
549123
|
+
notes.unshift(params.note);
|
|
549124
|
+
if (notes.length > 0) {
|
|
549125
|
+
output.notes = notes;
|
|
549126
|
+
}
|
|
549127
|
+
console.log(JSON.stringify(output, null, 2));
|
|
549128
|
+
console.error(targetDeviceId ? `Created Cloud schedule: it fires from the cloud and runs on computer "${targetDeviceId}" (sandbox fallback if offline).` : "Created Cloud schedule: it fires from the cloud and runs in this agent's managed cloud sandbox (survives local shutdown).");
|
|
549129
|
+
return 0;
|
|
549130
|
+
} catch (err) {
|
|
549131
|
+
console.error(`Error: failed to create Cloud schedule: ${err instanceof Error ? err.message : String(err)}`);
|
|
549132
|
+
console.error("No schedule was created. Retry, or pass --runner local to schedule on this device instead.");
|
|
549133
|
+
return 1;
|
|
549134
|
+
}
|
|
549135
|
+
}
|
|
549136
|
+
async function handleList(values2) {
|
|
549137
|
+
if (!isRunnerFlagValid(values2.runner)) {
|
|
549138
|
+
console.error(`Error: invalid --runner "${values2.runner}". Expected "local" or "cloud".`);
|
|
549139
|
+
return 1;
|
|
549140
|
+
}
|
|
548576
549141
|
const agentId = values2.agent || process.env.LETTA_AGENT_ID || undefined;
|
|
548577
549142
|
const conversationId = values2.conversation || undefined;
|
|
548578
|
-
const
|
|
548579
|
-
|
|
548580
|
-
|
|
548581
|
-
|
|
548582
|
-
|
|
549143
|
+
const includeLocal = values2.runner !== "cloud";
|
|
549144
|
+
const includeCloud = values2.runner !== "local";
|
|
549145
|
+
const output = [];
|
|
549146
|
+
if (includeLocal) {
|
|
549147
|
+
const tasks2 = listTasks2({
|
|
549148
|
+
agent_id: agentId,
|
|
549149
|
+
conversation_id: conversationId
|
|
549150
|
+
});
|
|
549151
|
+
for (const task2 of tasks2) {
|
|
549152
|
+
output.push({ ...task2, runner: "local" });
|
|
549153
|
+
}
|
|
549154
|
+
}
|
|
549155
|
+
if (includeCloud && agentId) {
|
|
549156
|
+
const resolved = await getRunnerForAgent(undefined, agentId);
|
|
549157
|
+
const cloudCapable = !("error" in resolved) && resolved.runner === "cloud";
|
|
549158
|
+
const cloudExplicit = values2.runner === "cloud";
|
|
549159
|
+
if (cloudCapable || cloudExplicit) {
|
|
549160
|
+
try {
|
|
549161
|
+
const response = await listCloudSchedules(agentId);
|
|
549162
|
+
for (const schedule of response.scheduled_messages) {
|
|
549163
|
+
if (conversationId && (schedule.conversation_id ?? "default") !== conversationId) {
|
|
549164
|
+
continue;
|
|
549165
|
+
}
|
|
549166
|
+
output.push(formatCloudScheduleOutput(schedule));
|
|
549167
|
+
}
|
|
549168
|
+
} catch (err) {
|
|
549169
|
+
console.error(`Warning: failed to list Cloud schedules: ${err instanceof Error ? err.message : String(err)}`);
|
|
549170
|
+
if (cloudExplicit) {
|
|
549171
|
+
return 1;
|
|
549172
|
+
}
|
|
549173
|
+
}
|
|
549174
|
+
}
|
|
549175
|
+
} else if (includeCloud && values2.runner === "cloud" && !agentId) {
|
|
549176
|
+
console.error("Error: --agent or LETTA_AGENT_ID required to list Cloud schedules.");
|
|
549177
|
+
return 1;
|
|
549178
|
+
}
|
|
549179
|
+
console.log(JSON.stringify(output, null, 2));
|
|
548583
549180
|
return 0;
|
|
548584
549181
|
}
|
|
548585
|
-
function handleGet(positionals) {
|
|
549182
|
+
async function handleGet(values2, positionals) {
|
|
549183
|
+
if (!isRunnerFlagValid(values2.runner)) {
|
|
549184
|
+
console.error(`Error: invalid --runner "${values2.runner}". Expected "local" or "cloud".`);
|
|
549185
|
+
return 1;
|
|
549186
|
+
}
|
|
548586
549187
|
const taskId = positionals[1];
|
|
548587
549188
|
if (!taskId) {
|
|
548588
549189
|
console.error("Error: task ID required. Usage: letta cron get <id>");
|
|
548589
549190
|
return 1;
|
|
548590
549191
|
}
|
|
548591
|
-
|
|
548592
|
-
|
|
548593
|
-
|
|
549192
|
+
if (values2.runner !== "cloud") {
|
|
549193
|
+
const task2 = getTask2(taskId);
|
|
549194
|
+
if (task2) {
|
|
549195
|
+
console.log(JSON.stringify({ ...task2, runner: "local" }, null, 2));
|
|
549196
|
+
return 0;
|
|
549197
|
+
}
|
|
549198
|
+
if (values2.runner === "local") {
|
|
549199
|
+
console.error(`Error: task ${taskId} not found.`);
|
|
549200
|
+
return 1;
|
|
549201
|
+
}
|
|
549202
|
+
}
|
|
549203
|
+
const agentId = getAgentId3(values2.agent);
|
|
549204
|
+
if (!agentId) {
|
|
549205
|
+
console.error(`Error: task ${taskId} not found locally, and --agent or LETTA_AGENT_ID is required to look up Cloud schedules.`);
|
|
549206
|
+
return 1;
|
|
549207
|
+
}
|
|
549208
|
+
try {
|
|
549209
|
+
await ensureSettingsForCloud();
|
|
549210
|
+
const schedule = await getCloudSchedule(agentId, taskId);
|
|
549211
|
+
console.log(JSON.stringify(formatCloudScheduleOutput(schedule), null, 2));
|
|
549212
|
+
return 0;
|
|
549213
|
+
} catch (err) {
|
|
549214
|
+
if (err instanceof ApiRequestError && err.status === 404) {
|
|
549215
|
+
console.error(`Error: task ${taskId} not found.`);
|
|
549216
|
+
} else {
|
|
549217
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
549218
|
+
}
|
|
548594
549219
|
return 1;
|
|
548595
549220
|
}
|
|
548596
|
-
console.log(JSON.stringify(task2, null, 2));
|
|
548597
|
-
return 0;
|
|
548598
549221
|
}
|
|
548599
|
-
function handleRuns(values2) {
|
|
549222
|
+
async function handleRuns(values2) {
|
|
549223
|
+
if (!isRunnerFlagValid(values2.runner)) {
|
|
549224
|
+
console.error(`Error: invalid --runner "${values2.runner}". Expected "local" or "cloud".`);
|
|
549225
|
+
return 1;
|
|
549226
|
+
}
|
|
548600
549227
|
const id2 = values2.id;
|
|
548601
549228
|
if (!id2 || typeof id2 !== "string") {
|
|
548602
549229
|
console.error("Error: --id is required. Usage: letta cron runs --id <id>");
|
|
@@ -548605,42 +549232,125 @@ function handleRuns(values2) {
|
|
|
548605
549232
|
const limitRaw = Number.parseInt(String(values2.limit ?? "50"), 10);
|
|
548606
549233
|
const limit3 = Number.isFinite(limitRaw) && limitRaw > 0 ? limitRaw : 50;
|
|
548607
549234
|
const runId = values2["run-id"];
|
|
549235
|
+
if (values2.runner !== "cloud" && getTask2(id2)) {
|
|
549236
|
+
try {
|
|
549237
|
+
const logPath = getCronRunLogPath(id2);
|
|
549238
|
+
const page = readCronRunLogEntriesPage(logPath, {
|
|
549239
|
+
jobId: id2,
|
|
549240
|
+
limit: limit3,
|
|
549241
|
+
...typeof runId === "string" && runId.trim() ? { runId } : {}
|
|
549242
|
+
});
|
|
549243
|
+
console.log(JSON.stringify(page, null, 2));
|
|
549244
|
+
return 0;
|
|
549245
|
+
} catch (err) {
|
|
549246
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
549247
|
+
return 1;
|
|
549248
|
+
}
|
|
549249
|
+
}
|
|
549250
|
+
if (values2.runner === "local") {
|
|
549251
|
+
console.error(`Error: task ${id2} not found.`);
|
|
549252
|
+
return 1;
|
|
549253
|
+
}
|
|
549254
|
+
const agentId = getAgentId3(values2.agent);
|
|
549255
|
+
if (!agentId) {
|
|
549256
|
+
console.error(`Error: task ${id2} not found locally, and --agent or LETTA_AGENT_ID is required to look up Cloud schedule runs.`);
|
|
549257
|
+
return 1;
|
|
549258
|
+
}
|
|
548608
549259
|
try {
|
|
548609
|
-
|
|
548610
|
-
const
|
|
548611
|
-
|
|
548612
|
-
|
|
548613
|
-
|
|
548614
|
-
|
|
548615
|
-
|
|
549260
|
+
await ensureSettingsForCloud();
|
|
549261
|
+
const response = await listCloudScheduleHistory(agentId, id2, { limit: limit3 });
|
|
549262
|
+
console.log(JSON.stringify({
|
|
549263
|
+
runner: "cloud",
|
|
549264
|
+
entries: response.history,
|
|
549265
|
+
has_next_page: response.has_next_page
|
|
549266
|
+
}, null, 2));
|
|
548616
549267
|
return 0;
|
|
548617
549268
|
} catch (err) {
|
|
548618
549269
|
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
548619
549270
|
return 1;
|
|
548620
549271
|
}
|
|
548621
549272
|
}
|
|
548622
|
-
function handleDelete(values2, positionals) {
|
|
549273
|
+
async function handleDelete(values2, positionals) {
|
|
549274
|
+
if (!isRunnerFlagValid(values2.runner)) {
|
|
549275
|
+
console.error(`Error: invalid --runner "${values2.runner}". Expected "local" or "cloud".`);
|
|
549276
|
+
return 1;
|
|
549277
|
+
}
|
|
548623
549278
|
if (values2.all) {
|
|
548624
|
-
|
|
548625
|
-
if (!agentId) {
|
|
548626
|
-
console.error("Error: --agent or LETTA_AGENT_ID required with --all.");
|
|
548627
|
-
return 1;
|
|
548628
|
-
}
|
|
548629
|
-
const count = deleteAllTasks(agentId);
|
|
548630
|
-
console.log(JSON.stringify({ deleted: count, agent_id: agentId }));
|
|
548631
|
-
return 0;
|
|
549279
|
+
return handleDeleteAll(values2);
|
|
548632
549280
|
}
|
|
548633
549281
|
const taskId = positionals[1];
|
|
548634
549282
|
if (!taskId) {
|
|
548635
549283
|
console.error("Error: task ID required. Usage: letta cron delete <id> or --all --agent <id>");
|
|
548636
549284
|
return 1;
|
|
548637
549285
|
}
|
|
548638
|
-
|
|
548639
|
-
|
|
548640
|
-
|
|
549286
|
+
if (values2.runner !== "cloud") {
|
|
549287
|
+
const found = deleteTask(taskId);
|
|
549288
|
+
if (found) {
|
|
549289
|
+
console.log(JSON.stringify({ deleted: taskId, runner: "local" }));
|
|
549290
|
+
return 0;
|
|
549291
|
+
}
|
|
549292
|
+
if (values2.runner === "local") {
|
|
549293
|
+
console.error(`Error: task ${taskId} not found.`);
|
|
549294
|
+
return 1;
|
|
549295
|
+
}
|
|
549296
|
+
}
|
|
549297
|
+
const agentId = getAgentId3(values2.agent);
|
|
549298
|
+
if (!agentId) {
|
|
549299
|
+
console.error(`Error: task ${taskId} not found locally, and --agent or LETTA_AGENT_ID is required to delete Cloud schedules.`);
|
|
548641
549300
|
return 1;
|
|
548642
549301
|
}
|
|
548643
|
-
|
|
549302
|
+
try {
|
|
549303
|
+
await ensureSettingsForCloud();
|
|
549304
|
+
await getCloudSchedule(agentId, taskId);
|
|
549305
|
+
await deleteCloudSchedule(agentId, taskId);
|
|
549306
|
+
console.log(JSON.stringify({ deleted: taskId, runner: "cloud" }));
|
|
549307
|
+
return 0;
|
|
549308
|
+
} catch (err) {
|
|
549309
|
+
if (err instanceof ApiRequestError && err.status === 404) {
|
|
549310
|
+
console.error(`Error: task ${taskId} not found.`);
|
|
549311
|
+
} else {
|
|
549312
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
549313
|
+
}
|
|
549314
|
+
return 1;
|
|
549315
|
+
}
|
|
549316
|
+
}
|
|
549317
|
+
async function handleDeleteAll(values2) {
|
|
549318
|
+
const agentId = getAgentId3(values2.agent);
|
|
549319
|
+
if (!agentId) {
|
|
549320
|
+
console.error("Error: --agent or LETTA_AGENT_ID required with --all.");
|
|
549321
|
+
return 1;
|
|
549322
|
+
}
|
|
549323
|
+
const includeLocal = values2.runner !== "cloud";
|
|
549324
|
+
const includeCloud = values2.runner !== "local";
|
|
549325
|
+
let localDeleted = 0;
|
|
549326
|
+
if (includeLocal) {
|
|
549327
|
+
localDeleted = deleteAllTasks(agentId);
|
|
549328
|
+
}
|
|
549329
|
+
let cloudDeleted = 0;
|
|
549330
|
+
if (includeCloud) {
|
|
549331
|
+
const resolved = await getRunnerForAgent(undefined, agentId);
|
|
549332
|
+
const cloudCapable = !("error" in resolved) && resolved.runner === "cloud";
|
|
549333
|
+
const cloudExplicit = values2.runner === "cloud";
|
|
549334
|
+
if (cloudCapable || cloudExplicit) {
|
|
549335
|
+
try {
|
|
549336
|
+
const response = await listCloudSchedules(agentId);
|
|
549337
|
+
for (const schedule of response.scheduled_messages) {
|
|
549338
|
+
await deleteCloudSchedule(agentId, schedule.id);
|
|
549339
|
+
cloudDeleted += 1;
|
|
549340
|
+
}
|
|
549341
|
+
} catch (err) {
|
|
549342
|
+
console.error(`Error: failed to delete Cloud schedules: ${err instanceof Error ? err.message : String(err)}`);
|
|
549343
|
+
console.error(`Deleted so far: ${localDeleted} local, ${cloudDeleted} cloud.`);
|
|
549344
|
+
return 1;
|
|
549345
|
+
}
|
|
549346
|
+
}
|
|
549347
|
+
}
|
|
549348
|
+
console.log(JSON.stringify({
|
|
549349
|
+
deleted: localDeleted + cloudDeleted,
|
|
549350
|
+
local_deleted: localDeleted,
|
|
549351
|
+
cloud_deleted: cloudDeleted,
|
|
549352
|
+
agent_id: agentId
|
|
549353
|
+
}));
|
|
548644
549354
|
return 0;
|
|
548645
549355
|
}
|
|
548646
549356
|
async function runCronSubcommand(argv) {
|
|
@@ -548663,7 +549373,7 @@ async function runCronSubcommand(argv) {
|
|
|
548663
549373
|
case "list":
|
|
548664
549374
|
return handleList(parsed.values);
|
|
548665
549375
|
case "get":
|
|
548666
|
-
return handleGet(parsed.positionals);
|
|
549376
|
+
return handleGet(parsed.values, parsed.positionals);
|
|
548667
549377
|
case "runs":
|
|
548668
549378
|
return handleRuns(parsed.values);
|
|
548669
549379
|
case "delete":
|
|
@@ -556327,19 +557037,6 @@ function getModelForToolLoading(specifiedModel, specifiedToolset) {
|
|
|
556327
557037
|
}
|
|
556328
557038
|
return specifiedModel;
|
|
556329
557039
|
}
|
|
556330
|
-
async function findLocalAgentsByName(name) {
|
|
556331
|
-
const backend4 = getBackendForMode("local");
|
|
556332
|
-
const normalizedName = name.toLowerCase();
|
|
556333
|
-
try {
|
|
556334
|
-
const page = await backend4.listAgents({
|
|
556335
|
-
query_text: name,
|
|
556336
|
-
limit: 100
|
|
556337
|
-
});
|
|
556338
|
-
return paginatedItems6(page).filter((agent2) => agent2.name?.toLowerCase() === normalizedName);
|
|
556339
|
-
} catch {
|
|
556340
|
-
return [];
|
|
556341
|
-
}
|
|
556342
|
-
}
|
|
556343
557040
|
function getStartupTargetLookupOrderForCredentials({
|
|
556344
557041
|
baseURL,
|
|
556345
557042
|
explicitBackendMode,
|
|
@@ -556356,44 +557053,24 @@ function getStartupTargetLookupOrderForCredentials({
|
|
|
556356
557053
|
}
|
|
556357
557054
|
async function resolveAgentByName2(name, backendLookupOrder) {
|
|
556358
557055
|
const normalizedSearchName = name.toLowerCase();
|
|
557056
|
+
const pinnedAgents = await listPinnedAgentsForCurrentUser(backendLookupOrder);
|
|
556359
557057
|
for (const backendMode of backendLookupOrder) {
|
|
556360
|
-
const
|
|
556361
|
-
|
|
556362
|
-
|
|
556363
|
-
|
|
556364
|
-
|
|
556365
|
-
|
|
556366
|
-
const agent2 = await backend4.retrieveAgent(id2);
|
|
556367
|
-
if (agent2.name?.toLowerCase() === normalizedSearchName) {
|
|
556368
|
-
matches3.push({ id: id2, name: agent2.name, agent: agent2, backendMode });
|
|
556369
|
-
}
|
|
556370
|
-
} catch {}
|
|
556371
|
-
}));
|
|
556372
|
-
}
|
|
556373
|
-
if (backendMode === "local") {
|
|
556374
|
-
const seen = new Set(matches3.map((match4) => match4.id));
|
|
556375
|
-
for (const agent2 of await findLocalAgentsByName(name)) {
|
|
556376
|
-
if (!seen.has(agent2.id)) {
|
|
556377
|
-
matches3.push({
|
|
556378
|
-
id: agent2.id,
|
|
556379
|
-
name: agent2.name ?? agent2.id,
|
|
556380
|
-
agent: agent2,
|
|
556381
|
-
backendMode
|
|
556382
|
-
});
|
|
556383
|
-
seen.add(agent2.id);
|
|
556384
|
-
}
|
|
557058
|
+
const matches3 = pinnedAgents.flatMap((pinned) => pinned.backendMode === backendMode && pinned.agent?.name?.toLowerCase() === normalizedSearchName ? [
|
|
557059
|
+
{
|
|
557060
|
+
id: pinned.agentId,
|
|
557061
|
+
name: pinned.agent.name,
|
|
557062
|
+
agent: pinned.agent,
|
|
557063
|
+
backendMode
|
|
556385
557064
|
}
|
|
556386
|
-
|
|
557065
|
+
] : []);
|
|
556387
557066
|
if (matches3.length === 0)
|
|
556388
557067
|
continue;
|
|
556389
557068
|
if (matches3.length === 1)
|
|
556390
557069
|
return matches3[0] ?? null;
|
|
556391
|
-
const
|
|
556392
|
-
const localMatch = matches3.find((m4) => m4.id === localSettings.lastAgent);
|
|
557070
|
+
const localMatch = matches3.find((match4) => match4.id === settingsManager2.getLocalLastAgentId());
|
|
556393
557071
|
if (localMatch)
|
|
556394
557072
|
return localMatch;
|
|
556395
|
-
const
|
|
556396
|
-
const globalMatch = matches3.find((m4) => m4.id === settings3.lastAgent);
|
|
557073
|
+
const globalMatch = matches3.find((match4) => match4.id === settingsManager2.getGlobalLastAgentId());
|
|
556397
557074
|
if (globalMatch)
|
|
556398
557075
|
return globalMatch;
|
|
556399
557076
|
return matches3[0] ?? null;
|
|
@@ -556401,18 +557078,8 @@ async function resolveAgentByName2(name, backendLookupOrder) {
|
|
|
556401
557078
|
return null;
|
|
556402
557079
|
}
|
|
556403
557080
|
async function getPinnedAgentNames(backendLookupOrder) {
|
|
556404
|
-
const
|
|
556405
|
-
|
|
556406
|
-
const backend4 = getBackendForMode(backendMode);
|
|
556407
|
-
const pinnedAgents = settingsManager2.getPinnedAgentsForBackendMode(backendMode);
|
|
556408
|
-
await Promise.all(pinnedAgents.map(async (id2) => {
|
|
556409
|
-
try {
|
|
556410
|
-
const agent2 = await backend4.retrieveAgent(id2);
|
|
556411
|
-
agents.push({ id: id2, name: agent2.name || "(unnamed)" });
|
|
556412
|
-
} catch {}
|
|
556413
|
-
}));
|
|
556414
|
-
}
|
|
556415
|
-
return agents;
|
|
557081
|
+
const pinnedAgents = await listPinnedAgentsForCurrentUser(backendLookupOrder);
|
|
557082
|
+
return pinnedAgents.flatMap(({ agentId, agent: agent2 }) => agent2 ? [{ id: agentId, name: agent2.name || "(unnamed)" }] : []);
|
|
556416
557083
|
}
|
|
556417
557084
|
async function resolveConversationAcrossBackends(conversationId, backendLookupOrder) {
|
|
556418
557085
|
for (const backendMode of backendLookupOrder) {
|
|
@@ -557256,54 +557923,54 @@ Error: ${message}`);
|
|
|
557256
557923
|
setLoadingState("assembling");
|
|
557257
557924
|
return;
|
|
557258
557925
|
}
|
|
557259
|
-
const
|
|
557260
|
-
const
|
|
557261
|
-
const
|
|
557262
|
-
const localAgentId = startupBackendMode === "local" ? rawLocalAgentId : null;
|
|
557263
|
-
const globalAgentId = startupBackendMode === "api" ? rawGlobalAgentId : null;
|
|
557264
|
-
const agentIdsToValidate = [
|
|
557265
|
-
...new Set([...pinnedAgentIds, localAgentId, globalAgentId].filter((agentId2) => Boolean(agentId2)))
|
|
557266
|
-
];
|
|
557267
|
-
const validationResults = await Promise.allSettled(agentIdsToValidate.map(async (agentId2) => ({
|
|
557268
|
-
agentId: agentId2,
|
|
557269
|
-
agent: await backend4.retrieveAgent(agentId2, {
|
|
557270
|
-
include: ["agent.tags"]
|
|
557271
|
-
})
|
|
557272
|
-
})));
|
|
557273
|
-
const cachedAgents = new Map;
|
|
557274
|
-
for (const result of validationResults) {
|
|
557275
|
-
if (result.status === "fulfilled") {
|
|
557276
|
-
cachedAgents.set(result.value.agentId, result.value.agent);
|
|
557277
|
-
}
|
|
557278
|
-
}
|
|
557279
|
-
const existingPinnedIds = pinnedAgentIds.filter((id2) => cachedAgents.has(id2));
|
|
557280
|
-
const pinnedAgentId = existingPinnedIds.length === 1 ? existingPinnedIds[0] ?? null : null;
|
|
557281
|
-
const pinnedAgentExists = pinnedAgentId !== null;
|
|
557282
|
-
let localAgentExists = false;
|
|
557283
|
-
let globalAgentExists = false;
|
|
557926
|
+
const localAgentId = settingsManager2.getLocalLastAgentId(process.cwd());
|
|
557927
|
+
const globalAgentId = startupBackendMode === "api" ? settingsManager2.getGlobalLastAgentId() : null;
|
|
557928
|
+
const localSession = settingsManager2.getLocalLastSession(process.cwd());
|
|
557284
557929
|
if (localAgentId) {
|
|
557285
|
-
|
|
557286
|
-
|
|
557930
|
+
try {
|
|
557931
|
+
const localAgent = await backend4.retrieveAgent(localAgentId, {
|
|
557932
|
+
include: ["agent.tags"]
|
|
557933
|
+
});
|
|
557934
|
+
setSelectedGlobalAgentId(localAgentId);
|
|
557935
|
+
setValidatedAgent(localAgent);
|
|
557936
|
+
if (localSession?.conversationId && !forceNewConversation) {
|
|
557937
|
+
setSelectedConversationId(localSession.conversationId);
|
|
557938
|
+
}
|
|
557939
|
+
markMilestone2("STARTUP_LRU_FETCH_DONE");
|
|
557940
|
+
setLoadingState("assembling");
|
|
557941
|
+
return;
|
|
557942
|
+
} catch {
|
|
557287
557943
|
setFailedAgentMessage(`Unable to locate recently used agent ${localAgentId}`);
|
|
557288
557944
|
}
|
|
557289
557945
|
}
|
|
557290
|
-
|
|
557291
|
-
|
|
557946
|
+
const pinnedAgents = await listPinnedAgentsForCurrentUser([
|
|
557947
|
+
startupBackendMode
|
|
557948
|
+
]);
|
|
557949
|
+
const pinnedAgentIds = pinnedAgents.map(({ agentId: agentId2 }) => agentId2);
|
|
557950
|
+
const cachedAgents = new Map(pinnedAgents.flatMap(({ agentId: agentId2, agent: agent2 }) => agent2 ? [[agentId2, agent2]] : []));
|
|
557951
|
+
if (globalAgentId && !cachedAgents.has(globalAgentId)) {
|
|
557952
|
+
try {
|
|
557953
|
+
const globalAgent = await backend4.retrieveAgent(globalAgentId, {
|
|
557954
|
+
include: ["agent.tags"]
|
|
557955
|
+
});
|
|
557956
|
+
cachedAgents.set(globalAgentId, globalAgent);
|
|
557957
|
+
} catch {}
|
|
557292
557958
|
}
|
|
557959
|
+
const existingPinnedIds = pinnedAgentIds.filter((id2) => cachedAgents.has(id2));
|
|
557960
|
+
const pinnedAgentId = existingPinnedIds.length === 1 ? existingPinnedIds[0] ?? null : null;
|
|
557961
|
+
const pinnedAgentExists = pinnedAgentId !== null;
|
|
557962
|
+
const globalAgentExists = globalAgentId ? cachedAgents.has(globalAgentId) : false;
|
|
557293
557963
|
markMilestone2("STARTUP_LRU_FETCH_DONE");
|
|
557294
|
-
const
|
|
557295
|
-
const existingPinnedCount = existingPinnedIds.length;
|
|
557296
|
-
const fallbackSession = startupBackendMode === "local" && !localAgentExists && !globalAgentExists ? await getLocalBackendStartupFallbackSession(backend4) : null;
|
|
557964
|
+
const fallbackSession = startupBackendMode === "local" && !globalAgentExists ? await getLocalBackendStartupFallbackSession(backend4) : null;
|
|
557297
557965
|
const { resolveStartupTarget: resolveStartupTarget2 } = await Promise.resolve().then(() => exports_resolve_startup_agent);
|
|
557298
|
-
const localSession = settingsManager2.getLocalLastSession(process.cwd());
|
|
557299
557966
|
const target2 = resolveStartupTarget2({
|
|
557300
557967
|
pinnedAgentId,
|
|
557301
557968
|
pinnedAgentExists,
|
|
557302
|
-
pinnedCount,
|
|
557303
|
-
existingPinnedCount,
|
|
557969
|
+
pinnedCount: pinnedAgentIds.length,
|
|
557970
|
+
existingPinnedCount: existingPinnedIds.length,
|
|
557304
557971
|
localAgentId,
|
|
557305
557972
|
localConversationId: localSession?.conversationId ?? null,
|
|
557306
|
-
localAgentExists,
|
|
557973
|
+
localAgentExists: false,
|
|
557307
557974
|
globalAgentId,
|
|
557308
557975
|
globalAgentExists,
|
|
557309
557976
|
fallbackAgentId: fallbackSession?.agentId ?? null,
|
|
@@ -557872,4 +558539,4 @@ Error during initialization: ${message}`);
|
|
|
557872
558539
|
}
|
|
557873
558540
|
main2();
|
|
557874
558541
|
|
|
557875
|
-
//# debugId=
|
|
558542
|
+
//# debugId=E8D52F75291B5E0B64756E2164756E21
|