@anuma/sdk 1.1.5 → 1.2.0
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/expo/{chunk-IKPPX3GY.mjs → chunk-OZ357R4P.mjs} +75 -40
- package/dist/expo/index.cjs +104 -42
- package/dist/expo/index.d.mts +15 -0
- package/dist/expo/index.d.ts +15 -0
- package/dist/expo/index.mjs +30 -3
- package/dist/expo/{recall-4JTNZBAW.mjs → recall-EMFBX3WU.mjs} +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.mjs +1 -0
- package/dist/react/{chunk-KQZ4KVYY.mjs → chunk-DC6J5RX5.mjs} +75 -40
- package/dist/react/index.cjs +104 -42
- package/dist/react/index.d.mts +15 -0
- package/dist/react/index.d.ts +15 -0
- package/dist/react/index.mjs +30 -3
- package/dist/react/{recall-Z2HUBS2Y.mjs → recall-RGKS2OIP.mjs} +1 -1
- package/dist/server/{chunk-KLJBJWWI.mjs → chunk-MKRSHU4S.mjs} +1 -1
- package/dist/server/index.cjs +30 -2
- package/dist/server/index.d.mts +15 -0
- package/dist/server/index.d.ts +15 -0
- package/dist/server/index.mjs +31 -3
- package/dist/server/{recall-IF2HX7ZA.mjs → recall-V4763HKS.mjs} +1 -1
- package/dist/tools/index.cjs +412 -58
- package/dist/tools/index.d.mts +14 -3
- package/dist/tools/index.d.ts +14 -3
- package/dist/tools/index.mjs +412 -58
- package/package.json +1 -1
package/dist/tools/index.cjs
CHANGED
|
@@ -3395,6 +3395,7 @@ function createDropboxTools(getToken, requestAccess) {
|
|
|
3395
3395
|
|
|
3396
3396
|
// src/tools/slack.ts
|
|
3397
3397
|
var SLACK_PROVIDER = "slack";
|
|
3398
|
+
var SLACK_PENDING_APPROVAL_NOTE = "This Slack request couldn't be completed because Slack rate-limited the app. The Slack app is still pending Slack Marketplace approval, so these reads aren't available yet. Tell the user this is a temporary limitation until the app is approved; do not attempt a workaround.";
|
|
3398
3399
|
var AUTH_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
3399
3400
|
"not_authed",
|
|
3400
3401
|
"invalid_auth",
|
|
@@ -3418,8 +3419,10 @@ function maybeConnectorError4(status, body) {
|
|
|
3418
3419
|
}
|
|
3419
3420
|
return null;
|
|
3420
3421
|
}
|
|
3421
|
-
|
|
3422
|
-
|
|
3422
|
+
function isRateLimited(status, body) {
|
|
3423
|
+
return status === 429 || body?.ok === false && body.error === "ratelimited";
|
|
3424
|
+
}
|
|
3425
|
+
function interpretSlackResult(path, status, json) {
|
|
3423
3426
|
const body = json ?? null;
|
|
3424
3427
|
if (status < 200 || status >= 300) {
|
|
3425
3428
|
const connectorError = maybeConnectorError4(status, body);
|
|
@@ -3433,6 +3436,10 @@ async function callSlack(callProxy, path, query, requestBody) {
|
|
|
3433
3436
|
}
|
|
3434
3437
|
return body;
|
|
3435
3438
|
}
|
|
3439
|
+
async function callSlack(callProxy, path, query, requestBody) {
|
|
3440
|
+
const { status, json } = await callProxy(path, query, requestBody);
|
|
3441
|
+
return interpretSlackResult(path, status, json);
|
|
3442
|
+
}
|
|
3436
3443
|
function readString(raw) {
|
|
3437
3444
|
return typeof raw === "string" ? raw : "";
|
|
3438
3445
|
}
|
|
@@ -3441,6 +3448,70 @@ function clampLimit(raw, fallback, min, max) {
|
|
|
3441
3448
|
const safe = typeof num === "number" && Number.isFinite(num) ? num : fallback;
|
|
3442
3449
|
return Math.min(max, Math.max(min, safe));
|
|
3443
3450
|
}
|
|
3451
|
+
function makeGetAuthUserId(callProxy) {
|
|
3452
|
+
let cached;
|
|
3453
|
+
return async () => {
|
|
3454
|
+
if (cached === void 0) {
|
|
3455
|
+
const auth = await callSlack(callProxy, "/auth.test");
|
|
3456
|
+
cached = typeof auth === "string" ? null : auth.user_id ?? null;
|
|
3457
|
+
}
|
|
3458
|
+
return cached;
|
|
3459
|
+
};
|
|
3460
|
+
}
|
|
3461
|
+
function displayNameForUser(user) {
|
|
3462
|
+
return user.profile?.display_name || user.real_name || user.name || user.id;
|
|
3463
|
+
}
|
|
3464
|
+
function authorName(m, directory) {
|
|
3465
|
+
const known = m.user ? directory.byId.get(m.user) : void 0;
|
|
3466
|
+
return known && displayNameForUser(known) || m.username || m.user || "";
|
|
3467
|
+
}
|
|
3468
|
+
var USER_MENTION_RE = /<@([UW][A-Z0-9]+)(?:\|([^>]+))?>/g;
|
|
3469
|
+
var CHANNEL_MENTION_RE = /<#(C[A-Z0-9]+)(?:\|([^>]+))?>/g;
|
|
3470
|
+
function humanizeSlackText(text, directory) {
|
|
3471
|
+
return text.replace(USER_MENTION_RE, (_match, id, handle) => {
|
|
3472
|
+
const known = directory.byId.get(id);
|
|
3473
|
+
return `@${known && displayNameForUser(known) || handle || id}`;
|
|
3474
|
+
}).replace(
|
|
3475
|
+
CHANNEL_MENTION_RE,
|
|
3476
|
+
(match, _id, name) => name ? `#${name}` : match
|
|
3477
|
+
);
|
|
3478
|
+
}
|
|
3479
|
+
var MAX_USERS_PAGES = 10;
|
|
3480
|
+
async function listAllUsers(callProxy) {
|
|
3481
|
+
const all = [];
|
|
3482
|
+
let cursor = "";
|
|
3483
|
+
for (let page = 0; page < MAX_USERS_PAGES; page++) {
|
|
3484
|
+
const query = { limit: 1e3 };
|
|
3485
|
+
if (cursor) query.cursor = cursor;
|
|
3486
|
+
const res = await callSlack(callProxy, "/users.list", query);
|
|
3487
|
+
if (typeof res === "string") {
|
|
3488
|
+
if (page === 0) return res;
|
|
3489
|
+
break;
|
|
3490
|
+
}
|
|
3491
|
+
all.push(...res.members ?? []);
|
|
3492
|
+
const next = res.response_metadata?.next_cursor;
|
|
3493
|
+
if (!next) break;
|
|
3494
|
+
cursor = next;
|
|
3495
|
+
}
|
|
3496
|
+
return all;
|
|
3497
|
+
}
|
|
3498
|
+
function makeGetUsersDirectory(callProxy) {
|
|
3499
|
+
let cached;
|
|
3500
|
+
return async () => {
|
|
3501
|
+
if (cached === void 0) {
|
|
3502
|
+
const res = await listAllUsers(callProxy);
|
|
3503
|
+
const members = (typeof res === "string" ? [] : res).filter((u) => !u.deleted);
|
|
3504
|
+
const byId = /* @__PURE__ */ new Map();
|
|
3505
|
+
const byHandle = /* @__PURE__ */ new Map();
|
|
3506
|
+
for (const user of members) {
|
|
3507
|
+
byId.set(user.id, user);
|
|
3508
|
+
if (user.name) byHandle.set(user.name.toLowerCase(), user);
|
|
3509
|
+
}
|
|
3510
|
+
cached = { members, byId, byHandle };
|
|
3511
|
+
}
|
|
3512
|
+
return cached;
|
|
3513
|
+
};
|
|
3514
|
+
}
|
|
3444
3515
|
async function getSlackMe(callProxy) {
|
|
3445
3516
|
const auth = await callSlack(callProxy, "/auth.test");
|
|
3446
3517
|
if (typeof auth === "string") return auth;
|
|
@@ -3463,14 +3534,10 @@ async function getSlackMe(callProxy) {
|
|
|
3463
3534
|
};
|
|
3464
3535
|
}
|
|
3465
3536
|
async function listSlackChannels(callProxy, args) {
|
|
3466
|
-
const limit = clampLimit(args.limit,
|
|
3467
|
-
const res = await
|
|
3468
|
-
limit,
|
|
3469
|
-
exclude_archived: "true",
|
|
3470
|
-
types: "public_channel,private_channel"
|
|
3471
|
-
});
|
|
3537
|
+
const limit = clampLimit(args.limit, 1e3, 1, 5e3);
|
|
3538
|
+
const res = await listAllConversations(callProxy, "public_channel,private_channel");
|
|
3472
3539
|
if (typeof res === "string") return res;
|
|
3473
|
-
return
|
|
3540
|
+
return res.slice(0, limit).map((c) => ({
|
|
3474
3541
|
id: c.id,
|
|
3475
3542
|
name: c.name,
|
|
3476
3543
|
is_private: c.is_private,
|
|
@@ -3479,8 +3546,150 @@ async function listSlackChannels(callProxy, args) {
|
|
|
3479
3546
|
purpose: c.purpose?.value
|
|
3480
3547
|
}));
|
|
3481
3548
|
}
|
|
3549
|
+
var MAX_DM_PROBES = 50;
|
|
3550
|
+
async function openSlackDm(callProxy, userId) {
|
|
3551
|
+
const res = await callSlack(
|
|
3552
|
+
callProxy,
|
|
3553
|
+
"/conversations.open",
|
|
3554
|
+
void 0,
|
|
3555
|
+
{ users: userId }
|
|
3556
|
+
);
|
|
3557
|
+
if (typeof res === "string") return null;
|
|
3558
|
+
return res.channel?.id ?? null;
|
|
3559
|
+
}
|
|
3560
|
+
async function dmHasMessages(callProxy, channelId) {
|
|
3561
|
+
const { status, json } = await callProxy("/conversations.history", {
|
|
3562
|
+
channel: channelId,
|
|
3563
|
+
limit: 1
|
|
3564
|
+
});
|
|
3565
|
+
const body = json ?? null;
|
|
3566
|
+
if (isRateLimited(status, body)) return true;
|
|
3567
|
+
const res = interpretSlackResult(
|
|
3568
|
+
"/conversations.history",
|
|
3569
|
+
status,
|
|
3570
|
+
json
|
|
3571
|
+
);
|
|
3572
|
+
if (typeof res === "string") return true;
|
|
3573
|
+
return (res.messages ?? []).length > 0;
|
|
3574
|
+
}
|
|
3575
|
+
async function listSlackDms(callProxy, args) {
|
|
3576
|
+
const res = await listAllConversations(callProxy, "im,mpim");
|
|
3577
|
+
if (typeof res === "string") return res;
|
|
3578
|
+
const getAuthUserId = makeGetAuthUserId(callProxy);
|
|
3579
|
+
const getUsersDirectory = makeGetUsersDirectory(callProxy);
|
|
3580
|
+
const dmNameCache = /* @__PURE__ */ new Map();
|
|
3581
|
+
const buildListing = async (convs) => {
|
|
3582
|
+
const listing = { direct_messages: [], group_dms: [] };
|
|
3583
|
+
for (const conv of convs) {
|
|
3584
|
+
if (conv.is_mpim) {
|
|
3585
|
+
const [directory, selfId] = await Promise.all([getUsersDirectory(), getAuthUserId()]);
|
|
3586
|
+
const members = groupDmMembers(conv.name, directory, selfId) ?? formatGroupDmLabel(conv.name, directory, selfId);
|
|
3587
|
+
listing.group_dms.push({ id: conv.id, members });
|
|
3588
|
+
continue;
|
|
3589
|
+
}
|
|
3590
|
+
const name = await labelForChannel(
|
|
3591
|
+
callProxy,
|
|
3592
|
+
conv,
|
|
3593
|
+
dmNameCache,
|
|
3594
|
+
getAuthUserId,
|
|
3595
|
+
getUsersDirectory
|
|
3596
|
+
);
|
|
3597
|
+
listing.direct_messages.push({ id: conv.id, name });
|
|
3598
|
+
}
|
|
3599
|
+
return listing;
|
|
3600
|
+
};
|
|
3601
|
+
const withUserRef = (args.with_user ?? "").trim();
|
|
3602
|
+
if (withUserRef) {
|
|
3603
|
+
const targetId = await resolveSlackUserId(withUserRef, getAuthUserId, getUsersDirectory);
|
|
3604
|
+
if (!targetId) return `Error: couldn't find a Slack user matching "${withUserRef}".`;
|
|
3605
|
+
const directory = await getUsersDirectory();
|
|
3606
|
+
const targetHandle = directory.byId.get(targetId)?.name?.toLowerCase();
|
|
3607
|
+
const matches = res.filter((conv) => {
|
|
3608
|
+
if (conv.is_mpim) {
|
|
3609
|
+
return targetHandle ? parseMpdmHandles(conv.name).some((h) => h.toLowerCase() === targetHandle) : false;
|
|
3610
|
+
}
|
|
3611
|
+
return conv.user === targetId;
|
|
3612
|
+
});
|
|
3613
|
+
const targetLimit = clampLimit(args.limit, matches.length, 1, matches.length);
|
|
3614
|
+
const listing = await buildListing(matches.slice(0, targetLimit));
|
|
3615
|
+
if (listing.direct_messages.length === 0) {
|
|
3616
|
+
const openedId = await openSlackDm(callProxy, targetId);
|
|
3617
|
+
if (openedId) {
|
|
3618
|
+
const surface = listing.group_dms.length === 0 || await dmHasMessages(callProxy, openedId);
|
|
3619
|
+
if (surface) {
|
|
3620
|
+
const counterparty = directory.byId.get(targetId);
|
|
3621
|
+
listing.direct_messages.push({
|
|
3622
|
+
id: openedId,
|
|
3623
|
+
name: counterparty ? displayNameForUser(counterparty) : targetId
|
|
3624
|
+
});
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
return listing;
|
|
3629
|
+
}
|
|
3630
|
+
const limit = clampLimit(args.limit, 5, 1, MAX_DM_PROBES);
|
|
3631
|
+
if (res.length > MAX_DM_PROBES) return SLACK_PENDING_APPROVAL_NOTE;
|
|
3632
|
+
const withTs = [];
|
|
3633
|
+
const withoutTs = [];
|
|
3634
|
+
for (const conv of res) {
|
|
3635
|
+
const { status, json } = await callProxy("/conversations.history", {
|
|
3636
|
+
channel: conv.id,
|
|
3637
|
+
limit: 1
|
|
3638
|
+
});
|
|
3639
|
+
const body = json ?? null;
|
|
3640
|
+
if (isRateLimited(status, body)) return SLACK_PENDING_APPROVAL_NOTE;
|
|
3641
|
+
if (status < 200 || status >= 300 || !body || body.ok === false) {
|
|
3642
|
+
withoutTs.push(conv);
|
|
3643
|
+
continue;
|
|
3644
|
+
}
|
|
3645
|
+
const messages = body.messages ?? [];
|
|
3646
|
+
if (messages.length === 0) continue;
|
|
3647
|
+
withTs.push({ conv, lastTs: Number(messages[0].ts) });
|
|
3648
|
+
}
|
|
3649
|
+
withTs.sort((a, b) => b.lastTs - a.lastTs);
|
|
3650
|
+
const ordered = [...withTs.map((x) => x.conv), ...withoutTs];
|
|
3651
|
+
return buildListing(ordered.slice(0, limit));
|
|
3652
|
+
}
|
|
3482
3653
|
var MAX_SEARCH_CHANNELS = 8;
|
|
3483
3654
|
var SEARCH_HISTORY_LIMIT = 15;
|
|
3655
|
+
var MAX_CONVERSATIONS_PAGES = 10;
|
|
3656
|
+
async function listAllConversations(callProxy, types) {
|
|
3657
|
+
const all = [];
|
|
3658
|
+
let cursor = "";
|
|
3659
|
+
for (let page = 0; page < MAX_CONVERSATIONS_PAGES; page++) {
|
|
3660
|
+
const query = {
|
|
3661
|
+
limit: 1e3,
|
|
3662
|
+
exclude_archived: "true",
|
|
3663
|
+
types
|
|
3664
|
+
};
|
|
3665
|
+
if (cursor) query.cursor = cursor;
|
|
3666
|
+
const res = await callSlack(
|
|
3667
|
+
callProxy,
|
|
3668
|
+
"/conversations.list",
|
|
3669
|
+
query
|
|
3670
|
+
);
|
|
3671
|
+
if (typeof res === "string") {
|
|
3672
|
+
if (page === 0) return res;
|
|
3673
|
+
break;
|
|
3674
|
+
}
|
|
3675
|
+
all.push(...res.channels ?? []);
|
|
3676
|
+
const next = res.response_metadata?.next_cursor;
|
|
3677
|
+
if (!next) break;
|
|
3678
|
+
cursor = next;
|
|
3679
|
+
}
|
|
3680
|
+
return all;
|
|
3681
|
+
}
|
|
3682
|
+
async function resolveChannelId(callProxy, ref) {
|
|
3683
|
+
const trimmed = ref.trim().replace(/^#/, "");
|
|
3684
|
+
if (SLACK_CHANNEL_ID_RE.test(trimmed)) return { id: trimmed };
|
|
3685
|
+
const channels = await listAllConversations(callProxy, "public_channel,private_channel");
|
|
3686
|
+
if (typeof channels === "string") return channels;
|
|
3687
|
+
const match = channels.find((c) => c.name?.toLowerCase() === trimmed.toLowerCase());
|
|
3688
|
+
if (!match) {
|
|
3689
|
+
return `Error: no Slack channel named "${ref}" in this workspace (checked the channels you're a member of).`;
|
|
3690
|
+
}
|
|
3691
|
+
return { id: match.id };
|
|
3692
|
+
}
|
|
3484
3693
|
function interleaveChannelsAndDms(conversations) {
|
|
3485
3694
|
const channels = conversations.filter((c) => !c.is_im && !c.is_mpim);
|
|
3486
3695
|
const dms = conversations.filter((c) => c.is_im || c.is_mpim);
|
|
@@ -3496,8 +3705,40 @@ function messageMatchesQuery(text, terms) {
|
|
|
3496
3705
|
const haystack = text.toLowerCase();
|
|
3497
3706
|
return terms.every((term) => haystack.includes(term));
|
|
3498
3707
|
}
|
|
3499
|
-
|
|
3500
|
-
|
|
3708
|
+
var MAX_GROUP_DM_NAMES = 5;
|
|
3709
|
+
var MPDM_NAME_RE = /^mpdm-(.+)-\d+$/;
|
|
3710
|
+
function parseMpdmHandles(mpimName) {
|
|
3711
|
+
if (!mpimName) return [];
|
|
3712
|
+
const match = MPDM_NAME_RE.exec(mpimName);
|
|
3713
|
+
return match ? match[1].split("--") : [];
|
|
3714
|
+
}
|
|
3715
|
+
function groupDmMembers(mpimName, directory, selfId) {
|
|
3716
|
+
const handles = parseMpdmHandles(mpimName);
|
|
3717
|
+
if (handles.length === 0) return null;
|
|
3718
|
+
const selfHandle = selfId ? directory.byId.get(selfId)?.name?.toLowerCase() : void 0;
|
|
3719
|
+
const names = [];
|
|
3720
|
+
for (const handle of handles) {
|
|
3721
|
+
const key = handle.toLowerCase();
|
|
3722
|
+
if (selfHandle && key === selfHandle) continue;
|
|
3723
|
+
const member = directory.byHandle.get(key);
|
|
3724
|
+
names.push(member ? displayNameForUser(member) : handle);
|
|
3725
|
+
}
|
|
3726
|
+
if (names.length === 0) return null;
|
|
3727
|
+
const shown = names.slice(0, MAX_GROUP_DM_NAMES);
|
|
3728
|
+
const extra = names.length - shown.length;
|
|
3729
|
+
return `${shown.join(", ")}${extra > 0 ? `, +${extra} more` : ""}`;
|
|
3730
|
+
}
|
|
3731
|
+
function formatGroupDmLabel(mpimName, directory, selfId) {
|
|
3732
|
+
if (!mpimName) return "Group DM";
|
|
3733
|
+
if (parseMpdmHandles(mpimName).length === 0) return mpimName;
|
|
3734
|
+
const members = groupDmMembers(mpimName, directory, selfId);
|
|
3735
|
+
return members === null ? "Group DM" : `Group DM with ${members}`;
|
|
3736
|
+
}
|
|
3737
|
+
async function labelForChannel(callProxy, channel, dmNameCache, getAuthUserId, getUsersDirectory) {
|
|
3738
|
+
if (channel.is_mpim) {
|
|
3739
|
+
const [directory2, selfId] = await Promise.all([getUsersDirectory(), getAuthUserId()]);
|
|
3740
|
+
return formatGroupDmLabel(channel.name, directory2, selfId);
|
|
3741
|
+
}
|
|
3501
3742
|
if (!channel.is_im) return channel.name ?? channel.id;
|
|
3502
3743
|
const otherId = channel.user;
|
|
3503
3744
|
if (!otherId) return channel.id;
|
|
@@ -3505,72 +3746,130 @@ async function labelForChannel(callProxy, channel, dmNameCache, getAuthUserId) {
|
|
|
3505
3746
|
if (authUserId && otherId === authUserId) return "Direct message";
|
|
3506
3747
|
const cached = dmNameCache.get(otherId);
|
|
3507
3748
|
if (cached) return `DM with ${cached}`;
|
|
3749
|
+
const directory = await getUsersDirectory();
|
|
3750
|
+
const known = directory.byId.get(otherId);
|
|
3751
|
+
if (known) {
|
|
3752
|
+
const name2 = displayNameForUser(known);
|
|
3753
|
+
dmNameCache.set(otherId, name2);
|
|
3754
|
+
return `DM with ${name2}`;
|
|
3755
|
+
}
|
|
3508
3756
|
const info = await callSlack(callProxy, "/users.info", { user: otherId });
|
|
3509
3757
|
if (typeof info === "string" || !info.user) return channel.id;
|
|
3510
3758
|
const name = info.user.profile?.display_name || info.user.real_name || info.user.name || otherId;
|
|
3511
3759
|
dmNameCache.set(otherId, name);
|
|
3512
3760
|
return `DM with ${name}`;
|
|
3513
3761
|
}
|
|
3762
|
+
var SLACK_USER_ID_RE = /^[UW][A-Z0-9]{6,}$/;
|
|
3763
|
+
var SLACK_CHANNEL_ID_RE = /^[CDG][A-Z0-9]{6,}$/;
|
|
3764
|
+
var SELF_REFS = /* @__PURE__ */ new Set(["me", "myself", "self", "i"]);
|
|
3765
|
+
async function resolveSlackUserId(ref, getAuthUserId, getUsersDirectory) {
|
|
3766
|
+
const trimmed = ref.trim();
|
|
3767
|
+
if (!trimmed) return null;
|
|
3768
|
+
if (SELF_REFS.has(trimmed.toLowerCase())) return getAuthUserId();
|
|
3769
|
+
if (SLACK_USER_ID_RE.test(trimmed)) return trimmed;
|
|
3770
|
+
const needle = trimmed.replace(/^@/, "").toLowerCase();
|
|
3771
|
+
if (!needle) return null;
|
|
3772
|
+
const { members } = await getUsersDirectory();
|
|
3773
|
+
const fieldsOf = (u) => [u.name, u.real_name, u.profile?.display_name].filter((f) => Boolean(f));
|
|
3774
|
+
const matching = (pred) => members.filter((u) => fieldsOf(u).some((f) => pred(f.toLowerCase())));
|
|
3775
|
+
const exact = matching((f) => f === needle);
|
|
3776
|
+
if (exact.length === 1) return exact[0].id;
|
|
3777
|
+
if (exact.length > 1) return null;
|
|
3778
|
+
const partial = matching((f) => f.includes(needle));
|
|
3779
|
+
return partial.length === 1 ? partial[0].id : null;
|
|
3780
|
+
}
|
|
3514
3781
|
async function searchSlackMessages(callProxy, args) {
|
|
3782
|
+
const query = (args.query ?? "").trim();
|
|
3783
|
+
const fromUserRef = (args.from_user ?? "").trim();
|
|
3784
|
+
const mentionsRef = (args.mentions ?? "").trim();
|
|
3785
|
+
if (!query && !fromUserRef && !mentionsRef) {
|
|
3786
|
+
return "Error: provide a query, from_user, or mentions to search Slack messages.";
|
|
3787
|
+
}
|
|
3515
3788
|
const count = clampLimit(args.count, 20, 1, 100);
|
|
3516
|
-
const terms =
|
|
3517
|
-
const
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3789
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
3790
|
+
const getAuthUserId = makeGetAuthUserId(callProxy);
|
|
3791
|
+
const getUsersDirectory = makeGetUsersDirectory(callProxy);
|
|
3792
|
+
let fromId = null;
|
|
3793
|
+
if (fromUserRef) {
|
|
3794
|
+
fromId = await resolveSlackUserId(fromUserRef, getAuthUserId, getUsersDirectory);
|
|
3795
|
+
if (!fromId)
|
|
3796
|
+
return `Error: couldn't find a Slack user matching "${fromUserRef}" for from_user.`;
|
|
3797
|
+
}
|
|
3798
|
+
let mentionId = null;
|
|
3799
|
+
if (mentionsRef) {
|
|
3800
|
+
mentionId = await resolveSlackUserId(mentionsRef, getAuthUserId, getUsersDirectory);
|
|
3801
|
+
if (!mentionId) {
|
|
3802
|
+
return `Error: couldn't find a Slack user matching "${mentionsRef}" for mentions.`;
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
const passes = (m) => {
|
|
3806
|
+
const text = m.text ?? "";
|
|
3807
|
+
if (terms.length > 0 && !messageMatchesQuery(text, terms)) return false;
|
|
3808
|
+
if (fromId && m.user !== fromId) return false;
|
|
3809
|
+
if (mentionId && !text.includes("<@" + mentionId)) return false;
|
|
3810
|
+
return true;
|
|
3811
|
+
};
|
|
3812
|
+
const listRes = await listAllConversations(callProxy, "public_channel,private_channel,im,mpim");
|
|
3526
3813
|
if (typeof listRes === "string") return listRes;
|
|
3527
|
-
const allChannels = listRes
|
|
3814
|
+
const allChannels = listRes;
|
|
3528
3815
|
let targets;
|
|
3529
3816
|
if (args.channel) {
|
|
3530
|
-
const
|
|
3531
|
-
|
|
3817
|
+
const raw = args.channel.trim();
|
|
3818
|
+
const bare = raw.replace(/^#/, "");
|
|
3819
|
+
const found = allChannels.find(
|
|
3820
|
+
(c) => c.id === raw || c.name?.toLowerCase() === bare.toLowerCase()
|
|
3821
|
+
);
|
|
3822
|
+
if (found) {
|
|
3823
|
+
targets = [found];
|
|
3824
|
+
} else if (SLACK_CHANNEL_ID_RE.test(bare)) {
|
|
3825
|
+
targets = [{ id: bare, name: bare }];
|
|
3826
|
+
} else {
|
|
3827
|
+
return `Error: no Slack channel named "${args.channel}" in this workspace.`;
|
|
3828
|
+
}
|
|
3532
3829
|
} else {
|
|
3533
3830
|
targets = interleaveChannelsAndDms(allChannels).slice(0, MAX_SEARCH_CHANNELS);
|
|
3534
3831
|
}
|
|
3535
|
-
let cachedAuthUserId;
|
|
3536
|
-
const getAuthUserId = async () => {
|
|
3537
|
-
if (cachedAuthUserId === void 0) {
|
|
3538
|
-
const auth = await callSlack(callProxy, "/auth.test");
|
|
3539
|
-
cachedAuthUserId = typeof auth === "string" ? null : auth.user_id ?? null;
|
|
3540
|
-
}
|
|
3541
|
-
return cachedAuthUserId;
|
|
3542
|
-
};
|
|
3543
3832
|
const dmNameCache = /* @__PURE__ */ new Map();
|
|
3544
3833
|
const results = [];
|
|
3545
3834
|
let rateLimited = false;
|
|
3546
3835
|
for (const channel of targets) {
|
|
3547
|
-
if (results.length >= count) break;
|
|
3548
3836
|
const { status, json } = await callProxy("/conversations.history", {
|
|
3549
3837
|
channel: channel.id,
|
|
3550
3838
|
limit: SEARCH_HISTORY_LIMIT
|
|
3551
3839
|
});
|
|
3552
3840
|
const body = json ?? null;
|
|
3553
|
-
if (status
|
|
3841
|
+
if (isRateLimited(status, body)) {
|
|
3554
3842
|
rateLimited = true;
|
|
3555
3843
|
break;
|
|
3556
3844
|
}
|
|
3557
3845
|
const connectorError = maybeConnectorError4(status, body);
|
|
3558
3846
|
if (connectorError) return connectorError;
|
|
3559
3847
|
if (status < 200 || status >= 300 || !body || body.ok === false) continue;
|
|
3560
|
-
const matched = (body.messages ?? []).filter(
|
|
3848
|
+
const matched = (body.messages ?? []).filter(passes);
|
|
3561
3849
|
if (matched.length === 0) continue;
|
|
3562
|
-
const label = await labelForChannel(
|
|
3850
|
+
const label = await labelForChannel(
|
|
3851
|
+
callProxy,
|
|
3852
|
+
channel,
|
|
3853
|
+
dmNameCache,
|
|
3854
|
+
getAuthUserId,
|
|
3855
|
+
getUsersDirectory
|
|
3856
|
+
);
|
|
3857
|
+
const directory = await getUsersDirectory();
|
|
3563
3858
|
for (const m of matched) {
|
|
3564
|
-
|
|
3565
|
-
|
|
3859
|
+
results.push({
|
|
3860
|
+
text: humanizeSlackText(m.text ?? "", directory),
|
|
3861
|
+
user: authorName(m, directory),
|
|
3862
|
+
ts: m.ts,
|
|
3863
|
+
channel: label
|
|
3864
|
+
});
|
|
3566
3865
|
}
|
|
3567
3866
|
}
|
|
3867
|
+
results.sort((a, b) => Number(b.ts) - Number(a.ts));
|
|
3868
|
+
const sliced = results.slice(0, count);
|
|
3568
3869
|
if (rateLimited) {
|
|
3569
|
-
|
|
3570
|
-
note: `Slack rate-limited channel history, so this search stopped early \u2014 returning ${results.length} match(es) found so far. Try a specific channel or ask again in a minute.`
|
|
3571
|
-
});
|
|
3870
|
+
sliced.push({ note: SLACK_PENDING_APPROVAL_NOTE });
|
|
3572
3871
|
}
|
|
3573
|
-
return
|
|
3872
|
+
return sliced;
|
|
3574
3873
|
}
|
|
3575
3874
|
async function listSlackUsers(callProxy, args) {
|
|
3576
3875
|
const limit = clampLimit(args.limit, 100, 1, 1e3);
|
|
@@ -3586,29 +3885,46 @@ async function listSlackUsers(callProxy, args) {
|
|
|
3586
3885
|
}
|
|
3587
3886
|
async function getSlackChannelHistory(callProxy, args) {
|
|
3588
3887
|
const limit = clampLimit(args.limit, 20, 1, 100);
|
|
3589
|
-
const
|
|
3590
|
-
|
|
3888
|
+
const resolved = await resolveChannelId(callProxy, args.channel);
|
|
3889
|
+
if (typeof resolved === "string") return resolved;
|
|
3890
|
+
const { status, json } = await callProxy("/conversations.history", {
|
|
3891
|
+
channel: resolved.id,
|
|
3892
|
+
limit
|
|
3893
|
+
});
|
|
3894
|
+
const body = json ?? null;
|
|
3895
|
+
if (isRateLimited(status, body)) return SLACK_PENDING_APPROVAL_NOTE;
|
|
3896
|
+
const res = interpretSlackResult(
|
|
3591
3897
|
"/conversations.history",
|
|
3592
|
-
|
|
3898
|
+
status,
|
|
3899
|
+
json
|
|
3593
3900
|
);
|
|
3594
3901
|
if (typeof res === "string") return res;
|
|
3902
|
+
const directory = await makeGetUsersDirectory(callProxy)();
|
|
3595
3903
|
return (res.messages ?? []).map((m) => ({
|
|
3596
|
-
text: m.text,
|
|
3597
|
-
user: m
|
|
3904
|
+
text: humanizeSlackText(m.text ?? "", directory),
|
|
3905
|
+
user: authorName(m, directory),
|
|
3598
3906
|
ts: m.ts
|
|
3599
3907
|
}));
|
|
3600
3908
|
}
|
|
3601
3909
|
async function getSlackThreadReplies(callProxy, args) {
|
|
3602
3910
|
const limit = clampLimit(args.limit, 20, 1, 100);
|
|
3603
|
-
const
|
|
3604
|
-
|
|
3911
|
+
const { status, json } = await callProxy("/conversations.replies", {
|
|
3912
|
+
channel: args.channel,
|
|
3913
|
+
ts: args.ts,
|
|
3914
|
+
limit
|
|
3915
|
+
});
|
|
3916
|
+
const body = json ?? null;
|
|
3917
|
+
if (isRateLimited(status, body)) return SLACK_PENDING_APPROVAL_NOTE;
|
|
3918
|
+
const res = interpretSlackResult(
|
|
3605
3919
|
"/conversations.replies",
|
|
3606
|
-
|
|
3920
|
+
status,
|
|
3921
|
+
json
|
|
3607
3922
|
);
|
|
3608
3923
|
if (typeof res === "string") return res;
|
|
3924
|
+
const directory = await makeGetUsersDirectory(callProxy)();
|
|
3609
3925
|
return (res.messages ?? []).map((m) => ({
|
|
3610
|
-
text: m.text,
|
|
3611
|
-
user: m
|
|
3926
|
+
text: humanizeSlackText(m.text ?? "", directory),
|
|
3927
|
+
user: authorName(m, directory),
|
|
3612
3928
|
ts: m.ts
|
|
3613
3929
|
}));
|
|
3614
3930
|
}
|
|
@@ -3643,7 +3959,7 @@ function createSlackListChannelsTool(callProxy) {
|
|
|
3643
3959
|
properties: {
|
|
3644
3960
|
limit: {
|
|
3645
3961
|
type: "number",
|
|
3646
|
-
description: "Max channels to return. Between 1 and
|
|
3962
|
+
description: "Max channels to return. Between 1 and 5000 (clamped). Defaults to 1000."
|
|
3647
3963
|
}
|
|
3648
3964
|
},
|
|
3649
3965
|
required: []
|
|
@@ -3657,13 +3973,13 @@ function createSlackSearchMessagesTool(callProxy) {
|
|
|
3657
3973
|
type: "function",
|
|
3658
3974
|
function: {
|
|
3659
3975
|
name: "slack_search_messages",
|
|
3660
|
-
description: "Search recent messages
|
|
3976
|
+
description: "Search recent messages across the user's Slack channels and direct messages, or within a single channel when 'channel' is given. Filter by any combination of 'query' (words the text must contain), 'from_user' (the author), and 'mentions' (who the message @-mentions) -- at least one is required. Only recent history is scanned (a bounded set of conversations), so this surfaces recent messages rather than the full archive. Use this to find messages by KEYWORD or TOPIC across conversations. Do NOT use it to read the latest/last message from a specific person or channel, or to read a specific conversation you already know: for the last message from a person, call slack_list_dms with with_user to get the DM id then slack_get_channel_history on it; for a channel, call slack_get_channel_history directly. Search fans out across many conversations and is heavily rate-limited, so a single targeted read is faster and more reliable.",
|
|
3661
3977
|
parameters: {
|
|
3662
3978
|
type: "object",
|
|
3663
3979
|
properties: {
|
|
3664
3980
|
query: {
|
|
3665
3981
|
type: "string",
|
|
3666
|
-
description: "Words to look for
|
|
3982
|
+
description: "Optional. Words to look for; a message matches when its text contains all of them (case-insensitive). Provide this, from_user, or mentions."
|
|
3667
3983
|
},
|
|
3668
3984
|
count: {
|
|
3669
3985
|
type: "number",
|
|
@@ -3672,15 +3988,52 @@ function createSlackSearchMessagesTool(callProxy) {
|
|
|
3672
3988
|
channel: {
|
|
3673
3989
|
type: "string",
|
|
3674
3990
|
description: "Optional channel id or name to search within. Omit to search across your channels."
|
|
3991
|
+
},
|
|
3992
|
+
from_user: {
|
|
3993
|
+
type: "string",
|
|
3994
|
+
description: "Optional. The message author to filter by, as a Slack user id or a name/handle."
|
|
3995
|
+
},
|
|
3996
|
+
mentions: {
|
|
3997
|
+
type: "string",
|
|
3998
|
+
description: 'Optional. Keep only messages that @-mention this person, as a user id, a name/handle, or "me" for yourself.'
|
|
3675
3999
|
}
|
|
3676
4000
|
},
|
|
3677
|
-
required: [
|
|
4001
|
+
required: []
|
|
3678
4002
|
}
|
|
3679
4003
|
},
|
|
3680
4004
|
executor: async (args) => searchSlackMessages(callProxy, {
|
|
3681
4005
|
query: readString(args.query),
|
|
3682
4006
|
count: args.count,
|
|
3683
|
-
channel: typeof args.channel === "string" ? args.channel : void 0
|
|
4007
|
+
channel: typeof args.channel === "string" ? args.channel : void 0,
|
|
4008
|
+
from_user: typeof args.from_user === "string" ? args.from_user : void 0,
|
|
4009
|
+
mentions: typeof args.mentions === "string" ? args.mentions : void 0
|
|
4010
|
+
})
|
|
4011
|
+
};
|
|
4012
|
+
}
|
|
4013
|
+
function createSlackListDmsTool(callProxy) {
|
|
4014
|
+
return {
|
|
4015
|
+
type: "function",
|
|
4016
|
+
function: {
|
|
4017
|
+
name: "slack_list_dms",
|
|
4018
|
+
description: "List the user's Slack direct messages and group DMs, or find the DM with a specific person. Returns two separate lists: 'direct_messages' (1:1 conversations, each with 'name' = the other person) and 'group_dms' (group conversations, each with 'members' = the member names). When showing these to the user, present them as TWO separate bulleted lists -- a 'Direct messages' list (by the other person's name) and a 'Group DMs' list (by the member names) -- by name only, and never show the conversation ids. To show or read the conversation with one person, call this with with_user=<name or id> to get that conversation's id, then call slack_get_channel_history with that id to read the messages. For the latest message or recent messages from a specific person, the flow is: call this with with_user, take the returned id, then call slack_get_channel_history on it (use limit: 1 for just the last message). Do NOT use slack_search_messages to find a person's DM conversation or to read their latest message -- use with_user here instead. Listing all DMs (no with_user) returns the 5 MOST RECENT by default (most recent first, like the Slack sidebar, with empty conversations excluded), split across 'direct_messages' and 'group_dms'; pass a larger 'limit' to get more. The conversation id is only for passing to slack_get_channel_history to read a conversation.",
|
|
4019
|
+
parameters: {
|
|
4020
|
+
type: "object",
|
|
4021
|
+
properties: {
|
|
4022
|
+
limit: {
|
|
4023
|
+
type: "number",
|
|
4024
|
+
description: "Max DMs to return, default 5, up to 50."
|
|
4025
|
+
},
|
|
4026
|
+
with_user: {
|
|
4027
|
+
type: "string",
|
|
4028
|
+
description: "Optional. Return only the DM(s) with this person, as a Slack user id or a name/handle. Use this to find and read the DM with a specific person. Works even for a DM that isn't currently open -- it will open/resume the conversation to read it."
|
|
4029
|
+
}
|
|
4030
|
+
},
|
|
4031
|
+
required: []
|
|
4032
|
+
}
|
|
4033
|
+
},
|
|
4034
|
+
executor: async (args) => listSlackDms(callProxy, {
|
|
4035
|
+
limit: args.limit,
|
|
4036
|
+
with_user: typeof args.with_user === "string" ? args.with_user : void 0
|
|
3684
4037
|
})
|
|
3685
4038
|
};
|
|
3686
4039
|
}
|
|
@@ -3709,7 +4062,7 @@ function createSlackGetChannelHistoryTool(callProxy) {
|
|
|
3709
4062
|
type: "function",
|
|
3710
4063
|
function: {
|
|
3711
4064
|
name: "slack_get_channel_history",
|
|
3712
|
-
description: "Fetch recent messages from a single Slack channel by id. Best-effort and heavily rate-limited (~1 request per minute for the app), so use it sparingly for a specific channel.",
|
|
4065
|
+
description: "Fetch recent messages from a single Slack channel by id. Best-effort and heavily rate-limited (~1 request per minute for the app), so use it sparingly for a specific channel. This is the right tool to read the most recent messages (including just the latest one with limit: 1) from a known channel or DM id, and should be PREFERRED over slack_search_messages whenever the conversation to read is already known or identified.",
|
|
3713
4066
|
parameters: {
|
|
3714
4067
|
type: "object",
|
|
3715
4068
|
properties: {
|
|
@@ -3799,6 +4152,7 @@ function createSlackTools(callProxy) {
|
|
|
3799
4152
|
return {
|
|
3800
4153
|
slack_get_me: createSlackGetMeTool(callProxy),
|
|
3801
4154
|
slack_list_channels: createSlackListChannelsTool(callProxy),
|
|
4155
|
+
slack_list_dms: createSlackListDmsTool(callProxy),
|
|
3802
4156
|
slack_search_messages: createSlackSearchMessagesTool(callProxy),
|
|
3803
4157
|
slack_list_users: createSlackListUsersTool(callProxy),
|
|
3804
4158
|
slack_get_channel_history: createSlackGetChannelHistoryTool(callProxy),
|