@cuylabs/channel-slack-agent-core 0.9.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.
Files changed (52) hide show
  1. package/README.md +13 -10
  2. package/dist/chunk-U6RC4SXN.js +331 -0
  3. package/dist/history/index.d.ts +7 -17
  4. package/dist/index.d.ts +5 -28
  5. package/dist/index.js +14 -58
  6. package/dist/interactive/index.d.ts +86 -7
  7. package/dist/shared/index.d.ts +144 -19
  8. package/dist/shared/index.js +8 -4
  9. package/docs/README.md +0 -1
  10. package/docs/concepts/final-response-artifacts.md +9 -7
  11. package/docs/concepts/tool-task-rendering.md +2 -0
  12. package/docs/reference/exports.md +10 -20
  13. package/package.json +4 -54
  14. package/dist/adapter/index.d.ts +0 -26
  15. package/dist/adapter/index.js +0 -9
  16. package/dist/adapter-vbqtraAr.d.ts +0 -31
  17. package/dist/app-surface.d.ts +0 -82
  18. package/dist/app-surface.js +0 -10
  19. package/dist/app.d.ts +0 -60
  20. package/dist/app.js +0 -11
  21. package/dist/artifacts/index.d.ts +0 -57
  22. package/dist/artifacts/index.js +0 -6
  23. package/dist/assistant/index.d.ts +0 -22
  24. package/dist/assistant/index.js +0 -14
  25. package/dist/chunk-A2PLAVW6.js +0 -75
  26. package/dist/chunk-C7CHMYV6.js +0 -226
  27. package/dist/chunk-D4CSEAIF.js +0 -82
  28. package/dist/chunk-ELR6MQD7.js +0 -12
  29. package/dist/chunk-FJP6ZFUB.js +0 -921
  30. package/dist/chunk-GKZRDNEB.js +0 -187
  31. package/dist/chunk-HHXAXSG6.js +0 -67
  32. package/dist/chunk-JU5R6JZG.js +0 -85
  33. package/dist/chunk-KAEZPS3U.js +0 -77
  34. package/dist/chunk-NNCVHQC4.js +0 -94
  35. package/dist/chunk-VBGQD6JT.js +0 -1008
  36. package/dist/chunk-XA7U3GRN.js +0 -482
  37. package/dist/express-assistant.d.ts +0 -106
  38. package/dist/express-assistant.js +0 -9
  39. package/dist/express.d.ts +0 -103
  40. package/dist/express.js +0 -8
  41. package/dist/feedback/index.d.ts +0 -1
  42. package/dist/feedback/index.js +0 -10
  43. package/dist/options-ByNm2o89.d.ts +0 -323
  44. package/dist/options-CGUfVStV.d.ts +0 -119
  45. package/dist/socket.d.ts +0 -143
  46. package/dist/socket.js +0 -13
  47. package/dist/types-BeGPexio.d.ts +0 -381
  48. package/dist/types-Bz4OYEAV.d.ts +0 -87
  49. package/dist/types-CiwGU6zC.d.ts +0 -56
  50. package/dist/views/index.d.ts +0 -8
  51. package/dist/views/index.js +0 -10
  52. package/docs/concepts/view-workflows.md +0 -52
