@cuylabs/channel-slack 0.10.0 → 0.11.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 +15 -2
- package/dist/adapter/index.d.ts +53 -0
- package/dist/adapter/index.js +13 -0
- package/dist/app-surface.d.ts +86 -0
- package/dist/app-surface.js +15 -0
- package/dist/app.d.ts +58 -0
- package/dist/app.js +86 -0
- package/dist/artifacts/index.d.ts +57 -3
- package/dist/artifacts/index.js +88 -0
- package/dist/assistant/index.d.ts +18 -53
- package/dist/assistant/index.js +15 -184
- package/dist/bolt-app-BM0tiL7c.d.ts +49 -0
- package/dist/{chunk-TWJGVDA2.js → chunk-37RN2YUI.js} +88 -1
- package/dist/chunk-LFQCINHI.js +187 -0
- package/dist/chunk-Q6YX7HHK.js +1062 -0
- package/dist/chunk-RHOIVQLD.js +127 -0
- package/dist/chunk-RTDLIYEE.js +446 -0
- package/dist/core.d.ts +5 -201
- package/dist/core.js +10 -12
- package/dist/feedback/index.d.ts +2 -2
- package/dist/feedback/index.js +5 -120
- package/dist/formatting-C-kwQseI.d.ts +25 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +10 -12
- package/dist/options-B0xQCaez.d.ts +221 -0
- package/dist/options-DQacQDmD.d.ts +368 -0
- package/dist/runtime/index.d.ts +6 -220
- package/dist/socket.d.ts +142 -0
- package/dist/socket.js +77 -0
- package/dist/transports/index.d.ts +2 -1
- package/dist/transports/socket/index.d.ts +4 -49
- package/dist/turn-BGAXddH_.d.ts +178 -0
- package/dist/types-wLZzyI9r.d.ts +375 -0
- package/docs/reference/exports.md +5 -1
- package/package.json +23 -3
- package/dist/chunk-ISOMBQXE.js +0 -89
package/dist/assistant/index.js
CHANGED
|
@@ -1,188 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
async get(args) {
|
|
15
|
-
args.logger?.debug?.("[channel-slack] thread context store get called");
|
|
16
|
-
const { channelId, threadTs } = extractThreadInfo(args.payload);
|
|
17
|
-
const cacheKey = contextCacheKey(channelId, threadTs);
|
|
18
|
-
const cached = cache.get(cacheKey);
|
|
19
|
-
if (cached) return cached;
|
|
20
|
-
const messages = await readThreadMessages(args, channelId, threadTs);
|
|
21
|
-
const botUserId = args.context?.botUserId;
|
|
22
|
-
const metadataMessage = findContextMessage(messages, botUserId);
|
|
23
|
-
const context = toThreadContext(metadataMessage?.metadata?.event_payload);
|
|
24
|
-
cache.set(cacheKey, context);
|
|
25
|
-
return context;
|
|
26
|
-
},
|
|
27
|
-
async save(args) {
|
|
28
|
-
args.logger?.debug?.("[channel-slack] thread context store save called");
|
|
29
|
-
const { channelId, threadTs, context } = extractThreadInfo(args.payload);
|
|
30
|
-
const cacheKey = contextCacheKey(channelId, threadTs);
|
|
31
|
-
cache.set(cacheKey, context);
|
|
32
|
-
const messages = await readThreadMessages(args, channelId, threadTs);
|
|
33
|
-
const initialMessage = findInitialBotMessage(
|
|
34
|
-
messages,
|
|
35
|
-
args.context?.botUserId
|
|
36
|
-
);
|
|
37
|
-
if (!initialMessage?.ts) return;
|
|
38
|
-
await args.client?.chat?.update?.({
|
|
39
|
-
channel: channelId,
|
|
40
|
-
ts: initialMessage.ts,
|
|
41
|
-
text: initialMessage.text ?? "",
|
|
42
|
-
blocks: Array.isArray(initialMessage.blocks) ? initialMessage.blocks : [],
|
|
43
|
-
metadata: {
|
|
44
|
-
event_type: ASSISTANT_THREAD_CONTEXT_EVENT_TYPE,
|
|
45
|
-
event_payload: context
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
async function readThreadMessages(args, channelId, threadTs) {
|
|
52
|
-
const replies = args.client?.conversations?.replies;
|
|
53
|
-
if (typeof replies !== "function") return [];
|
|
54
|
-
const result = await replies({
|
|
55
|
-
channel: channelId,
|
|
56
|
-
ts: threadTs,
|
|
57
|
-
oldest: threadTs,
|
|
58
|
-
include_all_metadata: true,
|
|
59
|
-
limit: 4
|
|
60
|
-
});
|
|
61
|
-
return Array.isArray(result.messages) ? result.messages.filter(isSlackMessageWithMetadata) : [];
|
|
62
|
-
}
|
|
63
|
-
function findContextMessage(messages, botUserId) {
|
|
64
|
-
return messages.find(
|
|
65
|
-
(message) => isBotAuthoredMessage(message, botUserId) && message.metadata?.event_type === ASSISTANT_THREAD_CONTEXT_EVENT_TYPE
|
|
66
|
-
) ?? messages.find(
|
|
67
|
-
(message) => isBotAuthoredMessage(message, botUserId) && message.metadata?.event_payload
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
function findInitialBotMessage(messages, botUserId) {
|
|
71
|
-
return messages.find((message) => isBotAuthoredMessage(message, botUserId));
|
|
72
|
-
}
|
|
73
|
-
function isBotAuthoredMessage(message, botUserId) {
|
|
74
|
-
if (message.subtype) return false;
|
|
75
|
-
return botUserId ? message.user === botUserId : Boolean(message.user);
|
|
76
|
-
}
|
|
77
|
-
function extractThreadInfo(payload) {
|
|
78
|
-
const channelId = extractChannelId(payload);
|
|
79
|
-
const threadTs = extractThreadTs(payload);
|
|
80
|
-
if (!channelId || !threadTs) {
|
|
81
|
-
const missing = [];
|
|
82
|
-
if (!channelId) missing.push("channel_id");
|
|
83
|
-
if (!threadTs) missing.push("thread_ts");
|
|
84
|
-
throw new Error(
|
|
85
|
-
`Assistant payload is missing required properties: ${missing.join(", ")}`
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
channelId,
|
|
90
|
-
threadTs,
|
|
91
|
-
context: extractThreadContext(payload)
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function extractThreadContext(payload) {
|
|
95
|
-
if (!isRecord(payload)) return {};
|
|
96
|
-
const assistantThread = payload.assistant_thread;
|
|
97
|
-
if (!isRecord(assistantThread) || !isRecord(assistantThread.context)) {
|
|
98
|
-
return {};
|
|
99
|
-
}
|
|
100
|
-
return toThreadContext(assistantThread.context);
|
|
101
|
-
}
|
|
102
|
-
function extractThreadTs(payload) {
|
|
103
|
-
if (!isRecord(payload)) return void 0;
|
|
104
|
-
if (isNonEmptyString(payload.thread_ts)) return payload.thread_ts;
|
|
105
|
-
if (isRecord(payload.assistant_thread) && isNonEmptyString(payload.assistant_thread.thread_ts)) {
|
|
106
|
-
return payload.assistant_thread.thread_ts;
|
|
107
|
-
}
|
|
108
|
-
if (isRecord(payload.message) && isNonEmptyString(payload.message.thread_ts)) {
|
|
109
|
-
return payload.message.thread_ts;
|
|
110
|
-
}
|
|
111
|
-
if (isRecord(payload.previous_message) && isNonEmptyString(payload.previous_message.thread_ts)) {
|
|
112
|
-
return payload.previous_message.thread_ts;
|
|
113
|
-
}
|
|
114
|
-
return void 0;
|
|
115
|
-
}
|
|
116
|
-
function extractChannelId(payload) {
|
|
117
|
-
if (!isRecord(payload)) return void 0;
|
|
118
|
-
if (isNonEmptyString(payload.channel)) return payload.channel;
|
|
119
|
-
if (isRecord(payload.channel) && isNonEmptyString(payload.channel.id)) {
|
|
120
|
-
return payload.channel.id;
|
|
121
|
-
}
|
|
122
|
-
if (isNonEmptyString(payload.channel_id)) return payload.channel_id;
|
|
123
|
-
if (isRecord(payload.item) && isNonEmptyString(payload.item.channel)) {
|
|
124
|
-
return payload.item.channel;
|
|
125
|
-
}
|
|
126
|
-
if (isRecord(payload.assistant_thread) && isNonEmptyString(payload.assistant_thread.channel_id)) {
|
|
127
|
-
return payload.assistant_thread.channel_id;
|
|
128
|
-
}
|
|
129
|
-
return void 0;
|
|
130
|
-
}
|
|
131
|
-
function toThreadContext(value) {
|
|
132
|
-
if (!isRecord(value)) return {};
|
|
133
|
-
return {
|
|
134
|
-
...isNonEmptyString(value.channel_id) ? { channel_id: value.channel_id } : {},
|
|
135
|
-
...isNonEmptyString(value.team_id) ? { team_id: value.team_id } : {},
|
|
136
|
-
..."enterprise_id" in value ? {
|
|
137
|
-
enterprise_id: typeof value.enterprise_id === "string" ? value.enterprise_id : null
|
|
138
|
-
} : {}
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
function isSlackMessageWithMetadata(value) {
|
|
142
|
-
return isRecord(value);
|
|
143
|
-
}
|
|
144
|
-
function contextCacheKey(channelId, threadTs) {
|
|
145
|
-
return `${channelId}:${threadTs}`;
|
|
146
|
-
}
|
|
147
|
-
function isNonEmptyString(value) {
|
|
148
|
-
return typeof value === "string" && value.length > 0;
|
|
149
|
-
}
|
|
150
|
-
function isRecord(value) {
|
|
151
|
-
return typeof value === "object" && value !== null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// src/assistant/message-parser.ts
|
|
155
|
-
function parseSlackMessageActivityFromMessageEvent(message) {
|
|
156
|
-
if (!message || typeof message !== "object") return void 0;
|
|
157
|
-
const m = message;
|
|
158
|
-
if (m.subtype && m.subtype.length > 0) return void 0;
|
|
159
|
-
if (typeof m.text !== "string" || m.text.length === 0) return void 0;
|
|
160
|
-
if (typeof m.thread_ts !== "string" || m.thread_ts.length === 0) {
|
|
161
|
-
return void 0;
|
|
162
|
-
}
|
|
163
|
-
const channel = typeof m.channel === "string" ? m.channel : void 0;
|
|
164
|
-
if (!channel) return void 0;
|
|
165
|
-
const threadTs = m.thread_ts;
|
|
166
|
-
const messageTs = m.ts;
|
|
167
|
-
const isThread = threadTs !== messageTs;
|
|
168
|
-
const channelType = resolveSlackChannelType(channel, threadTs, isThread);
|
|
169
|
-
const rawText = m.text;
|
|
170
|
-
const text = channelType === "dm" ? rawText.trim() : stripLeadingMentions(rawText);
|
|
171
|
-
return {
|
|
172
|
-
channel,
|
|
173
|
-
threadTs,
|
|
174
|
-
channelId: channel,
|
|
175
|
-
channelType,
|
|
176
|
-
userId: m.user ?? "unknown",
|
|
177
|
-
teamId: m.team ?? void 0,
|
|
178
|
-
parentUserId: m.parent_user_id,
|
|
179
|
-
actionToken: extractSlackActionToken(m),
|
|
180
|
-
text,
|
|
181
|
-
isMention: false,
|
|
182
|
-
...messageTs ? { messageTs } : {}
|
|
183
|
-
};
|
|
184
|
-
}
|
|
2
|
+
createSlackAssistantBridge,
|
|
3
|
+
createSlackAssistantThreadContextStore,
|
|
4
|
+
parseSlackMessageActivityFromMessageEvent,
|
|
5
|
+
resolveAssistantSessionId
|
|
6
|
+
} from "../chunk-Q6YX7HHK.js";
|
|
7
|
+
import "../chunk-RHOIVQLD.js";
|
|
8
|
+
import "../chunk-IRFKUPJN.js";
|
|
9
|
+
import "../chunk-IAQXQESO.js";
|
|
10
|
+
import "../chunk-37RN2YUI.js";
|
|
11
|
+
import "../chunk-3KP3CBCC.js";
|
|
12
|
+
import "../chunk-FPCE5V5Y.js";
|
|
13
|
+
import "../chunk-6WHFQUYQ.js";
|
|
185
14
|
export {
|
|
15
|
+
createSlackAssistantBridge,
|
|
186
16
|
createSlackAssistantThreadContextStore,
|
|
187
|
-
parseSlackMessageActivityFromMessageEvent
|
|
17
|
+
parseSlackMessageActivityFromMessageEvent,
|
|
18
|
+
resolveAssistantSessionId
|
|
188
19
|
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AppOptions, SocketModeReceiverOptions, App } from '@slack/bolt';
|
|
2
|
+
import { e as SlackDirectAuthOptions, d as SlackDirectAuthMode } from './types-B9NfCVrk.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `createSlackSocketBoltApp` — builds a Socket Mode Bolt `App` from the
|
|
6
|
+
* package's structured auth options. Mirrors `createSlackBoltApp` without
|
|
7
|
+
* owning any assistant/app surface mounting.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type ControlledSocketBoltAppOption = "receiver" | "socketMode" | "appToken" | "token" | "authorize" | "botId" | "botUserId" | "clientId" | "clientSecret" | "stateSecret" | "redirectUri" | "installationStore" | "scopes" | "installerOptions";
|
|
11
|
+
type ControlledSocketModeReceiverOption = "appToken" | "clientId" | "clientSecret" | "stateSecret" | "redirectUri" | "installationStore" | "scopes" | "installerOptions" | "logger" | "logLevel";
|
|
12
|
+
interface CreateSlackSocketBoltAppOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Slack app-level token (`xapp-...`) with `connections:write` scope.
|
|
15
|
+
* @default process.env.SLACK_APP_TOKEN
|
|
16
|
+
*/
|
|
17
|
+
appToken?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Convenience override for single-workspace mode.
|
|
20
|
+
* @default process.env.SLACK_BOT_TOKEN
|
|
21
|
+
*/
|
|
22
|
+
botToken?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Direct-mode auth configuration.
|
|
25
|
+
*
|
|
26
|
+
* - omit for single-workspace token mode
|
|
27
|
+
* - use `{ mode: "oauth", ... }` for Bolt-managed installs
|
|
28
|
+
* - use `{ mode: "authorize", authorize }` for external token resolution
|
|
29
|
+
*/
|
|
30
|
+
auth?: SlackDirectAuthOptions;
|
|
31
|
+
/**
|
|
32
|
+
* Additional Bolt `App` options passed after this package sets Socket Mode
|
|
33
|
+
* and auth. Use for logger/clientOptions and non-auth native Bolt config.
|
|
34
|
+
*/
|
|
35
|
+
boltAppOptions?: Partial<Omit<AppOptions, ControlledSocketBoltAppOption>>;
|
|
36
|
+
/**
|
|
37
|
+
* Additional native Socket Mode receiver options. Use this for websocket
|
|
38
|
+
* health tuning such as ping timeouts, reconnect behavior, and receiver
|
|
39
|
+
* error handling. Auth/OAuth fields stay controlled by this helper.
|
|
40
|
+
*/
|
|
41
|
+
socketModeReceiverOptions?: Partial<Omit<SocketModeReceiverOptions, ControlledSocketModeReceiverOption>>;
|
|
42
|
+
}
|
|
43
|
+
interface CreateSlackSocketBoltAppResult {
|
|
44
|
+
boltApp: App;
|
|
45
|
+
authMode: SlackDirectAuthMode;
|
|
46
|
+
}
|
|
47
|
+
declare function createSlackSocketBoltApp(options?: CreateSlackSocketBoltAppOptions): Promise<CreateSlackSocketBoltAppResult>;
|
|
48
|
+
|
|
49
|
+
export { type CreateSlackSocketBoltAppOptions as C, type CreateSlackSocketBoltAppResult as a, createSlackSocketBoltApp as c };
|
|
@@ -99,10 +99,97 @@ function extractSlackAuthContext(context, eventUserId) {
|
|
|
99
99
|
return auth;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
// src/shared/turn-context.ts
|
|
103
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
104
|
+
var store = new AsyncLocalStorage();
|
|
105
|
+
function currentSlackTurnContext() {
|
|
106
|
+
return store.getStore();
|
|
107
|
+
}
|
|
108
|
+
function runWithSlackTurnContext(value, fn) {
|
|
109
|
+
return Promise.resolve(store.run({ ...value }, fn));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/shared/follow-up.ts
|
|
113
|
+
function formatSlackAttributedFollowUp(message) {
|
|
114
|
+
const slack = currentSlackTurnContext();
|
|
115
|
+
if (!slack) {
|
|
116
|
+
return message;
|
|
117
|
+
}
|
|
118
|
+
const sender = formatSlackSender(slack);
|
|
119
|
+
const conversation = formatSlackConversation(slack);
|
|
120
|
+
if (!sender && !conversation) {
|
|
121
|
+
return message;
|
|
122
|
+
}
|
|
123
|
+
const lines = ["Additional Slack follow-up for the running request."];
|
|
124
|
+
if (sender) {
|
|
125
|
+
lines.push(`Sender: ${sender}`);
|
|
126
|
+
}
|
|
127
|
+
if (conversation) {
|
|
128
|
+
lines.push(`Conversation: ${conversation}`);
|
|
129
|
+
}
|
|
130
|
+
lines.push("Message:", message);
|
|
131
|
+
return lines.join("\n");
|
|
132
|
+
}
|
|
133
|
+
function formatSlackSender(slack) {
|
|
134
|
+
const userId = slack.slackActivity.userId || slack.user.userId;
|
|
135
|
+
const displayName = readPreparedSlackDisplayName(slack.context) ?? slack.user.userName;
|
|
136
|
+
if (displayName && userId) {
|
|
137
|
+
return `${displayName} (${userId})`;
|
|
138
|
+
}
|
|
139
|
+
return displayName ?? userId ?? "unknown Slack user";
|
|
140
|
+
}
|
|
141
|
+
function formatSlackConversation(slack) {
|
|
142
|
+
const activity = slack.slackActivity;
|
|
143
|
+
const surface = formatSlackSurface(activity.channelType);
|
|
144
|
+
const parts = [
|
|
145
|
+
activity.teamId ? `team ${activity.teamId}` : void 0,
|
|
146
|
+
activity.channelId ? `channel ${activity.channelId}` : void 0,
|
|
147
|
+
activity.threadTs ? `thread ${activity.threadTs}` : activity.messageTs ? `message ${activity.messageTs}` : void 0
|
|
148
|
+
].filter((part) => Boolean(part));
|
|
149
|
+
return parts.length > 0 ? `${surface} (${parts.join(", ")})` : surface;
|
|
150
|
+
}
|
|
151
|
+
function formatSlackSurface(channelType) {
|
|
152
|
+
switch (channelType) {
|
|
153
|
+
case "dm":
|
|
154
|
+
return "private Slack DM";
|
|
155
|
+
case "thread":
|
|
156
|
+
return "shared Slack thread";
|
|
157
|
+
case "channel":
|
|
158
|
+
return "shared Slack channel";
|
|
159
|
+
case "group":
|
|
160
|
+
return "shared Slack group conversation";
|
|
161
|
+
default:
|
|
162
|
+
return "Slack conversation";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function readPreparedSlackDisplayName(context) {
|
|
166
|
+
const slack = context?.slack;
|
|
167
|
+
if (!slack || typeof slack !== "object") {
|
|
168
|
+
return void 0;
|
|
169
|
+
}
|
|
170
|
+
const value = slack.userDisplayName;
|
|
171
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/shared/session.ts
|
|
175
|
+
function resolveThreadAwareSlackSessionId(info) {
|
|
176
|
+
if (info.channelType === "dm") {
|
|
177
|
+
return info.channelId;
|
|
178
|
+
}
|
|
179
|
+
if (info.threadTs) {
|
|
180
|
+
return `${info.channelId}:${info.threadTs}`;
|
|
181
|
+
}
|
|
182
|
+
return `${info.channelId}:${info.messageTs ?? info.channelId}`;
|
|
183
|
+
}
|
|
184
|
+
|
|
102
185
|
export {
|
|
103
186
|
extractSlackActionToken,
|
|
104
187
|
parseSlackMessageActivity,
|
|
105
188
|
parseSlackMentionActivity,
|
|
106
189
|
extractSlackUserIdentity,
|
|
107
|
-
extractSlackAuthContext
|
|
190
|
+
extractSlackAuthContext,
|
|
191
|
+
currentSlackTurnContext,
|
|
192
|
+
runWithSlackTurnContext,
|
|
193
|
+
formatSlackAttributedFollowUp,
|
|
194
|
+
resolveThreadAwareSlackSessionId
|
|
108
195
|
};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSlackAssistantBridge
|
|
3
|
+
} from "./chunk-Q6YX7HHK.js";
|
|
4
|
+
import {
|
|
5
|
+
createSlackFeedbackBlock
|
|
6
|
+
} from "./chunk-RHOIVQLD.js";
|
|
7
|
+
import {
|
|
8
|
+
createSlackChannelAdapter
|
|
9
|
+
} from "./chunk-RTDLIYEE.js";
|
|
10
|
+
|
|
11
|
+
// src/app-surface.ts
|
|
12
|
+
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
13
|
+
var DEFAULT_STREAM_ERROR_MESSAGE = "I ran into an error while preparing this answer. Please try again.";
|
|
14
|
+
function installSlackAppSurface(boltApp, options) {
|
|
15
|
+
const {
|
|
16
|
+
respondToMentions,
|
|
17
|
+
respondToMessages,
|
|
18
|
+
respondToChannelMessages,
|
|
19
|
+
resolveMessage,
|
|
20
|
+
feedback: feedbackOption,
|
|
21
|
+
onFeedback,
|
|
22
|
+
timeoutMs,
|
|
23
|
+
logger,
|
|
24
|
+
source,
|
|
25
|
+
prepareTurn,
|
|
26
|
+
resolveSession,
|
|
27
|
+
interactive,
|
|
28
|
+
viewWorkflows,
|
|
29
|
+
...assistantBridgeOptions
|
|
30
|
+
} = options;
|
|
31
|
+
const feedbackConfig = feedbackOption === false ? void 0 : feedbackOption ?? (onFeedback ? { onFeedback } : {});
|
|
32
|
+
const assistantFeedback = feedbackOption === false ? false : feedbackConfig;
|
|
33
|
+
const feedbackBlock = feedbackConfig ? createSlackFeedbackBlock(feedbackConfig) : void 0;
|
|
34
|
+
const formatStreamError = assistantBridgeOptions.formatStreamError ?? (() => DEFAULT_STREAM_ERROR_MESSAGE);
|
|
35
|
+
const formatting = assistantBridgeOptions.formatChatMarkdown;
|
|
36
|
+
const channelSource = createErrorTranslatingSource(source, formatStreamError);
|
|
37
|
+
const channelFinalArgs = mergeChatStreamFinalArgs(
|
|
38
|
+
assistantBridgeOptions.chatStreamFinalArgs,
|
|
39
|
+
feedbackBlock
|
|
40
|
+
);
|
|
41
|
+
const bridge = createSlackAssistantBridge({
|
|
42
|
+
...assistantBridgeOptions,
|
|
43
|
+
source,
|
|
44
|
+
...assistantFeedback !== void 0 ? { feedback: assistantFeedback } : {},
|
|
45
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {},
|
|
46
|
+
...logger ? { logger } : {},
|
|
47
|
+
...prepareTurn ? { prepareTurn: toAssistantPrepareTurn(prepareTurn) } : {},
|
|
48
|
+
...resolveSession ? { resolveSession: toAssistantResolveSession(resolveSession) } : {},
|
|
49
|
+
...interactive ? { handleInteractiveRequest: interactive.handleInteractiveRequest } : {}
|
|
50
|
+
});
|
|
51
|
+
bridge.install(boltApp);
|
|
52
|
+
const channelAdapter = createSlackChannelAdapter({
|
|
53
|
+
source: channelSource,
|
|
54
|
+
sessionStrategy: assistantBridgeOptions.sessionStrategy,
|
|
55
|
+
...resolveSession ? { resolveSession } : {},
|
|
56
|
+
...prepareTurn ? { prepareTurn } : {},
|
|
57
|
+
...interactive ? { handleInteractiveRequest: interactive.handleInteractiveRequest } : {},
|
|
58
|
+
streamingMode: "chat-stream",
|
|
59
|
+
showReasoning: assistantBridgeOptions.showReasoning,
|
|
60
|
+
showToolUsage: assistantBridgeOptions.showToolUsage,
|
|
61
|
+
showSubagentToolUsage: assistantBridgeOptions.showSubagentToolUsage,
|
|
62
|
+
showSubagentResultInTask: assistantBridgeOptions.showSubagentResultInTask,
|
|
63
|
+
formatToolTitle: assistantBridgeOptions.formatToolTitle,
|
|
64
|
+
formatToolUpdate: assistantBridgeOptions.formatToolUpdate,
|
|
65
|
+
formatToolDetails: assistantBridgeOptions.formatToolDetails,
|
|
66
|
+
formatToolResultOutput: assistantBridgeOptions.formatToolResultOutput,
|
|
67
|
+
formatToolError: assistantBridgeOptions.formatToolError,
|
|
68
|
+
formatReasoningUpdate: assistantBridgeOptions.formatReasoningUpdate,
|
|
69
|
+
chatStreamBufferSize: assistantBridgeOptions.chatStreamBufferSize,
|
|
70
|
+
chatStreamStartArgs: {
|
|
71
|
+
...assistantBridgeOptions.chatStreamStartArgs ?? {},
|
|
72
|
+
task_display_mode: assistantBridgeOptions.taskDisplayMode ?? "timeline"
|
|
73
|
+
},
|
|
74
|
+
publishFinalResponseArtifact: assistantBridgeOptions.publishFinalResponseArtifact,
|
|
75
|
+
finalResponseArtifactMode: assistantBridgeOptions.finalResponseArtifactMode,
|
|
76
|
+
finalResponseArtifactStreamThreshold: assistantBridgeOptions.finalResponseArtifactStreamThreshold,
|
|
77
|
+
formatFinalResponseArtifactContinuationNotice: assistantBridgeOptions.formatFinalResponseArtifactContinuationNotice,
|
|
78
|
+
formatFinalResponseArtifactMessage: assistantBridgeOptions.formatFinalResponseArtifactMessage,
|
|
79
|
+
onFinalResponseArtifactError: assistantBridgeOptions.onFinalResponseArtifactError,
|
|
80
|
+
...typeof assistantBridgeOptions.initialStatus === "object" && assistantBridgeOptions.initialStatus !== null ? { initialStatus: assistantBridgeOptions.initialStatus } : {},
|
|
81
|
+
...formatting?.formatChatMarkdown !== void 0 ? { formatChatMarkdown: formatting.formatChatMarkdown } : {},
|
|
82
|
+
...formatting?.formatMessageText ? { formatMessageText: formatting.formatMessageText } : {},
|
|
83
|
+
timeout: timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
84
|
+
respondToMentions: respondToMentions !== false,
|
|
85
|
+
respondToMessages: respondToMessages !== false,
|
|
86
|
+
respondToChannelMessages: respondToChannelMessages === true,
|
|
87
|
+
...resolveMessage ? { resolveMessage } : {},
|
|
88
|
+
chatStreamFinalArgs: channelFinalArgs,
|
|
89
|
+
...logger ? { logger } : {},
|
|
90
|
+
...logger ? {
|
|
91
|
+
onError: async (error, info) => {
|
|
92
|
+
logger.warn?.("slack channel turn failed", {
|
|
93
|
+
error: formatErrorForLog(error),
|
|
94
|
+
channelId: info.channelId,
|
|
95
|
+
userId: info.userId
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
} : {}
|
|
99
|
+
});
|
|
100
|
+
channelAdapter.mount(boltApp);
|
|
101
|
+
interactive?.install(boltApp);
|
|
102
|
+
viewWorkflows?.install(boltApp);
|
|
103
|
+
return { bridge };
|
|
104
|
+
}
|
|
105
|
+
function mergeChatStreamFinalArgs(base, feedbackBlock) {
|
|
106
|
+
if (!base && !feedbackBlock) {
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
const configuredBlocks = Array.isArray(base?.blocks) ? base.blocks : [];
|
|
110
|
+
const blocks = feedbackBlock ? [...configuredBlocks, feedbackBlock] : configuredBlocks;
|
|
111
|
+
return {
|
|
112
|
+
...base ?? {},
|
|
113
|
+
...blocks.length > 0 ? { blocks } : {}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function toAssistantPrepareTurn(prepareTurn) {
|
|
117
|
+
return (request) => prepareTurn(toAppTurnRequest(request));
|
|
118
|
+
}
|
|
119
|
+
function toAssistantResolveSession(resolveSession) {
|
|
120
|
+
return (request) => resolveSession(toAppTurnRequest(request));
|
|
121
|
+
}
|
|
122
|
+
function toAppTurnRequest(request) {
|
|
123
|
+
const user = {
|
|
124
|
+
userId: request.message.userId,
|
|
125
|
+
channelId: request.message.channelId,
|
|
126
|
+
teamId: request.message.teamId ?? request.auth.teamId,
|
|
127
|
+
threadTs: request.message.threadTs,
|
|
128
|
+
messageTs: request.message.messageTs
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
slackActivity: request.message,
|
|
132
|
+
user,
|
|
133
|
+
sessionId: request.sessionId,
|
|
134
|
+
message: request.message.text,
|
|
135
|
+
auth: request.auth,
|
|
136
|
+
threadContext: request.threadContext,
|
|
137
|
+
assistant: request.assistant,
|
|
138
|
+
client: request.client,
|
|
139
|
+
rawArgs: request.rawArgs
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function createErrorTranslatingSource(source, formatStreamError) {
|
|
143
|
+
return {
|
|
144
|
+
chat(sessionId, message, options) {
|
|
145
|
+
const events = source.chat(sessionId, message, options);
|
|
146
|
+
return translateErrors(events, formatStreamError);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async function* translateErrors(events, formatStreamError) {
|
|
151
|
+
try {
|
|
152
|
+
for await (const event of events) {
|
|
153
|
+
if (event.type === "error") {
|
|
154
|
+
yield {
|
|
155
|
+
type: "text-delta",
|
|
156
|
+
text: `
|
|
157
|
+
|
|
158
|
+
${formatStreamError(toError(event.error))}`
|
|
159
|
+
};
|
|
160
|
+
yield { type: "complete" };
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
yield event;
|
|
164
|
+
}
|
|
165
|
+
} catch (error) {
|
|
166
|
+
yield {
|
|
167
|
+
type: "text-delta",
|
|
168
|
+
text: `
|
|
169
|
+
|
|
170
|
+
${formatStreamError(toError(error))}`
|
|
171
|
+
};
|
|
172
|
+
yield { type: "complete" };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function toError(value) {
|
|
176
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
177
|
+
}
|
|
178
|
+
function formatErrorForLog(error) {
|
|
179
|
+
if (error instanceof Error) {
|
|
180
|
+
return error.stack ?? error.message;
|
|
181
|
+
}
|
|
182
|
+
return String(error);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export {
|
|
186
|
+
installSlackAppSurface
|
|
187
|
+
};
|