@cuylabs/channel-slack-agent-core 0.5.1 → 0.7.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/README.md +20 -39
- package/dist/{adapter.d.ts → adapter/index.d.ts} +7 -5
- package/dist/adapter/index.js +9 -0
- package/dist/{adapter-Cmd2C90g.d.ts → adapter-B3CI611y.d.ts} +1 -1
- package/dist/app-surface.d.ts +13 -3
- package/dist/app-surface.js +5 -7
- package/dist/app.d.ts +9 -4
- package/dist/app.js +6 -9
- package/dist/artifacts/index.d.ts +57 -0
- package/dist/artifacts/index.js +6 -0
- package/dist/{assistant.d.ts → assistant/index.d.ts} +6 -4
- package/dist/{assistant.js → assistant/index.js} +3 -5
- package/dist/{chunk-CYEBGC6G.js → chunk-76SRS54H.js} +5 -5
- package/dist/{chunk-M64Z6TYL.js → chunk-7DUO5BMW.js} +16 -6
- package/dist/{chunk-NIPAN4KA.js → chunk-A2PLAVW6.js} +2 -3
- package/dist/chunk-C7CHMYV6.js +226 -0
- package/dist/chunk-C7VSW4ZM.js +548 -0
- package/dist/{chunk-JMLB7A2V.js → chunk-DJPKRKGP.js} +5 -5
- package/dist/chunk-ELR6MQD7.js +12 -0
- package/dist/{chunk-FDRQOG7Q.js → chunk-FQWFB54C.js} +26 -15
- package/dist/{chunk-BFUPAJON.js → chunk-MGBNGG4D.js} +59 -37
- package/dist/chunk-NNCVHQC4.js +94 -0
- package/dist/chunk-P7PFQ3SQ.js +396 -0
- package/dist/{chunk-WO4BJMF3.js → chunk-TCNJY7QA.js} +5 -5
- package/dist/{chunk-DHPD4XH5.js → chunk-TMADMHBN.js} +210 -29
- package/dist/{chunk-IWUYIAY5.js → chunk-VMVQIDNR.js} +5 -7
- package/dist/{chunk-IXY3BXU5.js → chunk-X7ILLZZP.js} +359 -2
- package/dist/context-fragments-CQEDcjYR.d.ts +30 -0
- package/dist/express-assistant.d.ts +6 -3
- package/dist/express-assistant.js +4 -7
- package/dist/express.d.ts +7 -3
- package/dist/express.js +3 -6
- package/dist/feedback/index.d.ts +1 -0
- package/dist/feedback/index.js +10 -0
- package/dist/history/index.d.ts +61 -0
- package/dist/history/index.js +8 -0
- package/dist/index.d.ts +23 -16
- package/dist/index.js +81 -160
- package/dist/interactive/index.d.ts +71 -0
- package/dist/{interactive.js → interactive/index.js} +9 -4
- package/dist/mcp.js +0 -1
- package/dist/{options-C7OYeNR-.d.ts → options-BcDReOJv.d.ts} +48 -0
- package/dist/{options-Uf-qmQKN.d.ts → options-CdqBABcM.d.ts} +26 -1
- package/dist/{shared.d.ts → shared/index.d.ts} +24 -36
- package/dist/{shared.js → shared/index.js} +2 -6
- package/dist/socket.d.ts +9 -4
- package/dist/socket.js +6 -9
- package/dist/source/index.d.ts +154 -0
- package/dist/source/index.js +38 -0
- package/dist/{types-BqRzb_Cd.d.ts → types-CRWzJB5G.d.ts} +35 -0
- package/dist/types-CiwGU6zC.d.ts +56 -0
- package/dist/views/index.d.ts +8 -0
- package/dist/views/index.js +10 -0
- package/docs/README.md +18 -0
- package/docs/concepts/final-response-artifacts.md +39 -0
- package/docs/concepts/interactive-requests.md +43 -0
- package/docs/concepts/tool-task-rendering.md +46 -0
- package/docs/concepts/view-workflows.md +52 -0
- package/docs/reference/boundary.md +22 -0
- package/docs/reference/exports.md +26 -0
- package/package.json +36 -50
- package/dist/adapter.js +0 -13
- package/dist/bolt.d.ts +0 -8
- package/dist/bolt.js +0 -10
- package/dist/chunk-2SUAW6MV.js +0 -12
- package/dist/chunk-645NNJIM.js +0 -12
- package/dist/chunk-ANIZ5NT4.js +0 -12
- package/dist/chunk-GNXWTKQ6.js +0 -48
- package/dist/chunk-HFT2FXJP.js +0 -12
- package/dist/chunk-I2KLQ2HA.js +0 -22
- package/dist/chunk-K2E6A377.js +0 -12
- package/dist/chunk-NDVXBI7Z.js +0 -12
- package/dist/chunk-PX4RGO3N.js +0 -12
- package/dist/chunk-VHGV66M7.js +0 -12
- package/dist/diagnostics.d.ts +0 -1
- package/dist/diagnostics.js +0 -10
- package/dist/feedback.d.ts +0 -1
- package/dist/feedback.js +0 -10
- package/dist/history.d.ts +0 -1
- package/dist/history.js +0 -10
- package/dist/interactive.d.ts +0 -30
- package/dist/policy.d.ts +0 -1
- package/dist/policy.js +0 -10
- package/dist/setup.d.ts +0 -1
- package/dist/setup.js +0 -10
- package/dist/targets.d.ts +0 -1
- package/dist/targets.js +0 -10
- package/dist/users.d.ts +0 -1
- package/dist/users.js +0 -10
|
@@ -1,38 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
resolveSlackMessageFormatter
|
|
7
|
-
} from "./chunk-GNXWTKQ6.js";
|
|
2
|
+
SLACK_FEEDBACK_ACTION_ID,
|
|
3
|
+
createSlackFeedbackBlock,
|
|
4
|
+
registerSlackFeedbackAction
|
|
5
|
+
} from "./chunk-ELR6MQD7.js";
|
|
8
6
|
import {
|
|
9
7
|
UnsupportedSlackInteractiveRequestError,
|
|
10
8
|
bridgeAgentEventsToSlack,
|
|
11
|
-
resolveSlackEventBridgeOptions
|
|
12
|
-
|
|
13
|
-
} from "./chunk-DHPD4XH5.js";
|
|
14
|
-
|
|
15
|
-
// src/assistant/message-parser.ts
|
|
16
|
-
import {
|
|
17
|
-
parseSlackMessageActivityFromMessageEvent
|
|
18
|
-
} from "@cuylabs/channel-slack/assistant";
|
|
19
|
-
|
|
20
|
-
// src/assistant/session.ts
|
|
21
|
-
function resolveAssistantSessionId(message, strategy) {
|
|
22
|
-
switch (strategy) {
|
|
23
|
-
case "channel-id":
|
|
24
|
-
return message.channel;
|
|
25
|
-
case "user-per-thread":
|
|
26
|
-
return `${message.channel}:${message.threadTs}:${message.userId}`;
|
|
27
|
-
default:
|
|
28
|
-
return `${message.channel}:${message.threadTs}`;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// src/assistant/thread-context-store.ts
|
|
33
|
-
import {
|
|
34
|
-
createSlackAssistantThreadContextStore
|
|
35
|
-
} from "@cuylabs/channel-slack/assistant";
|
|
9
|
+
resolveSlackEventBridgeOptions
|
|
10
|
+
} from "./chunk-TMADMHBN.js";
|
|
36
11
|
|
|
37
12
|
// src/assistant/bridge.ts
|
|
38
13
|
import { Assistant } from "@slack/bolt";
|
|
@@ -190,6 +165,24 @@ function createThreadContextChangedHandler() {
|
|
|
190
165
|
import {
|
|
191
166
|
withinScope
|
|
192
167
|
} from "@cuylabs/agent-core";
|
|
168
|
+
import {
|
|
169
|
+
extractSlackAuthContext,
|
|
170
|
+
resolveSlackMessageFormatter,
|
|
171
|
+
runWithSlackTurnContext
|
|
172
|
+
} from "@cuylabs/channel-slack/core";
|
|
173
|
+
import { parseSlackMessageActivityFromMessageEvent } from "@cuylabs/channel-slack/assistant";
|
|
174
|
+
|
|
175
|
+
// src/assistant/session.ts
|
|
176
|
+
function resolveAssistantSessionId(message, strategy) {
|
|
177
|
+
switch (strategy) {
|
|
178
|
+
case "channel-id":
|
|
179
|
+
return message.channel;
|
|
180
|
+
case "user-per-thread":
|
|
181
|
+
return `${message.channel}:${message.threadTs}:${message.userId}`;
|
|
182
|
+
default:
|
|
183
|
+
return `${message.channel}:${message.threadTs}`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
193
186
|
|
|
194
187
|
// src/assistant/scope-attributes.ts
|
|
195
188
|
function buildAssistantScopeAttributes(message, auth, threadContext, prep) {
|
|
@@ -246,6 +239,11 @@ function createAssistantSink(params) {
|
|
|
246
239
|
return stream;
|
|
247
240
|
}
|
|
248
241
|
return {
|
|
242
|
+
artifactClient: params.client,
|
|
243
|
+
artifactTarget: {
|
|
244
|
+
channelId: params.channel,
|
|
245
|
+
threadTs: params.threadTs
|
|
246
|
+
},
|
|
249
247
|
async postMessage() {
|
|
250
248
|
throw new Error(
|
|
251
249
|
"Assistant bridge uses chat-stream mode and does not support postMessage."
|
|
@@ -434,6 +432,7 @@ function createUserMessageHandler(deps) {
|
|
|
434
432
|
...options.formatToolTitle ? { formatToolTitle: options.formatToolTitle } : {},
|
|
435
433
|
...options.formatToolUpdate ? { formatToolUpdate: options.formatToolUpdate } : {},
|
|
436
434
|
...options.formatToolDetails ? { formatToolDetails: options.formatToolDetails } : {},
|
|
435
|
+
...options.formatToolResultOutput ? { formatToolResultOutput: options.formatToolResultOutput } : {},
|
|
437
436
|
...options.formatToolError ? { formatToolError: options.formatToolError } : {},
|
|
438
437
|
...options.formatReasoningUpdate ? { formatReasoningUpdate: options.formatReasoningUpdate } : {},
|
|
439
438
|
...options.chatStreamBufferSize !== void 0 ? { chatStreamBufferSize: options.chatStreamBufferSize } : {},
|
|
@@ -441,6 +440,22 @@ function createUserMessageHandler(deps) {
|
|
|
441
440
|
...options.maxTaskUpdateTextChars !== void 0 ? { maxTaskUpdateTextChars: options.maxTaskUpdateTextChars } : {},
|
|
442
441
|
...options.maxTaskUpdateFieldChars !== void 0 ? { maxTaskUpdateFieldChars: options.maxTaskUpdateFieldChars } : {},
|
|
443
442
|
...chatStreamFinalArgs ? { chatStreamFinalArgs } : {},
|
|
443
|
+
...options.publishFinalResponseArtifact ? {
|
|
444
|
+
publishFinalResponseArtifact: options.publishFinalResponseArtifact
|
|
445
|
+
} : {},
|
|
446
|
+
...options.finalResponseArtifactMode ? { finalResponseArtifactMode: options.finalResponseArtifactMode } : {},
|
|
447
|
+
...options.finalResponseArtifactStreamThreshold !== void 0 ? {
|
|
448
|
+
finalResponseArtifactStreamThreshold: options.finalResponseArtifactStreamThreshold
|
|
449
|
+
} : {},
|
|
450
|
+
...options.formatFinalResponseArtifactContinuationNotice ? {
|
|
451
|
+
formatFinalResponseArtifactContinuationNotice: options.formatFinalResponseArtifactContinuationNotice
|
|
452
|
+
} : {},
|
|
453
|
+
...options.formatFinalResponseArtifactMessage ? {
|
|
454
|
+
formatFinalResponseArtifactMessage: options.formatFinalResponseArtifactMessage
|
|
455
|
+
} : {},
|
|
456
|
+
...options.onFinalResponseArtifactError ? {
|
|
457
|
+
onFinalResponseArtifactError: options.onFinalResponseArtifactError
|
|
458
|
+
} : {},
|
|
444
459
|
formatMessageText: messageFormatter,
|
|
445
460
|
onStatusChange: async (label, event) => {
|
|
446
461
|
const update = await resolveStatusUpdate(
|
|
@@ -603,6 +618,7 @@ ${formatStreamError(translatedError)}`
|
|
|
603
618
|
}
|
|
604
619
|
|
|
605
620
|
// src/assistant/bridge.ts
|
|
621
|
+
import { createSlackAssistantThreadContextStore } from "@cuylabs/channel-slack/assistant";
|
|
606
622
|
function createSlackAssistantBridge(options) {
|
|
607
623
|
if (!options.source || typeof options.source.chat !== "function") {
|
|
608
624
|
throw new Error(
|
|
@@ -616,8 +632,8 @@ function createSlackAssistantBridge(options) {
|
|
|
616
632
|
}
|
|
617
633
|
const sessionStrategy = options.sessionStrategy ?? "thread-aware";
|
|
618
634
|
const feedbackConfig = options.feedback === false ? void 0 : options.feedback ?? {};
|
|
619
|
-
const feedbackBlock = feedbackConfig ?
|
|
620
|
-
const feedbackActionId = feedbackConfig?.actionId ??
|
|
635
|
+
const feedbackBlock = feedbackConfig ? createSlackFeedbackBlock(feedbackConfig) : void 0;
|
|
636
|
+
const feedbackActionId = feedbackConfig?.actionId ?? SLACK_FEEDBACK_ACTION_ID;
|
|
621
637
|
const threadStarted = createThreadStartedHandler(options);
|
|
622
638
|
const threadContextChanged = createThreadContextChangedHandler();
|
|
623
639
|
const threadContextStore = options.threadContextStore ?? createSlackAssistantThreadContextStore();
|
|
@@ -639,7 +655,7 @@ function createSlackAssistantBridge(options) {
|
|
|
639
655
|
function install(app) {
|
|
640
656
|
app.assistant(assistant);
|
|
641
657
|
if (feedbackConfig) {
|
|
642
|
-
|
|
658
|
+
registerSlackFeedbackAction(app, {
|
|
643
659
|
...feedbackConfig,
|
|
644
660
|
resolveSessionId: (context) => resolveFeedbackSessionId(context, sessionStrategy),
|
|
645
661
|
onFeedback: feedbackConfig.onFeedback ?? (async (_ctx) => {
|
|
@@ -654,9 +670,15 @@ function createSlackAssistantBridge(options) {
|
|
|
654
670
|
};
|
|
655
671
|
}
|
|
656
672
|
|
|
673
|
+
// src/assistant/index.ts
|
|
674
|
+
import {
|
|
675
|
+
createSlackAssistantThreadContextStore as createSlackAssistantThreadContextStore2,
|
|
676
|
+
parseSlackMessageActivityFromMessageEvent as parseSlackMessageActivityFromMessageEvent2
|
|
677
|
+
} from "@cuylabs/channel-slack/assistant";
|
|
678
|
+
|
|
657
679
|
export {
|
|
658
|
-
parseSlackMessageActivityFromMessageEvent,
|
|
659
680
|
resolveAssistantSessionId,
|
|
660
|
-
|
|
661
|
-
|
|
681
|
+
createSlackAssistantBridge,
|
|
682
|
+
createSlackAssistantThreadContextStore2 as createSlackAssistantThreadContextStore,
|
|
683
|
+
parseSlackMessageActivityFromMessageEvent2 as parseSlackMessageActivityFromMessageEvent
|
|
662
684
|
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/artifacts/final-response.ts
|
|
2
|
+
import {
|
|
3
|
+
publishSlackCanvasArtifact,
|
|
4
|
+
publishSlackTextArtifact
|
|
5
|
+
} from "@cuylabs/channel-slack/artifacts";
|
|
6
|
+
var DEFAULT_MIN_CHARACTERS = 4e3;
|
|
7
|
+
var DEFAULT_TITLE = "Agent response";
|
|
8
|
+
function createSlackFinalResponseArtifactPublisher({
|
|
9
|
+
minCharacters = DEFAULT_MIN_CHARACTERS,
|
|
10
|
+
title = DEFAULT_TITLE,
|
|
11
|
+
summary,
|
|
12
|
+
channelCanvas = true,
|
|
13
|
+
fallback = "file",
|
|
14
|
+
onPublished,
|
|
15
|
+
onError
|
|
16
|
+
} = {}) {
|
|
17
|
+
const configuredThreshold = Math.floor(minCharacters);
|
|
18
|
+
const threshold = Number.isFinite(configuredThreshold) && configuredThreshold > 0 ? configuredThreshold : DEFAULT_MIN_CHARACTERS;
|
|
19
|
+
return async (context) => {
|
|
20
|
+
const text = context.text.trim();
|
|
21
|
+
if (text.length < threshold) {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
const resolvedTitle = normalizeText(await resolveValue(title, context)) ?? DEFAULT_TITLE;
|
|
25
|
+
const resolvedSummary = normalizeText(await resolveValue(summary, context));
|
|
26
|
+
let lastError;
|
|
27
|
+
try {
|
|
28
|
+
const publication = await publishSlackCanvasArtifact({
|
|
29
|
+
client: context.client,
|
|
30
|
+
channelId: context.channelId,
|
|
31
|
+
...context.threadTs ? { threadTs: context.threadTs } : {},
|
|
32
|
+
artifact: {
|
|
33
|
+
kind: "canvas",
|
|
34
|
+
title: resolvedTitle,
|
|
35
|
+
markdown: text,
|
|
36
|
+
channelCanvas,
|
|
37
|
+
...resolvedSummary ? { summary: resolvedSummary } : {},
|
|
38
|
+
metadata: {
|
|
39
|
+
source: "slack-final-response",
|
|
40
|
+
characterCount: text.length
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const result = { publication };
|
|
45
|
+
await onPublished?.(result, context);
|
|
46
|
+
return result;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
lastError = error;
|
|
49
|
+
}
|
|
50
|
+
if (fallback === "file") {
|
|
51
|
+
try {
|
|
52
|
+
const publication = await publishSlackTextArtifact({
|
|
53
|
+
client: context.client,
|
|
54
|
+
channelId: context.channelId,
|
|
55
|
+
...context.threadTs ? { threadTs: context.threadTs } : {},
|
|
56
|
+
artifact: {
|
|
57
|
+
kind: "text",
|
|
58
|
+
title: resolvedTitle,
|
|
59
|
+
text,
|
|
60
|
+
filename: filenameFromTitle(resolvedTitle),
|
|
61
|
+
...resolvedSummary ? { summary: resolvedSummary } : {},
|
|
62
|
+
metadata: {
|
|
63
|
+
source: "slack-final-response",
|
|
64
|
+
characterCount: text.length,
|
|
65
|
+
canvasFallback: true
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const result = { publication };
|
|
70
|
+
await onPublished?.(result, context);
|
|
71
|
+
return result;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
lastError = error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
await onError?.(lastError, context);
|
|
77
|
+
return void 0;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function resolveValue(value, context) {
|
|
81
|
+
return typeof value === "function" ? await value(context) : value;
|
|
82
|
+
}
|
|
83
|
+
function normalizeText(value) {
|
|
84
|
+
const trimmed = value?.trim();
|
|
85
|
+
return trimmed ? trimmed : void 0;
|
|
86
|
+
}
|
|
87
|
+
function filenameFromTitle(title) {
|
|
88
|
+
const slug = title.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
89
|
+
return `${slug || "agent-response"}.txt`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export {
|
|
93
|
+
createSlackFinalResponseArtifactPublisher
|
|
94
|
+
};
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
// src/history/turn-context.ts
|
|
2
|
+
import {
|
|
3
|
+
createSlackSupplementalHistoryVisibilityPolicy,
|
|
4
|
+
loadSlackTurnHistoryContext
|
|
5
|
+
} from "@cuylabs/channel-slack/history";
|
|
6
|
+
import { enrichSlackUserMentions } from "@cuylabs/channel-slack/users";
|
|
7
|
+
import { WebClient } from "@slack/web-api";
|
|
8
|
+
async function loadSlackAgentTurnHistoryContext({
|
|
9
|
+
history,
|
|
10
|
+
logger,
|
|
11
|
+
originChannelId: providedOriginChannelId,
|
|
12
|
+
profileResolver,
|
|
13
|
+
request
|
|
14
|
+
}) {
|
|
15
|
+
const originChannelId = providedOriginChannelId ?? resolveOriginChannelId(request);
|
|
16
|
+
const client = resolveSlackHistoryClient(request);
|
|
17
|
+
if (!history.enabled) {
|
|
18
|
+
logger?.debug?.("Slack context history disabled", {
|
|
19
|
+
channelId: request.slackActivity.channelId,
|
|
20
|
+
threadTs: request.slackActivity.threadTs
|
|
21
|
+
});
|
|
22
|
+
return emptySlackAgentTurnHistoryContextResult();
|
|
23
|
+
}
|
|
24
|
+
if (!client) {
|
|
25
|
+
logger?.warn?.("Slack context history enabled without a Slack WebClient", {
|
|
26
|
+
channelId: request.slackActivity.channelId,
|
|
27
|
+
threadTs: request.slackActivity.threadTs
|
|
28
|
+
});
|
|
29
|
+
return emptySlackAgentTurnHistoryContextResult();
|
|
30
|
+
}
|
|
31
|
+
const teamId = request.slackActivity.teamId ?? request.user.teamId ?? request.auth?.teamId;
|
|
32
|
+
const threadTs = request.slackActivity.threadTs ?? request.slackActivity.messageTs;
|
|
33
|
+
const resolveAuthor = profileResolver ? createSlackAgentHistoryAuthorResolver({
|
|
34
|
+
botToken: request.auth?.botToken,
|
|
35
|
+
logger,
|
|
36
|
+
profileResolver,
|
|
37
|
+
teamId
|
|
38
|
+
}) : void 0;
|
|
39
|
+
const loaded = await loadSlackTurnHistoryContext({
|
|
40
|
+
activity: request.slackActivity,
|
|
41
|
+
botUserId: request.auth?.botUserId,
|
|
42
|
+
client,
|
|
43
|
+
includeTopLevelChannelFallback: request.slackActivity.channelType !== "dm",
|
|
44
|
+
limit: history.limit,
|
|
45
|
+
maxCharacters: history.maxCharacters,
|
|
46
|
+
token: request.auth?.botToken,
|
|
47
|
+
...teamId ? { teamId } : {},
|
|
48
|
+
...originChannelId ? { originChannelId } : {},
|
|
49
|
+
visibilityPolicy: createSlackSupplementalHistoryVisibilityPolicy({
|
|
50
|
+
allowedUserIds: history.visibilityAllowedUserIds ?? [],
|
|
51
|
+
mode: history.visibilityMode ?? "all"
|
|
52
|
+
}),
|
|
53
|
+
onVisibilityPolicyError: ({ channelId, error, source, teamId: teamId2 }) => {
|
|
54
|
+
logger?.warn?.("Slack supplemental history visibility policy failed", {
|
|
55
|
+
channelId,
|
|
56
|
+
source,
|
|
57
|
+
...teamId2 ? { teamId: teamId2 } : {},
|
|
58
|
+
error: formatHistoryError(error)
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
...request.assistant ? {
|
|
62
|
+
assistantSurfaceThreadRootUser: {
|
|
63
|
+
userId: request.slackActivity.userId
|
|
64
|
+
},
|
|
65
|
+
onAssistantSurfaceThreadRootNormalized: ({
|
|
66
|
+
action,
|
|
67
|
+
channelId,
|
|
68
|
+
teamId: normalizedTeamId,
|
|
69
|
+
threadTs: threadTs2,
|
|
70
|
+
userId
|
|
71
|
+
}) => {
|
|
72
|
+
logger?.debug?.(
|
|
73
|
+
action === "removed-duplicate" ? "Slack assistant surface duplicate thread root removed" : "Slack assistant surface thread root author corrected",
|
|
74
|
+
{
|
|
75
|
+
channelId,
|
|
76
|
+
...normalizedTeamId ? { teamId: normalizedTeamId } : {},
|
|
77
|
+
threadTs: threadTs2,
|
|
78
|
+
userId
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
} : {},
|
|
83
|
+
headers: {
|
|
84
|
+
channel: "Recent Slack channel history before the current message:",
|
|
85
|
+
...originChannelId ? {
|
|
86
|
+
originChannel: `Recent Slack channel context from ${originChannelId}:`
|
|
87
|
+
} : {},
|
|
88
|
+
thread: "Recent Slack thread history before the current message:"
|
|
89
|
+
},
|
|
90
|
+
...resolveAuthor ? { resolveAuthor } : {},
|
|
91
|
+
...resolveAuthor ? {
|
|
92
|
+
onResolveAuthorError: ({
|
|
93
|
+
channelId,
|
|
94
|
+
error,
|
|
95
|
+
source,
|
|
96
|
+
teamId: errorTeamId,
|
|
97
|
+
userId
|
|
98
|
+
}) => {
|
|
99
|
+
logger?.debug?.("Slack history author attribution failed", {
|
|
100
|
+
channelId,
|
|
101
|
+
source,
|
|
102
|
+
...errorTeamId ? { teamId: errorTeamId } : {},
|
|
103
|
+
...userId ? { userId } : {},
|
|
104
|
+
error: formatHistoryError(error)
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
} : {}
|
|
108
|
+
});
|
|
109
|
+
const unavailableReasons = summarizeUnavailable(loaded.unavailable);
|
|
110
|
+
for (const issue of loaded.unavailable) {
|
|
111
|
+
logSlackAgentHistoryUnavailable(logger, issue);
|
|
112
|
+
}
|
|
113
|
+
const loadedPrompt = await enrichSlackAgentHistoryMentions({
|
|
114
|
+
botToken: request.auth?.botToken,
|
|
115
|
+
logger,
|
|
116
|
+
profileResolver,
|
|
117
|
+
prompt: loaded.prompt,
|
|
118
|
+
teamId
|
|
119
|
+
});
|
|
120
|
+
const prompt = truncateHistoryPrompt(
|
|
121
|
+
[
|
|
122
|
+
loadedPrompt,
|
|
123
|
+
...loaded.unavailable.map(
|
|
124
|
+
(issue) => (history.unavailableNoteFormatter ?? formatSlackAgentHistoryUnavailableNote)(issue)
|
|
125
|
+
)
|
|
126
|
+
].filter(Boolean).join("\n\n"),
|
|
127
|
+
history
|
|
128
|
+
);
|
|
129
|
+
const fragment = prompt ? createSlackAgentHistoryFragment({
|
|
130
|
+
history,
|
|
131
|
+
loadedFragment: loaded.fragment,
|
|
132
|
+
prompt,
|
|
133
|
+
unavailableReasons
|
|
134
|
+
}) : void 0;
|
|
135
|
+
logger?.debug?.("Slack context history loaded", {
|
|
136
|
+
channelId: request.slackActivity.channelId,
|
|
137
|
+
included: Boolean(prompt),
|
|
138
|
+
maxCharacters: history.maxCharacters,
|
|
139
|
+
maxMessages: history.limit,
|
|
140
|
+
originChannelId,
|
|
141
|
+
originChannelMessageCount: loaded.originChannelMessageCount,
|
|
142
|
+
originChannelUnavailableReason: unavailableReasons.originChannelUnavailableReason,
|
|
143
|
+
promptChars: prompt?.length ?? 0,
|
|
144
|
+
threadMessageCount: loaded.threadMessageCount,
|
|
145
|
+
threadTs,
|
|
146
|
+
threadUnavailableReason: unavailableReasons.threadUnavailableReason,
|
|
147
|
+
topLevelChannelMessageCount: loaded.topLevelChannelMessageCount,
|
|
148
|
+
topLevelChannelUnavailableReason: unavailableReasons.topLevelChannelUnavailableReason
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
...fragment ? { fragment } : {},
|
|
152
|
+
...prompt ? { prompt } : {},
|
|
153
|
+
originChannelMessageCount: loaded.originChannelMessageCount,
|
|
154
|
+
originChannelVisibilityOmittedMessageCount: loaded.originChannelVisibilityOmittedMessageCount,
|
|
155
|
+
threadMessageCount: loaded.threadMessageCount,
|
|
156
|
+
threadVisibilityOmittedMessageCount: loaded.threadVisibilityOmittedMessageCount,
|
|
157
|
+
topLevelChannelMessageCount: loaded.topLevelChannelMessageCount,
|
|
158
|
+
topLevelChannelVisibilityOmittedMessageCount: loaded.topLevelChannelVisibilityOmittedMessageCount,
|
|
159
|
+
visibilityOmittedMessageCount: loaded.visibilityOmittedMessageCount,
|
|
160
|
+
...unavailableReasons.originChannelUnavailableReason ? {
|
|
161
|
+
originChannelUnavailableReason: unavailableReasons.originChannelUnavailableReason
|
|
162
|
+
} : {},
|
|
163
|
+
...unavailableReasons.threadUnavailableReason ? { threadUnavailableReason: unavailableReasons.threadUnavailableReason } : {},
|
|
164
|
+
...unavailableReasons.topLevelChannelUnavailableReason ? {
|
|
165
|
+
topLevelChannelUnavailableReason: unavailableReasons.topLevelChannelUnavailableReason
|
|
166
|
+
} : {}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function emptySlackAgentTurnHistoryContextResult() {
|
|
170
|
+
return {
|
|
171
|
+
originChannelMessageCount: 0,
|
|
172
|
+
originChannelVisibilityOmittedMessageCount: 0,
|
|
173
|
+
threadMessageCount: 0,
|
|
174
|
+
threadVisibilityOmittedMessageCount: 0,
|
|
175
|
+
topLevelChannelMessageCount: 0,
|
|
176
|
+
topLevelChannelVisibilityOmittedMessageCount: 0,
|
|
177
|
+
visibilityOmittedMessageCount: 0
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
async function enrichSlackAgentHistoryMentions({
|
|
181
|
+
botToken,
|
|
182
|
+
logger,
|
|
183
|
+
profileResolver,
|
|
184
|
+
prompt,
|
|
185
|
+
teamId
|
|
186
|
+
}) {
|
|
187
|
+
if (!prompt || !profileResolver) {
|
|
188
|
+
return prompt;
|
|
189
|
+
}
|
|
190
|
+
const result = await enrichSlackUserMentions(
|
|
191
|
+
{
|
|
192
|
+
text: prompt,
|
|
193
|
+
...botToken ? { botToken } : {},
|
|
194
|
+
...teamId ? { teamId } : {}
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
concurrency: 4,
|
|
198
|
+
formatLabel: ({ mention, profile }) => profile.displayName ? `${mention} (${profile.displayName})` : void 0,
|
|
199
|
+
maxLookups: 20,
|
|
200
|
+
onResolveError: ({ error, teamId: errorTeamId, userId }) => {
|
|
201
|
+
logger?.debug?.("Slack history mention enrichment failed", {
|
|
202
|
+
...errorTeamId ? { teamId: errorTeamId } : {},
|
|
203
|
+
error: formatHistoryError(error),
|
|
204
|
+
userId
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
profileResolver: {
|
|
208
|
+
resolve: async (input) => ({
|
|
209
|
+
userId: input.userId,
|
|
210
|
+
...await profileResolver.resolve({
|
|
211
|
+
botToken: input.botToken,
|
|
212
|
+
teamId: input.teamId,
|
|
213
|
+
userId: input.userId
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
return result.text;
|
|
220
|
+
}
|
|
221
|
+
function createSlackAgentHistoryAuthorResolver({
|
|
222
|
+
botToken,
|
|
223
|
+
logger,
|
|
224
|
+
profileResolver,
|
|
225
|
+
teamId
|
|
226
|
+
}) {
|
|
227
|
+
return async (message, context) => {
|
|
228
|
+
if (message.role === "assistant") {
|
|
229
|
+
return "Assistant";
|
|
230
|
+
}
|
|
231
|
+
if (message.role === "bot") {
|
|
232
|
+
return "Bot";
|
|
233
|
+
}
|
|
234
|
+
if (!message.userId) {
|
|
235
|
+
return void 0;
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const profile = await profileResolver.resolve({
|
|
239
|
+
botToken,
|
|
240
|
+
teamId: context.teamId ?? teamId,
|
|
241
|
+
userId: message.userId
|
|
242
|
+
});
|
|
243
|
+
return formatSlackHistoryUserAuthor(message.userId, profile.displayName);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
logger?.debug?.("Slack history profile lookup failed", {
|
|
246
|
+
channelId: context.channelId,
|
|
247
|
+
error: formatHistoryError(error),
|
|
248
|
+
source: context.source,
|
|
249
|
+
teamId: context.teamId ?? teamId,
|
|
250
|
+
userId: message.userId
|
|
251
|
+
});
|
|
252
|
+
return formatSlackHistoryUserAuthor(message.userId);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function formatSlackHistoryUserAuthor(userId, displayName) {
|
|
257
|
+
return displayName ? `${displayName} (${userId})` : `Slack user ${userId}`;
|
|
258
|
+
}
|
|
259
|
+
function createSlackAgentHistoryFragment({
|
|
260
|
+
history,
|
|
261
|
+
loadedFragment,
|
|
262
|
+
prompt,
|
|
263
|
+
unavailableReasons
|
|
264
|
+
}) {
|
|
265
|
+
return {
|
|
266
|
+
...loadedFragment,
|
|
267
|
+
budgetBehavior: loadedFragment?.budgetBehavior ?? "droppable",
|
|
268
|
+
content: prompt,
|
|
269
|
+
kind: loadedFragment?.kind ?? "slack-context",
|
|
270
|
+
lifetime: loadedFragment?.lifetime ?? "turn",
|
|
271
|
+
maxChars: loadedFragment?.maxChars ?? history.maxCharacters,
|
|
272
|
+
metadata: {
|
|
273
|
+
...loadedFragment?.metadata,
|
|
274
|
+
...unavailableReasons.originChannelUnavailableReason ? {
|
|
275
|
+
originChannelUnavailableReason: unavailableReasons.originChannelUnavailableReason
|
|
276
|
+
} : {},
|
|
277
|
+
...unavailableReasons.threadUnavailableReason ? {
|
|
278
|
+
threadUnavailableReason: unavailableReasons.threadUnavailableReason
|
|
279
|
+
} : {},
|
|
280
|
+
...unavailableReasons.topLevelChannelUnavailableReason ? {
|
|
281
|
+
topLevelChannelUnavailableReason: unavailableReasons.topLevelChannelUnavailableReason
|
|
282
|
+
} : {}
|
|
283
|
+
},
|
|
284
|
+
placement: loadedFragment?.placement ?? "after-latest-user",
|
|
285
|
+
role: loadedFragment?.role ?? "user",
|
|
286
|
+
source: loadedFragment?.source ?? "slack",
|
|
287
|
+
title: loadedFragment?.title ?? "Slack Conversation History"
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function logSlackAgentHistoryUnavailable(logger, issue) {
|
|
291
|
+
const fields = {
|
|
292
|
+
channelId: issue.channelId,
|
|
293
|
+
error: issue.message,
|
|
294
|
+
expected: issue.expected,
|
|
295
|
+
...normalizedHistoryHint(issue) ? { hint: normalizedHistoryHint(issue) } : {},
|
|
296
|
+
method: issue.method,
|
|
297
|
+
slackError: normalizedHistoryErrorCode(issue) ?? "unknown",
|
|
298
|
+
source: issue.source
|
|
299
|
+
};
|
|
300
|
+
if (issue.expected) {
|
|
301
|
+
logger?.info?.("Slack context history unavailable", fields);
|
|
302
|
+
} else {
|
|
303
|
+
logger?.warn?.("Slack context history unavailable", fields);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function formatSlackAgentHistoryUnavailableNote(issue) {
|
|
307
|
+
const reason = normalizedHistoryErrorCode(issue) ?? "unknown_error";
|
|
308
|
+
const hint = normalizedHistoryHint(issue);
|
|
309
|
+
return [
|
|
310
|
+
`${historyScopeLabel(issue)} context is unavailable to the app for this turn (${reason}).`,
|
|
311
|
+
`If the user asks about that Slack context, say that you cannot read it yet and explain the needed access.${hint ? ` ${normalizeHistoryHint(hint)}` : ""}`
|
|
312
|
+
].join(" ");
|
|
313
|
+
}
|
|
314
|
+
function historyScopeLabel(issue) {
|
|
315
|
+
switch (issue.source) {
|
|
316
|
+
case "thread":
|
|
317
|
+
return "current assistant thread";
|
|
318
|
+
case "channel":
|
|
319
|
+
return `current Slack channel ${issue.channelId}`;
|
|
320
|
+
case "origin-channel":
|
|
321
|
+
return `origin Slack channel ${issue.channelId}`;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function normalizeHistoryHint(hint) {
|
|
325
|
+
return hint.endsWith(".") ? hint : `${hint}.`;
|
|
326
|
+
}
|
|
327
|
+
function summarizeUnavailable(unavailable) {
|
|
328
|
+
return {
|
|
329
|
+
originChannelUnavailableReason: unavailableReason(
|
|
330
|
+
unavailable,
|
|
331
|
+
"origin-channel"
|
|
332
|
+
),
|
|
333
|
+
threadUnavailableReason: unavailableReason(unavailable, "thread"),
|
|
334
|
+
topLevelChannelUnavailableReason: unavailableReason(unavailable, "channel")
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function unavailableReason(unavailable, source) {
|
|
338
|
+
const issue = unavailable.find((entry) => entry.source === source);
|
|
339
|
+
return issue ? normalizedHistoryErrorCode(issue) ?? issue.message : void 0;
|
|
340
|
+
}
|
|
341
|
+
function normalizedHistoryErrorCode(issue) {
|
|
342
|
+
if (issue.errorCode) {
|
|
343
|
+
return issue.errorCode;
|
|
344
|
+
}
|
|
345
|
+
const failedMatch = issue.message.match(/failed:\s*([a-z_]+)/i);
|
|
346
|
+
if (failedMatch?.[1]) {
|
|
347
|
+
return failedMatch[1];
|
|
348
|
+
}
|
|
349
|
+
const apiMatch = issue.message.match(/API error occurred:\s*([a-z_]+)/i);
|
|
350
|
+
return apiMatch?.[1];
|
|
351
|
+
}
|
|
352
|
+
function normalizedHistoryHint(issue) {
|
|
353
|
+
if (issue.hint) {
|
|
354
|
+
return issue.hint;
|
|
355
|
+
}
|
|
356
|
+
switch (normalizedHistoryErrorCode(issue)) {
|
|
357
|
+
case "not_in_channel":
|
|
358
|
+
return "Invite the app to the channel, then retry.";
|
|
359
|
+
case "channel_not_found":
|
|
360
|
+
return "Slack did not expose a readable channel for this bot token.";
|
|
361
|
+
case "no_permission":
|
|
362
|
+
case "access_denied":
|
|
363
|
+
return "The bot token cannot read this conversation.";
|
|
364
|
+
case "missing_scope":
|
|
365
|
+
return "Add the matching Slack history scope and reinstall the app.";
|
|
366
|
+
default:
|
|
367
|
+
return void 0;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function resolveSlackHistoryClient(request) {
|
|
371
|
+
if (request.client) {
|
|
372
|
+
return request.client;
|
|
373
|
+
}
|
|
374
|
+
return request.auth?.botToken ? new WebClient(request.auth.botToken) : void 0;
|
|
375
|
+
}
|
|
376
|
+
function resolveOriginChannelId(request) {
|
|
377
|
+
return request.threadContext?.channel_id && request.threadContext.channel_id !== request.slackActivity.channelId ? request.threadContext.channel_id : void 0;
|
|
378
|
+
}
|
|
379
|
+
function truncateHistoryPrompt(value, history) {
|
|
380
|
+
const trimmed = value.trim();
|
|
381
|
+
if (!trimmed) {
|
|
382
|
+
return void 0;
|
|
383
|
+
}
|
|
384
|
+
if (trimmed.length <= history.maxCharacters) {
|
|
385
|
+
return trimmed;
|
|
386
|
+
}
|
|
387
|
+
return trimmed.slice(0, history.maxCharacters).trimEnd();
|
|
388
|
+
}
|
|
389
|
+
function formatHistoryError(error) {
|
|
390
|
+
return error instanceof Error ? error.stack ?? error.message : String(error);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export {
|
|
394
|
+
loadSlackAgentTurnHistoryContext,
|
|
395
|
+
emptySlackAgentTurnHistoryContextResult
|
|
396
|
+
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSlackChannelAdapter
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
bolt_exports
|
|
6
|
-
} from "./chunk-ANIZ5NT4.js";
|
|
3
|
+
} from "./chunk-FQWFB54C.js";
|
|
7
4
|
|
|
8
5
|
// src/express.ts
|
|
6
|
+
import {
|
|
7
|
+
createSlackBoltApp
|
|
8
|
+
} from "@cuylabs/channel-slack/transports/http";
|
|
9
9
|
async function mountSlackAgent(source, options = {}) {
|
|
10
10
|
const {
|
|
11
11
|
botToken: providedToken,
|
|
@@ -27,7 +27,7 @@ async function mountSlackAgent(source, options = {}) {
|
|
|
27
27
|
app: expressApp,
|
|
28
28
|
authMode,
|
|
29
29
|
routePath
|
|
30
|
-
} = await
|
|
30
|
+
} = await createSlackBoltApp({
|
|
31
31
|
signingSecret,
|
|
32
32
|
path,
|
|
33
33
|
botToken: providedToken,
|