@@ -1,921 +0,0 @@
1
- import {
2
- SLACK_FEEDBACK_ACTION_ID,
3
- createSlackFeedbackBlock,
4
- registerSlackFeedbackAction
5
- } from "./chunk-ELR6MQD7.js";
6
- import {
7
- UnsupportedSlackInteractiveRequestError,
8
- bridgeAgentEventsToSlack,
9
- resolveSlackEventBridgeOptions
10
- } from "./chunk-VBGQD6JT.js";
11
-
12
- // src/assistant/bridge.ts
13
- import { Assistant } from "@slack/bolt";
14
- import { resolveSlackTurnCancelActionId as resolveSlackTurnCancelActionId2 } from "@cuylabs/channel-slack/turn-controls";
15
-
16
- // src/assistant/options.ts
17
- var DEFAULT_INITIAL_REPLY = "Hi, how can I help?";
18
- var DEFAULT_INITIAL_STATUS = {
19
- status: "Thinking..."
20
- };
21
- var DEFAULT_STREAM_ERROR_MESSAGE = "I ran into an error while preparing this answer. Please try again.";
22
-
23
- // src/assistant/helpers.ts
24
- function toError(value) {
25
- return value instanceof Error ? value : new Error(String(value));
26
- }
27
- function formatErrorForLog(error) {
28
- if (error instanceof Error) {
29
- return error.stack ?? error.message;
30
- }
31
- return String(error);
32
- }
33
- async function safeGetThreadContext(getThreadContext, logger) {
34
- try {
35
- const value = await getThreadContext();
36
- if (value && typeof value === "object") {
37
- return value;
38
- }
39
- } catch (error) {
40
- logger.warn?.("getThreadContext failed", {
41
- error: formatErrorForLog(error)
42
- });
43
- }
44
- return {};
45
- }
46
- async function resolveInitialReply(resolver, context) {
47
- if (resolver === void 0) {
48
- return DEFAULT_INITIAL_REPLY;
49
- }
50
- const value = await resolver(context);
51
- if (value === false) {
52
- return void 0;
53
- }
54
- return value ?? void 0;
55
- }
56
- async function resolveInitialStatus(resolver, request) {
57
- if (resolver === void 0) {
58
- return DEFAULT_INITIAL_STATUS;
59
- }
60
- if (typeof resolver === "function") {
61
- return await resolver(request) ?? void 0;
62
- }
63
- return resolver;
64
- }
65
- async function resolveStatusUpdate(resolver, label, event, rawArgs) {
66
- if (!resolver) {
67
- return label ? { status: label } : { status: "" };
68
- }
69
- if (!event) {
70
- return void 0;
71
- }
72
- return await resolver({ event, rawArgs }) ?? void 0;
73
- }
74
- function resolveFeedbackSessionId(context, strategy) {
75
- const threadTs = context.threadTs ?? context.messageTs;
76
- switch (strategy) {
77
- case "channel-id":
78
- return context.channelId;
79
- case "user-per-thread":
80
- return `${context.channelId}:${threadTs}:${context.userId}`;
81
- default:
82
- return `${context.channelId}:${threadTs}`;
83
- }
84
- }
85
-
86
- // src/assistant/lifecycle/thread-started.ts
87
- function createThreadStartedHandler(options) {
88
- return async (args) => {
89
- const {
90
- event,
91
- context,
92
- client,
93
- logger,
94
- say,
95
- setSuggestedPrompts,
96
- saveThreadContext
97
- } = args;
98
- const threadCtx = {
99
- event,
100
- context,
101
- client,
102
- logger
103
- };
104
- let savedThreadContext = false;
105
- const saveContext = async () => {
106
- if (savedThreadContext) return;
107
- try {
108
- await saveThreadContext();
109
- savedThreadContext = true;
110
- } catch (error) {
111
- logger.error(
112
- `[channel-slack-agent-core] saveThreadContext failed: ${error instanceof Error ? error.message : String(error)}`
113
- );
114
- }
115
- };
116
- try {
117
- const initialReply = await resolveInitialReply(
118
- options.getInitialReply,
119
- threadCtx
120
- );
121
- if (initialReply) {
122
- await say(initialReply);
123
- }
124
- } catch (error) {
125
- logger.error(
126
- `[channel-slack-agent-core] initial assistant reply failed: ${error instanceof Error ? error.message : String(error)}`
127
- );
128
- }
129
- await saveContext();
130
- try {
131
- const prompts = await options.getSuggestedPrompts?.(threadCtx);
132
- if (prompts && prompts.prompts.length > 0) {
133
- await setSuggestedPrompts({
134
- ...prompts.title ? { title: prompts.title } : {},
135
- prompts: prompts.prompts
136
- });
137
- }
138
- } catch (error) {
139
- logger.error(
140
- `[channel-slack-agent-core] setSuggestedPrompts failed: ${error instanceof Error ? error.message : String(error)}`
141
- );
142
- }
143
- };
144
- }
145
-
146
- // src/assistant/lifecycle/thread-context-changed.ts
147
- function createThreadContextChangedHandler() {
148
- return async ({ event, saveThreadContext, logger }) => {
149
- try {
150
- await saveThreadContext();
151
- logger.info("[channel-slack-agent-core] thread context changed saved", {
152
- channelId: event.assistant_thread.channel_id,
153
- threadTs: event.assistant_thread.thread_ts,
154
- originChannelId: event.assistant_thread.context.channel_id,
155
- teamId: event.assistant_thread.context.team_id
156
- });
157
- } catch (error) {
158
- logger.error(
159
- `[channel-slack-agent-core] threadContextChanged failed: ${error instanceof Error ? error.message : String(error)}`
160
- );
161
- }
162
- };
163
- }
164
-
165
- // src/assistant/lifecycle/user-message.ts
166
- import {
167
- withinScope
168
- } from "@cuylabs/agent-core";
169
- import {
170
- extractSlackAuthContext,
171
- resolveSlackMessageFormatter,
172
- runWithSlackTurnContext
173
- } from "@cuylabs/channel-slack/core";
174
- import { parseSlackMessageActivityFromMessageEvent } from "@cuylabs/channel-slack/assistant";
175
-
176
- // src/assistant/session.ts
177
- function resolveAssistantSessionId(message, strategy) {
178
- switch (strategy) {
179
- case "channel-id":
180
- return message.channel;
181
- case "user-per-thread":
182
- return `${message.channel}:${message.threadTs}:${message.userId}`;
183
- default:
184
- return `${message.channel}:${message.threadTs}`;
185
- }
186
- }
187
-
188
- // src/assistant/scope-attributes.ts
189
- function buildAssistantScopeAttributes(message, auth, threadContext, prep) {
190
- return {
191
- slackChannelId: message.channelId,
192
- slackChannelType: message.channelType,
193
- slackUserId: message.userId,
194
- slackTeamId: message.teamId ?? auth.teamId,
195
- slackEnterpriseId: auth.enterpriseId,
196
- slackThreadTs: message.threadTs,
197
- slackMessageTs: message.messageTs,
198
- slackAssistantOriginChannelId: threadContext.channel_id,
199
- ...prep.scopeAttributes ?? {}
200
- };
201
- }
202
-
203
- // src/assistant/sink.ts
204
- function createAssistantSink(params) {
205
- let stream;
206
- let stopped = false;
207
- function ensureStream(bufferSize) {
208
- if (stream) return stream;
209
- const streamArgs = {
210
- ...params.chatStreamStartArgs ?? {},
211
- task_display_mode: params.taskDisplayMode,
212
- buffer_size: bufferSize
213
- };
214
- let created;
215
- if (typeof params.sayStream === "function") {
216
- created = params.sayStream(streamArgs);
217
- } else {
218
- const clientWithStream = params.client;
219
- if (typeof clientWithStream.chatStream !== "function") {
220
- throw new Error(
221
- "Slack client does not expose chatStream; ensure @slack/web-api >=7.16.0 is installed."
222
- );
223
- }
224
- created = clientWithStream.chatStream({
225
- channel: params.channel,
226
- thread_ts: params.threadTs,
227
- ...params.teamId ? { recipient_team_id: params.teamId } : {},
228
- ...params.userId ? { recipient_user_id: params.userId } : {},
229
- ...streamArgs
230
- });
231
- }
232
- stream = {
233
- append: (args) => created.append(args),
234
- stop: async (args) => {
235
- if (stopped) return void 0;
236
- stopped = true;
237
- return created.stop(args);
238
- }
239
- };
240
- return stream;
241
- }
242
- return {
243
- artifactClient: params.client,
244
- artifactTarget: {
245
- channelId: params.channel,
246
- threadTs: params.threadTs
247
- },
248
- async postMessage() {
249
- throw new Error(
250
- "Assistant bridge uses chat-stream mode and does not support postMessage."
251
- );
252
- },
253
- async updateMessage() {
254
- },
255
- createChatStream: ({ bufferSize }) => ensureStream(bufferSize),
256
- async appendError(text) {
257
- if (stopped) return;
258
- const target = ensureStream(256);
259
- await target.append({ markdown_text: `
260
-
261
- ${text}` });
262
- },
263
- async finalize() {
264
- if (stopped || !stream) return;
265
- await stream.stop();
266
- }
267
- };
268
- }
269
- function createAssistantInteractiveResponder(params) {
270
- return {
271
- async postMessage(message) {
272
- const result = await params.client.chat.postMessage({
273
- channel: params.channel,
274
- thread_ts: params.threadTs,
275
- text: message.text,
276
- ...message.blocks ? { blocks: message.blocks } : {}
277
- });
278
- if (!result.channel || !result.ts) {
279
- throw new Error(
280
- "Slack chat.postMessage response did not include channel and ts."
281
- );
282
- }
283
- return { channel: result.channel, ts: result.ts };
284
- },
285
- async updateMessage(ref, message) {
286
- await params.client.chat.update({
287
- channel: ref.channel,
288
- ts: ref.ts,
289
- text: message.text,
290
- ...message.blocks ? { blocks: message.blocks } : {}
291
- });
292
- }
293
- };
294
- }
295
-
296
- // src/assistant/lifecycle/user-message.ts
297
- function createUserMessageHandler(deps) {
298
- const { options, feedbackBlock, sessionStrategy, turnControlController } = deps;
299
- const taskDisplayMode = options.taskDisplayMode ?? "timeline";
300
- const messageFormatter = resolveSlackMessageFormatter(
301
- options.formatChatMarkdown ?? {}
302
- );
303
- const formatStreamError = options.formatStreamError ?? (() => DEFAULT_STREAM_ERROR_MESSAGE);
304
- const showReasoning = options.showReasoning ?? false;
305
- const showToolUsage = options.showToolUsage ?? true;
306
- const showSubagentToolUsage = options.showSubagentToolUsage ?? false;
307
- const showSubagentResultInTask = options.showSubagentResultInTask ?? false;
308
- const timeoutMs = options.timeoutMs ?? 12e4;
309
- return async (args) => {
310
- const {
311
- message,
312
- client,
313
- context,
314
- logger,
315
- sayStream,
316
- setStatus,
317
- setSuggestedPrompts,
318
- setTitle,
319
- getThreadContext,
320
- saveThreadContext
321
- } = args;
322
- const parsed = parseSlackMessageActivityFromMessageEvent(message);
323
- if (!parsed) {
324
- return;
325
- }
326
- const { channel, threadTs, text, userId } = parsed;
327
- const teamId = context.teamId ?? context.enterpriseId;
328
- if (!channel || !threadTs || !text) {
329
- return;
330
- }
331
- const diagnosticsLogger = options.logger ?? logger;
332
- const auth = extractSlackAuthContext(context, userId);
333
- const threadContext = await safeGetThreadContext(
334
- getThreadContext,
335
- diagnosticsLogger
336
- );
337
- const assistantUtilities = {
338
- setStatus: async (update) => {
339
- await setStatus(update);
340
- },
341
- setSuggestedPrompts: async (prompts) => {
342
- await setSuggestedPrompts({
343
- ...prompts.title ? { title: prompts.title } : {},
344
- prompts: prompts.prompts
345
- });
346
- },
347
- setTitle: async (title) => {
348
- await setTitle(title);
349
- },
350
- getThreadContext: () => getThreadContext(),
351
- saveThreadContext: () => saveThreadContext()
352
- };
353
- const initialSessionId = resolveAssistantSessionId(parsed, sessionStrategy);
354
- const baseRequest = {
355
- message: parsed,
356
- sessionId: initialSessionId,
357
- client,
358
- rawArgs: args,
359
- auth,
360
- threadContext,
361
- assistant: assistantUtilities
362
- };
363
- const sink = createAssistantSink({
364
- client,
365
- sayStream,
366
- channel,
367
- threadTs,
368
- teamId,
369
- userId,
370
- taskDisplayMode,
371
- ...options.chatStreamStartArgs ? { chatStreamStartArgs: options.chatStreamStartArgs } : {}
372
- });
373
- let translatedError;
374
- let unsupportedInteractive;
375
- let timeoutId;
376
- try {
377
- const initialStatus = await resolveInitialStatus(
378
- options.initialStatus,
379
- baseRequest
380
- );
381
- if (initialStatus) {
382
- try {
383
- await setStatus(initialStatus);
384
- } catch (error) {
385
- diagnosticsLogger.warn?.("setStatus failed", {
386
- error: formatErrorForLog(error)
387
- });
388
- }
389
- }
390
- let sessionId = initialSessionId;
391
- if (options.resolveSession) {
392
- const resolved = await options.resolveSession(baseRequest);
393
- if (resolved && resolved.length > 0) {
394
- sessionId = resolved;
395
- }
396
- }
397
- const turnPrep = await options.prepareTurn?.({ ...baseRequest, sessionId }) ?? {};
398
- if (turnPrep.sessionId && turnPrep.sessionId.length > 0) {
399
- sessionId = turnPrep.sessionId;
400
- }
401
- const turnMessage = turnPrep.message ?? text;
402
- const userMessageRequest = {
403
- ...baseRequest,
404
- sessionId,
405
- message: { ...parsed, text: turnMessage }
406
- };
407
- if (options.formatThreadTitle) {
408
- try {
409
- const title = await options.formatThreadTitle(userMessageRequest);
410
- if (title) {
411
- await setTitle(title);
412
- }
413
- } catch (error) {
414
- diagnosticsLogger.warn?.("setTitle failed", {
415
- error: formatErrorForLog(error)
416
- });
417
- }
418
- }
419
- const configuredFinalBlocks = Array.isArray(
420
- options.chatStreamFinalArgs?.blocks
421
- ) ? options.chatStreamFinalArgs.blocks : [];
422
- const finalBlocks = feedbackBlock ? [...configuredFinalBlocks, feedbackBlock] : configuredFinalBlocks;
423
- const chatStreamFinalArgs = options.chatStreamFinalArgs || finalBlocks.length > 0 ? {
424
- ...options.chatStreamFinalArgs ?? {},
425
- ...finalBlocks.length > 0 ? { blocks: finalBlocks } : {}
426
- } : void 0;
427
- const bridgeOptions = resolveSlackEventBridgeOptions({
428
- streamingMode: "chat-stream",
429
- showReasoning,
430
- showToolUsage,
431
- showSubagentToolUsage,
432
- showSubagentResultInTask,
433
- ...options.formatToolTitle ? { formatToolTitle: options.formatToolTitle } : {},
434
- ...options.formatToolUpdate ? { formatToolUpdate: options.formatToolUpdate } : {},
435
- ...options.formatToolDetails ? { formatToolDetails: options.formatToolDetails } : {},
436
- ...options.formatToolResultOutput ? { formatToolResultOutput: options.formatToolResultOutput } : {},
437
- ...options.formatToolError ? { formatToolError: options.formatToolError } : {},
438
- ...options.formatReasoningUpdate ? { formatReasoningUpdate: options.formatReasoningUpdate } : {},
439
- ...options.chatStreamBufferSize !== void 0 ? { chatStreamBufferSize: options.chatStreamBufferSize } : {},
440
- ...options.maxTaskUpdates !== void 0 ? { maxTaskUpdates: options.maxTaskUpdates } : {},
441
- ...options.maxTaskUpdateTextChars !== void 0 ? { maxTaskUpdateTextChars: options.maxTaskUpdateTextChars } : {},
442
- ...options.maxTaskUpdateFieldChars !== void 0 ? { maxTaskUpdateFieldChars: options.maxTaskUpdateFieldChars } : {},
443
- ...chatStreamFinalArgs ? { chatStreamFinalArgs } : {},
444
- ...options.publishFinalResponseArtifact ? {
445
- publishFinalResponseArtifact: options.publishFinalResponseArtifact
446
- } : {},
447
- ...options.finalResponseArtifactMode ? { finalResponseArtifactMode: options.finalResponseArtifactMode } : {},
448
- ...options.finalResponseArtifactStreamThreshold !== void 0 ? {
449
- finalResponseArtifactStreamThreshold: options.finalResponseArtifactStreamThreshold
450
- } : {},
451
- ...options.formatFinalResponseArtifactContinuationNotice ? {
452
- formatFinalResponseArtifactContinuationNotice: options.formatFinalResponseArtifactContinuationNotice
453
- } : {},
454
- ...options.formatFinalResponseArtifactMessage ? {
455
- formatFinalResponseArtifactMessage: options.formatFinalResponseArtifactMessage
456
- } : {},
457
- ...options.onFinalResponseArtifactError ? {
458
- onFinalResponseArtifactError: options.onFinalResponseArtifactError
459
- } : {},
460
- formatMessageText: messageFormatter,
461
- onStatusChange: async (label, event) => {
462
- const update = await resolveStatusUpdate(
463
- options.formatStatus,
464
- label,
465
- event,
466
- args
467
- );
468
- if (update) {
469
- try {
470
- await setStatus(update);
471
- } catch (error) {
472
- diagnosticsLogger.warn?.("setStatus failed", {
473
- error: formatErrorForLog(error)
474
- });
475
- }
476
- }
477
- },
478
- ...options.handleInteractiveRequest ? {
479
- handleInteractiveRequest: (interactive) => options.handleInteractiveRequest({
480
- ...interactive,
481
- slackActivity: parsed,
482
- user: {
483
- userId: userId ?? "unknown",
484
- channelId: channel,
485
- ...teamId ? { teamId } : {},
486
- threadTs,
487
- ...parsed.messageTs ? { messageTs: parsed.messageTs } : {}
488
- },
489
- sessionId,
490
- message: turnMessage,
491
- responder: createAssistantInteractiveResponder({
492
- client,
493
- channel,
494
- threadTs
495
- })
496
- })
497
- } : {}
498
- });
499
- const abortController = new AbortController();
500
- timeoutId = timeoutMs > 0 ? setTimeout(() => abortController.abort(), timeoutMs) : void 0;
501
- const cancelControl = turnControlController?.createCancelControl({
502
- abortController,
503
- channel,
504
- client,
505
- logger: diagnosticsLogger,
506
- sessionId,
507
- ...teamId ? { teamId } : {},
508
- threadTs,
509
- userId: userId ?? "unknown"
510
- });
511
- cancelControl?.show("turn-start");
512
- await runWithSlackTurnContext(
513
- {
514
- slackActivity: parsed,
515
- user: {
516
- userId: userId ?? "unknown",
517
- channelId: channel,
518
- ...teamId ? { teamId } : {},
519
- threadTs
520
- },
521
- sessionId,
522
- message: turnMessage,
523
- auth,
524
- threadContext,
525
- assistant: assistantUtilities,
526
- ...turnPrep.context ? { context: turnPrep.context } : {}
527
- },
528
- () => withinScope(
529
- {
530
- kind: "activity",
531
- name: turnPrep.scopeName?.trim() || "slack-assistant-activity",
532
- sessionId,
533
- attributes: buildAssistantScopeAttributes(
534
- parsed,
535
- auth,
536
- threadContext,
537
- turnPrep
538
- )
539
- },
540
- async () => {
541
- const events = options.source.chat(sessionId, turnMessage, {
542
- abort: abortController.signal,
543
- ...turnPrep.system ? { system: turnPrep.system } : {}
544
- });
545
- const translated = (async function* () {
546
- try {
547
- for await (const event of events) {
548
- cancelControl?.sync(event);
549
- if (event.type === "error") {
550
- translatedError = toError(
551
- event.error
552
- );
553
- yield {
554
- type: "text-delta",
555
- text: `
556
-
557
- ${formatStreamError(translatedError)}`
558
- };
559
- yield { type: "complete" };
560
- return;
561
- }
562
- yield event;
563
- }
564
- } catch (error) {
565
- if (error instanceof UnsupportedSlackInteractiveRequestError) {
566
- unsupportedInteractive = error;
567
- throw error;
568
- }
569
- translatedError = toError(error);
570
- yield {
571
- type: "text-delta",
572
- text: `
573
-
574
- ${formatStreamError(translatedError)}`
575
- };
576
- yield { type: "complete" };
577
- } finally {
578
- cancelControl?.clear("turn-finished");
579
- }
580
- })();
581
- const finalText = await bridgeAgentEventsToSlack(
582
- translated,
583
- sink,
584
- bridgeOptions
585
- );
586
- if (!translatedError && options.getFollowUpPrompts) {
587
- try {
588
- const followUps = await options.getFollowUpPrompts({
589
- ...userMessageRequest,
590
- finalText
591
- });
592
- if (followUps && followUps.prompts.length > 0) {
593
- await assistantUtilities.setSuggestedPrompts(followUps);
594
- }
595
- } catch (error) {
596
- diagnosticsLogger.warn?.("getFollowUpPrompts failed", {
597
- error: formatErrorForLog(error)
598
- });
599
- }
600
- }
601
- }
602
- )
603
- );
604
- } catch (error) {
605
- if (error instanceof UnsupportedSlackInteractiveRequestError && !unsupportedInteractive) {
606
- unsupportedInteractive = error;
607
- }
608
- if (!unsupportedInteractive) {
609
- try {
610
- await sink.appendError(formatStreamError(toError(error)));
611
- } catch {
612
- }
613
- }
614
- try {
615
- await sink.finalize();
616
- } catch {
617
- }
618
- } finally {
619
- if (timeoutId) {
620
- clearTimeout(timeoutId);
621
- }
622
- try {
623
- await setStatus({ status: "" });
624
- } catch {
625
- }
626
- if (translatedError) {
627
- logger.error(
628
- `[channel-slack-agent-core] userMessage error surfaced to user: ${translatedError.message}`
629
- );
630
- }
631
- }
632
- };
633
- }
634
-
635
- // src/assistant/turn-controls.ts
636
- import { randomUUID } from "crypto";
637
- import {
638
- createSlackTurnCancelMessage,
639
- registerSlackTurnCancelAction,
640
- resolveSlackTurnCancelActionId
641
- } from "@cuylabs/channel-slack/turn-controls";
642
- function createSlackAssistantTurnControlController(options) {
643
- const resolvedCancelOptions = resolveCancelOptions(options);
644
- if (!resolvedCancelOptions) {
645
- return void 0;
646
- }
647
- const cancelOptions = resolvedCancelOptions;
648
- const actionId = resolveSlackTurnCancelActionId(cancelOptions.actionId);
649
- const activeControls = /* @__PURE__ */ new Map();
650
- function install(app) {
651
- registerSlackTurnCancelAction(app, {
652
- actionId,
653
- onCancel: (context) => handleCancelAction(context)
654
- });
655
- }
656
- function createCancelControl(controlOptions) {
657
- const control = {
658
- abortController: controlOptions.abortController,
659
- channel: controlOptions.channel,
660
- client: controlOptions.client,
661
- controlId: randomUUID(),
662
- logger: controlOptions.logger,
663
- pending: Promise.resolve(),
664
- sessionId: controlOptions.sessionId,
665
- ...controlOptions.teamId ? { teamId: controlOptions.teamId } : {},
666
- threadTs: controlOptions.threadTs,
667
- userId: controlOptions.userId,
668
- visible: false
669
- };
670
- activeControls.set(control.controlId, control);
671
- return {
672
- show(reason) {
673
- showCancelControl(control, reason);
674
- },
675
- clear(reason) {
676
- clearCancelControl(control, reason);
677
- },
678
- sync(event) {
679
- syncCancelControlForEvent(control, event);
680
- }
681
- };
682
- }
683
- async function handleCancelAction(context) {
684
- const control = activeControls.get(context.controlId);
685
- if (!control) {
686
- await context.deleteMessage().catch(() => void 0);
687
- await acknowledge(context, cancelOptions.alreadyCompletedAck);
688
- return;
689
- }
690
- if (context.userId !== control.userId) {
691
- await acknowledge(context, cancelOptions.unauthorizedAck);
692
- return;
693
- }
694
- clearCancelControl(control, "button-cancel");
695
- activeControls.delete(control.controlId);
696
- if (!control.abortController.signal.aborted) {
697
- control.abortController.abort(createSlackTurnCancelledError());
698
- }
699
- await cancelOptions.onCancel?.({
700
- controlId: control.controlId,
701
- sessionId: control.sessionId,
702
- channelId: control.channel,
703
- threadTs: control.threadTs,
704
- userId: control.userId,
705
- ...control.teamId ? { teamId: control.teamId } : {}
706
- });
707
- await acknowledge(context, cancelOptions.canceledAck);
708
- }
709
- function showCancelControl(control, reason) {
710
- if (control.visible || control.abortController.signal.aborted) {
711
- return;
712
- }
713
- control.visible = true;
714
- enqueueControlUpdate(control, "show", reason, async () => {
715
- const message = createSlackTurnCancelMessage({
716
- actionId,
717
- controlId: control.controlId,
718
- messageText: cancelOptions.messageText,
719
- buttonText: cancelOptions.buttonText,
720
- sessionId: control.sessionId
721
- });
722
- if (control.target) {
723
- await control.client.chat.update({
724
- channel: control.target.channel,
725
- ts: control.target.ts,
726
- text: message.text,
727
- blocks: message.blocks
728
- });
729
- return;
730
- }
731
- const response = await control.client.chat.postMessage({
732
- channel: control.channel,
733
- thread_ts: control.threadTs,
734
- text: message.text,
735
- blocks: message.blocks
736
- });
737
- if (response.channel && response.ts) {
738
- control.target = { channel: response.channel, ts: response.ts };
739
- }
740
- });
741
- }
742
- function clearCancelControl(control, reason) {
743
- const terminal = isTerminalClearReason(reason);
744
- if (terminal) {
745
- activeControls.delete(control.controlId);
746
- }
747
- if (!control.visible) {
748
- return;
749
- }
750
- control.visible = false;
751
- enqueueControlUpdate(control, "clear", reason, async () => {
752
- if (!control.target) {
753
- return;
754
- }
755
- const target = control.target;
756
- control.target = void 0;
757
- await control.client.chat.delete({
758
- channel: target.channel,
759
- ts: target.ts
760
- });
761
- });
762
- }
763
- function syncCancelControlForEvent(control, event) {
764
- switch (event.type) {
765
- case "text-start":
766
- case "text-delta":
767
- case "text-end":
768
- if (cancelOptions.visibleWhen === "before-output") {
769
- clearCancelControl(control, "streaming-started");
770
- }
771
- break;
772
- case "reasoning-start":
773
- case "reasoning-delta":
774
- case "reasoning-end":
775
- case "tool-start":
776
- case "approval-request":
777
- case "human-input-request":
778
- showCancelControl(control, event.type);
779
- break;
780
- case "status":
781
- if (isWaitingForAgentStatus(event.status)) {
782
- showCancelControl(control, `status:${event.status}`);
783
- } else if (event.status === "idle" || event.status === "error") {
784
- clearCancelControl(control, `status:${event.status}`);
785
- }
786
- break;
787
- case "complete":
788
- clearCancelControl(control, "turn-finished");
789
- break;
790
- case "error":
791
- clearCancelControl(control, "turn-error");
792
- break;
793
- default:
794
- break;
795
- }
796
- }
797
- function enqueueControlUpdate(control, action, reason, update) {
798
- control.pending = control.pending.then(update);
799
- control.pending = control.pending.catch((error) => {
800
- control.logger.warn?.("Slack turn cancel control update failed", {
801
- action,
802
- controlId: control.controlId,
803
- error: formatErrorForLog(error),
804
- reason,
805
- sessionId: control.sessionId
806
- });
807
- });
808
- }
809
- async function acknowledge(context, text) {
810
- if (text === false) {
811
- return;
812
- }
813
- await context.acknowledgeEphemeral(text ?? "Canceled the running request.");
814
- }
815
- return { install, createCancelControl };
816
- }
817
- function resolveCancelOptions(options) {
818
- if (!options?.cancel) {
819
- return void 0;
820
- }
821
- const input = options.cancel === true ? {} : options.cancel;
822
- return {
823
- ...input,
824
- visibleWhen: input.visibleWhen ?? "before-output",
825
- canceledAck: input.canceledAck ?? "Canceled the running request.",
826
- alreadyCompletedAck: input.alreadyCompletedAck ?? "That request is no longer running.",
827
- unauthorizedAck: input.unauthorizedAck ?? "Only the user who started this request can cancel it."
828
- };
829
- }
830
- function isWaitingForAgentStatus(status) {
831
- return status === "processing" || status === "thinking" || status === "reasoning" || status === "calling-tool" || status === "waiting-approval" || status === "waiting-input";
832
- }
833
- function isTerminalClearReason(reason) {
834
- return reason === "streaming-started" || reason === "turn-finished" || reason === "turn-error" || reason === "button-cancel";
835
- }
836
- function createSlackTurnCancelledError() {
837
- return Object.assign(new Error("Slack turn was cancelled by the user."), {
838
- category: "cancelled"
839
- });
840
- }
841
-
842
- // src/assistant/bridge.ts
843
- import { createSlackAssistantThreadContextStore } from "@cuylabs/channel-slack/assistant";
844
- function createSlackAssistantBridge(options) {
845
- if (!options.source || typeof options.source.chat !== "function") {
846
- throw new Error(
847
- "createSlackAssistantBridge: options.source must implement AgentTurnSource"
848
- );
849
- }
850
- if (options.timeoutMs !== void 0 && (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 0)) {
851
- throw new Error(
852
- "createSlackAssistantBridge: timeoutMs must be a finite non-negative number"
853
- );
854
- }
855
- const sessionStrategy = options.sessionStrategy ?? "thread-aware";
856
- const feedbackConfig = options.feedback === false ? void 0 : options.feedback ?? {};
857
- const feedbackBlock = feedbackConfig ? createSlackFeedbackBlock(feedbackConfig) : void 0;
858
- const feedbackActionId = feedbackConfig?.actionId ?? SLACK_FEEDBACK_ACTION_ID;
859
- const turnControlController = createSlackAssistantTurnControlController(
860
- options.turnControls
861
- );
862
- const turnCancelActionId = turnControlController ? resolveSlackTurnCancelActionId2(
863
- resolveTurnCancelActionIdOption(options.turnControls)
864
- ) : void 0;
865
- const threadStarted = createThreadStartedHandler(options);
866
- const threadContextChanged = createThreadContextChangedHandler();
867
- const threadContextStore = options.threadContextStore ?? createSlackAssistantThreadContextStore();
868
- const userMessage = createUserMessageHandler({
869
- options,
870
- feedbackBlock,
871
- sessionStrategy,
872
- ...turnControlController ? { turnControlController } : {}
873
- });
874
- const config = {
875
- threadStarted,
876
- threadContextChanged,
877
- userMessage,
878
- // The store shape must match Bolt's `AssistantThreadContextStore` exactly.
879
- // We accept the structural alias above and cast here so callers don't
880
- // need to import Bolt's internal types in their own code.
881
- threadContextStore
882
- };
883
- const assistant = new Assistant(config);
884
- function install(app) {
885
- app.assistant(assistant);
886
- if (feedbackConfig) {
887
- registerSlackFeedbackAction(app, {
888
- ...feedbackConfig,
889
- resolveSessionId: (context) => resolveFeedbackSessionId(context, sessionStrategy),
890
- onFeedback: feedbackConfig.onFeedback ?? (async (_ctx) => {
891
- })
892
- });
893
- }
894
- turnControlController?.install(app);
895
- }
896
- return {
897
- assistant,
898
- install,
899
- ...feedbackConfig ? { feedbackActionId } : {},
900
- ...turnCancelActionId ? { turnCancelActionId } : {}
901
- };
902
- }
903
- function resolveTurnCancelActionIdOption(options) {
904
- if (!options || !options.cancel) {
905
- return void 0;
906
- }
907
- return typeof options.cancel === "object" ? options.cancel.actionId : void 0;
908
- }
909
-
910
- // src/assistant/index.ts
911
- import {
912
- createSlackAssistantThreadContextStore as createSlackAssistantThreadContextStore2,
913
- parseSlackMessageActivityFromMessageEvent as parseSlackMessageActivityFromMessageEvent2
914
- } from "@cuylabs/channel-slack/assistant";
915
-
916
- export {
917
- resolveAssistantSessionId,
918
- createSlackAssistantBridge,
919
- createSlackAssistantThreadContextStore2 as createSlackAssistantThreadContextStore,
920
- parseSlackMessageActivityFromMessageEvent2 as parseSlackMessageActivityFromMessageEvent
921
- };