@cuylabs/channel-slack-agent-core 0.1.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 (68) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +80 -0
  3. package/dist/adapter-Cmd2C90g.d.ts +31 -0
  4. package/dist/adapter.d.ts +23 -0
  5. package/dist/adapter.js +13 -0
  6. package/dist/app-surface.d.ts +71 -0
  7. package/dist/app-surface.js +12 -0
  8. package/dist/app.d.ts +54 -0
  9. package/dist/app.js +14 -0
  10. package/dist/assistant.d.ts +19 -0
  11. package/dist/assistant.js +16 -0
  12. package/dist/bolt.d.ts +8 -0
  13. package/dist/bolt.js +10 -0
  14. package/dist/chunk-2SUAW6MV.js +12 -0
  15. package/dist/chunk-645NNJIM.js +12 -0
  16. package/dist/chunk-ANIZ5NT4.js +12 -0
  17. package/dist/chunk-BFUPAJON.js +662 -0
  18. package/dist/chunk-CYEBGC6G.js +77 -0
  19. package/dist/chunk-DHPD4XH5.js +827 -0
  20. package/dist/chunk-FDRQOG7Q.js +471 -0
  21. package/dist/chunk-GNXWTKQ6.js +48 -0
  22. package/dist/chunk-HFT2FXJP.js +12 -0
  23. package/dist/chunk-I2KLQ2HA.js +22 -0
  24. package/dist/chunk-IWUYIAY5.js +69 -0
  25. package/dist/chunk-IXY3BXU5.js +689 -0
  26. package/dist/chunk-JMLB7A2V.js +85 -0
  27. package/dist/chunk-K2E6A377.js +12 -0
  28. package/dist/chunk-M64Z6TYL.js +198 -0
  29. package/dist/chunk-NDVXBI7Z.js +12 -0
  30. package/dist/chunk-NIPAN4KA.js +76 -0
  31. package/dist/chunk-PX4RGO3N.js +12 -0
  32. package/dist/chunk-RFHXERNL.js +27 -0
  33. package/dist/chunk-VHGV66M7.js +12 -0
  34. package/dist/chunk-WO4BJMF3.js +82 -0
  35. package/dist/diagnostics.d.ts +1 -0
  36. package/dist/diagnostics.js +10 -0
  37. package/dist/express-assistant.d.ts +102 -0
  38. package/dist/express-assistant.js +12 -0
  39. package/dist/express.d.ts +98 -0
  40. package/dist/express.js +11 -0
  41. package/dist/feedback.d.ts +1 -0
  42. package/dist/feedback.js +10 -0
  43. package/dist/history.d.ts +1 -0
  44. package/dist/history.js +10 -0
  45. package/dist/index.d.ts +32 -0
  46. package/dist/index.js +202 -0
  47. package/dist/interactive-o_NZb-Xg.d.ts +47 -0
  48. package/dist/interactive.d.ts +30 -0
  49. package/dist/interactive.js +25 -0
  50. package/dist/mcp.d.ts +84 -0
  51. package/dist/mcp.js +9 -0
  52. package/dist/options-C7OYeNR-.d.ts +71 -0
  53. package/dist/options-Uf-qmQKN.d.ts +263 -0
  54. package/dist/policy.d.ts +1 -0
  55. package/dist/policy.js +10 -0
  56. package/dist/setup.d.ts +1 -0
  57. package/dist/setup.js +10 -0
  58. package/dist/shared.d.ts +129 -0
  59. package/dist/shared.js +20 -0
  60. package/dist/socket.d.ts +137 -0
  61. package/dist/socket.js +16 -0
  62. package/dist/targets.d.ts +1 -0
  63. package/dist/targets.js +10 -0
  64. package/dist/types-BqRzb_Cd.d.ts +346 -0
  65. package/dist/types-Crpil4kb.d.ts +136 -0
  66. package/dist/users.d.ts +1 -0
  67. package/dist/users.js +10 -0
  68. package/package.json +169 -0
@@ -0,0 +1,827 @@
1
+ // src/shared/event-bridge/interactive.ts
2
+ var UnsupportedSlackInteractiveRequestError = class extends Error {
3
+ kind;
4
+ requestId;
5
+ constructor(kind, requestId, message) {
6
+ super(message);
7
+ this.name = "UnsupportedSlackInteractiveRequestError";
8
+ this.kind = kind;
9
+ this.requestId = requestId;
10
+ }
11
+ };
12
+
13
+ // src/shared/event-bridge/bridge.ts
14
+ async function bridgeAgentEventsToSlack(events, sink, options) {
15
+ let fullResponse = "";
16
+ let lastUpdateLength = 0;
17
+ let postedTs;
18
+ let postedChannel;
19
+ let chatStream;
20
+ let chatStreamStopped = false;
21
+ let chatStreamAppendDisabled = false;
22
+ let chatStreamOverflowed = false;
23
+ let msgTooLongFallbackPosted = false;
24
+ let statusLabel = "";
25
+ let lastNotifiedStatus = "";
26
+ let lastProgressiveUpdateAtMs;
27
+ const subAgentTitles = /* @__PURE__ */ new Map();
28
+ const openSubAgentTaskTitles = /* @__PURE__ */ new Map();
29
+ const taskUpdateFingerprints = /* @__PURE__ */ new Set();
30
+ let taskUpdateCount = 0;
31
+ let taskUpdateTextChars = 0;
32
+ let taskUpdatesOmitted = false;
33
+ async function notifyStatus(label, event) {
34
+ if (!options.onStatusChange) return;
35
+ const trimmed = label.trim();
36
+ if (trimmed === lastNotifiedStatus) return;
37
+ lastNotifiedStatus = trimmed;
38
+ try {
39
+ await options.onStatusChange(trimmed, event);
40
+ } catch {
41
+ }
42
+ }
43
+ async function ensurePlaceholder() {
44
+ if (postedTs) return;
45
+ const placeholder = statusLabel.trim() || "...";
46
+ const result = await sink.postMessage(placeholder);
47
+ postedTs = result.ts;
48
+ postedChannel = result.channel;
49
+ lastUpdateLength = 0;
50
+ }
51
+ async function updatePlaceholder(text) {
52
+ if (!postedTs || !postedChannel) return;
53
+ await sink.updateMessage(postedChannel, postedTs, text);
54
+ }
55
+ async function updateIfNeeded(force = false) {
56
+ if (!postedTs || !postedChannel || !fullResponse) return;
57
+ const shouldUpdate = force || fullResponse.length - lastUpdateLength >= options.progressiveUpdateThreshold && shouldIssueProgressiveUpdate();
58
+ if (shouldUpdate) {
59
+ await sink.updateMessage(
60
+ postedChannel,
61
+ postedTs,
62
+ options.formatMessageText(fullResponse)
63
+ );
64
+ lastUpdateLength = fullResponse.length;
65
+ lastProgressiveUpdateAtMs = Date.now();
66
+ }
67
+ }
68
+ function shouldIssueProgressiveUpdate() {
69
+ if (options.progressiveUpdateIntervalMs <= 0) {
70
+ return true;
71
+ }
72
+ if (lastProgressiveUpdateAtMs === void 0) {
73
+ return true;
74
+ }
75
+ return Date.now() - lastProgressiveUpdateAtMs >= options.progressiveUpdateIntervalMs;
76
+ }
77
+ function ensureChatStream() {
78
+ if (chatStream) {
79
+ return chatStream;
80
+ }
81
+ if (!sink.createChatStream) {
82
+ throw new Error(
83
+ "SlackResponseSink.createChatStream is required for chat-stream mode."
84
+ );
85
+ }
86
+ chatStream = sink.createChatStream({
87
+ bufferSize: options.chatStreamBufferSize
88
+ });
89
+ return chatStream;
90
+ }
91
+ async function postMsgTooLongFallback(text) {
92
+ if (msgTooLongFallbackPosted) {
93
+ return;
94
+ }
95
+ msgTooLongFallbackPosted = true;
96
+ const fallback = formatMsgTooLongFallback(text);
97
+ await sink.postMessage(fallback).catch(() => void 0);
98
+ }
99
+ function formatToolTitle(toolName) {
100
+ return options.formatToolTitle?.(toolName) ?? toolName;
101
+ }
102
+ function subAgentTaskId(dispatchId) {
103
+ return `subagent:${dispatchId}`;
104
+ }
105
+ function subAgentToolTaskId(dispatchId, toolCallId) {
106
+ return `subagent:${dispatchId}:tool:${toolCallId}`;
107
+ }
108
+ function shouldRenderTaskUpdates() {
109
+ return options.showToolUsage && options.streamingMode === "chat-stream";
110
+ }
111
+ function formatSubAgentRole(role) {
112
+ const words = role.replace(/[_-]+/g, " ").trim();
113
+ return words ? words.charAt(0).toUpperCase() + words.slice(1) : "Subagent";
114
+ }
115
+ function inferSubAgentDepth(event) {
116
+ if (typeof event.depth === "number" && event.depth > 0) {
117
+ return event.depth;
118
+ }
119
+ if (event.agentPath) {
120
+ return Math.max(0, event.agentPath.split("/").filter(Boolean).length - 1);
121
+ }
122
+ return event.parentDispatchId ? 1 : 0;
123
+ }
124
+ function fallbackNestedPrefix(depth) {
125
+ return depth > 0 ? `Nested ${depth}` : "Nested";
126
+ }
127
+ function formatSubAgentTaskTitle(event) {
128
+ const roleTitle = formatSubAgentRole(event.role);
129
+ if (!event.parentDispatchId) {
130
+ return roleTitle;
131
+ }
132
+ const parentTitle = subAgentTitles.get(event.parentDispatchId);
133
+ const title = parentTitle ? `${parentTitle} / ${roleTitle}` : `${fallbackNestedPrefix(inferSubAgentDepth(event))} / ${roleTitle}`;
134
+ subAgentTitles.set(event.dispatchId, title);
135
+ return title;
136
+ }
137
+ function formatSubAgentStartDetails(event) {
138
+ return event.title && event.title !== event.role ? event.title : "Started";
139
+ }
140
+ function formatSubAgentStatusDetails(status) {
141
+ switch (status) {
142
+ case "thinking":
143
+ case "reasoning":
144
+ return options.formatReasoningUpdate();
145
+ case "calling-tool":
146
+ return "Using tools...";
147
+ case "waiting-approval":
148
+ return "Waiting for approval...";
149
+ case "waiting-input":
150
+ return "Waiting for input...";
151
+ case "processing":
152
+ return "Working...";
153
+ case "error":
154
+ return "Error";
155
+ default:
156
+ return void 0;
157
+ }
158
+ }
159
+ async function appendChatStreamText(text) {
160
+ if (chatStreamAppendDisabled) {
161
+ return;
162
+ }
163
+ const formatted = options.formatMessageText(text);
164
+ if (!formatted) {
165
+ return;
166
+ }
167
+ await appendToChatStream({ markdown_text: formatted });
168
+ }
169
+ async function appendChatStreamChunk(chunk) {
170
+ if (chatStreamAppendDisabled) {
171
+ return;
172
+ }
173
+ await appendToChatStream({ chunks: [chunk] });
174
+ }
175
+ async function appendToChatStream(args) {
176
+ try {
177
+ await ensureChatStream().append(args);
178
+ } catch (error) {
179
+ if (!isSlackMsgTooLongError(error)) {
180
+ throw error;
181
+ }
182
+ chatStreamAppendDisabled = true;
183
+ chatStreamOverflowed = true;
184
+ }
185
+ }
186
+ function truncateTaskField(value) {
187
+ if (value === void 0) {
188
+ return void 0;
189
+ }
190
+ const maxLength = Math.max(0, options.maxTaskUpdateFieldChars);
191
+ if (value.length <= maxLength) {
192
+ return value;
193
+ }
194
+ if (maxLength <= 3) {
195
+ return value.slice(0, maxLength);
196
+ }
197
+ return `${value.slice(0, maxLength - 3).trimEnd()}...`;
198
+ }
199
+ function normalizeTaskUpdate(chunk) {
200
+ const normalized = {
201
+ type: "task_update",
202
+ id: chunk.id,
203
+ title: chunk.title,
204
+ status: chunk.status
205
+ };
206
+ const details = truncateTaskField(chunk.details);
207
+ const output = truncateTaskField(chunk.output);
208
+ if (details !== void 0) {
209
+ normalized.details = details;
210
+ }
211
+ if (output !== void 0) {
212
+ normalized.output = output;
213
+ }
214
+ return normalized;
215
+ }
216
+ function taskUpdateTextLength(chunk) {
217
+ return chunk.title.length + (chunk.details?.length ?? 0) + (chunk.output?.length ?? 0);
218
+ }
219
+ function taskUpdateFingerprint(chunk) {
220
+ return JSON.stringify([
221
+ chunk.id,
222
+ chunk.title,
223
+ chunk.status,
224
+ chunk.details ?? "",
225
+ chunk.output ?? ""
226
+ ]);
227
+ }
228
+ async function appendTaskUpdatesOmittedNotice() {
229
+ if (taskUpdatesOmitted || taskUpdateCount >= options.maxTaskUpdates || options.maxTaskUpdateTextChars <= 0) {
230
+ return;
231
+ }
232
+ taskUpdatesOmitted = true;
233
+ const notice = {
234
+ type: "task_update",
235
+ id: "slack-task-updates-omitted",
236
+ title: "Additional task updates omitted",
237
+ status: "complete",
238
+ details: "Slack task timeline limit reached; see the final answer for remaining context."
239
+ };
240
+ taskUpdateCount += 1;
241
+ taskUpdateTextChars += taskUpdateTextLength(notice);
242
+ taskUpdateFingerprints.add(taskUpdateFingerprint(notice));
243
+ await appendChatStreamChunk(notice);
244
+ }
245
+ async function appendTaskUpdate(chunk, appendOptions = {}) {
246
+ const normalized = normalizeTaskUpdate(chunk);
247
+ const fingerprint = taskUpdateFingerprint(normalized);
248
+ if (taskUpdateFingerprints.has(fingerprint)) {
249
+ return false;
250
+ }
251
+ const textLength = taskUpdateTextLength(normalized);
252
+ const maxRealTaskUpdates = Math.max(0, options.maxTaskUpdates - 1);
253
+ if (!appendOptions.force && (taskUpdateCount >= maxRealTaskUpdates || taskUpdateTextChars + textLength > options.maxTaskUpdateTextChars)) {
254
+ await appendTaskUpdatesOmittedNotice();
255
+ return false;
256
+ }
257
+ taskUpdateFingerprints.add(fingerprint);
258
+ taskUpdateCount += 1;
259
+ taskUpdateTextChars += textLength;
260
+ await appendChatStreamChunk(normalized);
261
+ return !chatStreamAppendDisabled;
262
+ }
263
+ async function finalizeOpenSubAgentTasks() {
264
+ if (!shouldRenderTaskUpdates() || openSubAgentTaskTitles.size === 0) {
265
+ return;
266
+ }
267
+ for (const [dispatchId, title] of [...openSubAgentTaskTitles]) {
268
+ await appendTaskUpdate(
269
+ {
270
+ type: "task_update",
271
+ id: subAgentTaskId(dispatchId),
272
+ title,
273
+ status: "complete",
274
+ output: "Still running in the background."
275
+ },
276
+ { force: true }
277
+ );
278
+ openSubAgentTaskTitles.delete(dispatchId);
279
+ }
280
+ }
281
+ async function stopChatStream(markdownText, fallbackText) {
282
+ if (chatStreamStopped) {
283
+ return;
284
+ }
285
+ const stream = ensureChatStream();
286
+ if (chatStreamOverflowed) {
287
+ try {
288
+ await stream.stop();
289
+ } catch (error) {
290
+ if (!isSlackMsgTooLongError(error)) {
291
+ throw error;
292
+ }
293
+ }
294
+ chatStreamStopped = true;
295
+ await postMsgTooLongFallback(fallbackText ?? markdownText);
296
+ return;
297
+ }
298
+ const finalArgs = {
299
+ ...options.chatStreamFinalArgs ?? {}
300
+ };
301
+ if (markdownText) {
302
+ finalArgs.markdown_text = markdownText;
303
+ }
304
+ try {
305
+ await stream.stop(
306
+ Object.keys(finalArgs).length > 0 ? finalArgs : void 0
307
+ );
308
+ chatStreamStopped = true;
309
+ } catch (error) {
310
+ if (!isSlackMsgTooLongError(error)) {
311
+ throw error;
312
+ }
313
+ chatStreamStopped = true;
314
+ await postMsgTooLongFallback(fallbackText ?? markdownText);
315
+ }
316
+ }
317
+ try {
318
+ for await (const event of events) {
319
+ switch (event.type) {
320
+ // ── Text streaming ──────────────────────────────────────────────
321
+ case "text-start":
322
+ if (options.streamingMode === "progressive") {
323
+ await ensurePlaceholder();
324
+ }
325
+ break;
326
+ case "text-delta":
327
+ if (statusLabel) {
328
+ statusLabel = "";
329
+ await notifyStatus("", event);
330
+ }
331
+ fullResponse += event.text;
332
+ if (options.streamingMode === "chat-stream") {
333
+ await appendChatStreamText(event.text);
334
+ } else if (options.streamingMode === "progressive") {
335
+ await ensurePlaceholder();
336
+ await updateIfNeeded();
337
+ }
338
+ break;
339
+ case "text-end":
340
+ break;
341
+ // ── Reasoning ───────────────────────────────────────────────────
342
+ case "reasoning-start":
343
+ if (options.showReasoning) {
344
+ statusLabel = options.formatReasoningUpdate();
345
+ await notifyStatus(statusLabel, event);
346
+ if (options.streamingMode === "progressive") {
347
+ if (postedTs) {
348
+ await updatePlaceholder(statusLabel);
349
+ } else {
350
+ await ensurePlaceholder();
351
+ }
352
+ }
353
+ }
354
+ break;
355
+ case "reasoning-end":
356
+ statusLabel = "";
357
+ break;
358
+ // ── Tools ───────────────────────────────────────────────────────
359
+ case "tool-start":
360
+ if (options.showToolUsage) {
361
+ const toolTitle = formatToolTitle(event.toolName);
362
+ statusLabel = options.formatToolUpdate(event.toolName);
363
+ const toolDetails = options.formatToolDetails?.(event) ?? statusLabel;
364
+ await notifyStatus(statusLabel, event);
365
+ if (options.streamingMode === "chat-stream") {
366
+ await appendTaskUpdate({
367
+ type: "task_update",
368
+ id: event.toolCallId,
369
+ title: toolTitle,
370
+ status: "in_progress",
371
+ details: toolDetails
372
+ });
373
+ } else if (options.streamingMode === "progressive") {
374
+ if (!postedTs) {
375
+ const result = await sink.postMessage(statusLabel);
376
+ postedTs = result.ts;
377
+ postedChannel = result.channel;
378
+ } else if (!fullResponse) {
379
+ await updatePlaceholder(statusLabel);
380
+ }
381
+ }
382
+ }
383
+ break;
384
+ case "tool-result":
385
+ statusLabel = "";
386
+ if (options.showToolUsage && options.streamingMode === "chat-stream") {
387
+ const toolTitle = formatToolTitle(event.toolName);
388
+ await appendTaskUpdate({
389
+ type: "task_update",
390
+ id: event.toolCallId,
391
+ title: toolTitle,
392
+ status: "complete",
393
+ output: formatStreamChunkOutput(event.result)
394
+ });
395
+ }
396
+ break;
397
+ case "tool-error":
398
+ if (options.showToolUsage) {
399
+ const toolTitle = formatToolTitle(event.toolName);
400
+ statusLabel = options.formatToolError(event.toolName, event.error);
401
+ await notifyStatus(statusLabel, event);
402
+ if (options.streamingMode === "chat-stream") {
403
+ await appendTaskUpdate({
404
+ type: "task_update",
405
+ id: event.toolCallId,
406
+ title: toolTitle,
407
+ status: "error",
408
+ details: statusLabel
409
+ });
410
+ } else if (options.streamingMode === "progressive" && postedTs && !fullResponse) {
411
+ await updatePlaceholder(statusLabel);
412
+ }
413
+ }
414
+ break;
415
+ // ── Subagents ───────────────────────────────────────────────────
416
+ case "subagent-start": {
417
+ const roleTitle = formatSubAgentTaskTitle(event);
418
+ subAgentTitles.set(event.dispatchId, roleTitle);
419
+ const details = formatSubAgentStartDetails(event);
420
+ statusLabel = `${roleTitle}: ${details}`;
421
+ await notifyStatus(statusLabel, event);
422
+ if (shouldRenderTaskUpdates()) {
423
+ const appended = await appendTaskUpdate({
424
+ type: "task_update",
425
+ id: subAgentTaskId(event.dispatchId),
426
+ title: roleTitle,
427
+ status: "in_progress",
428
+ details
429
+ });
430
+ if (appended) {
431
+ openSubAgentTaskTitles.set(event.dispatchId, roleTitle);
432
+ }
433
+ }
434
+ break;
435
+ }
436
+ case "subagent-event": {
437
+ const roleTitle = formatSubAgentTaskTitle(event);
438
+ const childEvent = event.event;
439
+ if (childEvent.type === "approval-request") {
440
+ const msg = `${roleTitle}: ${options.formatApprovalRequired(
441
+ childEvent.request
442
+ )}`;
443
+ const handled = await options.handleInteractiveRequest?.({
444
+ kind: "approval",
445
+ request: childEvent.request,
446
+ fallbackText: msg
447
+ });
448
+ if (handled) {
449
+ statusLabel = "";
450
+ await notifyStatus("", event);
451
+ break;
452
+ }
453
+ if (options.interactiveMode === "message-and-error") {
454
+ if (options.streamingMode === "chat-stream") {
455
+ await stopChatStream(msg, msg);
456
+ } else if (postedTs && postedChannel) {
457
+ await sink.updateMessage(postedChannel, postedTs, msg);
458
+ } else {
459
+ await sink.postMessage(msg);
460
+ }
461
+ throw new UnsupportedSlackInteractiveRequestError(
462
+ "approval",
463
+ childEvent.request.id ?? "unknown",
464
+ msg
465
+ );
466
+ }
467
+ break;
468
+ }
469
+ if (childEvent.type === "human-input-request") {
470
+ const msg = `${roleTitle}: ${options.formatHumanInputRequired(
471
+ childEvent.request
472
+ )}`;
473
+ const handled = await options.handleInteractiveRequest?.({
474
+ kind: "human-input",
475
+ request: childEvent.request,
476
+ fallbackText: msg
477
+ });
478
+ if (handled) {
479
+ statusLabel = "";
480
+ await notifyStatus("", event);
481
+ break;
482
+ }
483
+ if (options.interactiveMode === "message-and-error") {
484
+ if (options.streamingMode === "chat-stream") {
485
+ await stopChatStream(msg, msg);
486
+ } else if (postedTs && postedChannel) {
487
+ await sink.updateMessage(postedChannel, postedTs, msg);
488
+ } else {
489
+ await sink.postMessage(msg);
490
+ }
491
+ throw new UnsupportedSlackInteractiveRequestError(
492
+ "human-input",
493
+ childEvent.request.id ?? "unknown",
494
+ msg
495
+ );
496
+ }
497
+ break;
498
+ }
499
+ if (childEvent.type === "approval-resolved" || childEvent.type === "human-input-resolved") {
500
+ statusLabel = "";
501
+ await notifyStatus("", event);
502
+ break;
503
+ }
504
+ if (childEvent.type === "status") {
505
+ const details = formatSubAgentStatusDetails(childEvent.status);
506
+ if (details) {
507
+ statusLabel = `${roleTitle}: ${details}`;
508
+ await notifyStatus(statusLabel, event);
509
+ }
510
+ break;
511
+ }
512
+ if (!options.showToolUsage || !options.showSubagentToolUsage || options.streamingMode !== "chat-stream") {
513
+ break;
514
+ }
515
+ if (childEvent.type === "tool-start") {
516
+ const toolTitle = formatToolTitle(childEvent.toolName);
517
+ const toolDetails = options.formatToolDetails?.(childEvent) ?? options.formatToolUpdate(childEvent.toolName);
518
+ await appendTaskUpdate({
519
+ type: "task_update",
520
+ id: subAgentToolTaskId(event.dispatchId, childEvent.toolCallId),
521
+ title: `${roleTitle}: ${toolTitle}`,
522
+ status: "in_progress",
523
+ details: toolDetails
524
+ });
525
+ } else if (childEvent.type === "tool-result") {
526
+ const toolTitle = formatToolTitle(childEvent.toolName);
527
+ await appendTaskUpdate({
528
+ type: "task_update",
529
+ id: subAgentToolTaskId(event.dispatchId, childEvent.toolCallId),
530
+ title: `${roleTitle}: ${toolTitle}`,
531
+ status: "complete",
532
+ output: formatStreamChunkOutput(childEvent.result)
533
+ });
534
+ } else if (childEvent.type === "tool-error") {
535
+ const toolTitle = formatToolTitle(childEvent.toolName);
536
+ await appendTaskUpdate({
537
+ type: "task_update",
538
+ id: subAgentToolTaskId(event.dispatchId, childEvent.toolCallId),
539
+ title: `${roleTitle}: ${toolTitle}`,
540
+ status: "error",
541
+ details: options.formatToolError(
542
+ childEvent.toolName,
543
+ childEvent.error
544
+ )
545
+ });
546
+ }
547
+ break;
548
+ }
549
+ case "subagent-complete": {
550
+ const roleTitle = formatSubAgentTaskTitle(event);
551
+ statusLabel = "";
552
+ await notifyStatus("", event);
553
+ if (shouldRenderTaskUpdates()) {
554
+ const wasOpen = openSubAgentTaskTitles.has(event.dispatchId);
555
+ const taskUpdate = {
556
+ type: "task_update",
557
+ id: subAgentTaskId(event.dispatchId),
558
+ title: roleTitle,
559
+ status: "complete"
560
+ };
561
+ if (options.showSubagentResultInTask) {
562
+ taskUpdate.output = formatStreamChunkOutput(event.output);
563
+ }
564
+ await appendTaskUpdate(taskUpdate, { force: wasOpen });
565
+ openSubAgentTaskTitles.delete(event.dispatchId);
566
+ }
567
+ break;
568
+ }
569
+ case "subagent-error": {
570
+ const roleTitle = formatSubAgentTaskTitle(event);
571
+ statusLabel = `${roleTitle}: ${event.error}`;
572
+ await notifyStatus(statusLabel, event);
573
+ if (shouldRenderTaskUpdates()) {
574
+ const wasOpen = openSubAgentTaskTitles.has(event.dispatchId);
575
+ await appendTaskUpdate(
576
+ {
577
+ type: "task_update",
578
+ id: subAgentTaskId(event.dispatchId),
579
+ title: roleTitle,
580
+ status: "error",
581
+ details: event.error
582
+ },
583
+ { force: wasOpen }
584
+ );
585
+ openSubAgentTaskTitles.delete(event.dispatchId);
586
+ }
587
+ break;
588
+ }
589
+ // ── Status ──────────────────────────────────────────────────────
590
+ case "status":
591
+ if ((event.status === "thinking" || event.status === "reasoning") && options.showReasoning) {
592
+ statusLabel = options.formatReasoningUpdate();
593
+ await notifyStatus(statusLabel, event);
594
+ }
595
+ break;
596
+ // ── Completion ──────────────────────────────────────────────────
597
+ case "complete":
598
+ if (!fullResponse && event.output) {
599
+ fullResponse = event.output;
600
+ if (options.streamingMode === "chat-stream") {
601
+ await appendChatStreamText(event.output);
602
+ }
603
+ }
604
+ await finalizeOpenSubAgentTasks();
605
+ break;
606
+ // ── Interactive requests (unsupported in Slack transport) ────────
607
+ case "approval-request": {
608
+ const msg = options.formatApprovalRequired(event.request);
609
+ const handled = await options.handleInteractiveRequest?.({
610
+ kind: "approval",
611
+ request: event.request,
612
+ fallbackText: msg
613
+ });
614
+ if (handled) {
615
+ statusLabel = "";
616
+ await notifyStatus("", event);
617
+ break;
618
+ }
619
+ if (options.interactiveMode === "message-and-error") {
620
+ if (options.streamingMode === "chat-stream") {
621
+ await stopChatStream(msg, msg);
622
+ } else if (postedTs && postedChannel) {
623
+ await sink.updateMessage(postedChannel, postedTs, msg);
624
+ } else {
625
+ await sink.postMessage(msg);
626
+ }
627
+ throw new UnsupportedSlackInteractiveRequestError(
628
+ "approval",
629
+ event.request.id ?? "unknown",
630
+ msg
631
+ );
632
+ }
633
+ break;
634
+ }
635
+ case "human-input-request": {
636
+ const msg = options.formatHumanInputRequired(event.request);
637
+ const handled = await options.handleInteractiveRequest?.({
638
+ kind: "human-input",
639
+ request: event.request,
640
+ fallbackText: msg
641
+ });
642
+ if (handled) {
643
+ statusLabel = "";
644
+ await notifyStatus("", event);
645
+ break;
646
+ }
647
+ if (options.interactiveMode === "message-and-error") {
648
+ if (options.streamingMode === "chat-stream") {
649
+ await stopChatStream(msg, msg);
650
+ } else if (postedTs && postedChannel) {
651
+ await sink.updateMessage(postedChannel, postedTs, msg);
652
+ } else {
653
+ await sink.postMessage(msg);
654
+ }
655
+ throw new UnsupportedSlackInteractiveRequestError(
656
+ "human-input",
657
+ event.request.id ?? "unknown",
658
+ msg
659
+ );
660
+ }
661
+ break;
662
+ }
663
+ default:
664
+ break;
665
+ }
666
+ }
667
+ const finalText = fullResponse.trim() ? options.formatMessageText(fullResponse.trim()) : "_No response_";
668
+ if (options.streamingMode === "chat-stream") {
669
+ await stopChatStream(
670
+ fullResponse.trim() ? void 0 : finalText,
671
+ finalText
672
+ );
673
+ } else if (postedTs && postedChannel) {
674
+ await sink.updateMessage(postedChannel, postedTs, finalText);
675
+ } else {
676
+ await sink.postMessage(finalText);
677
+ }
678
+ return fullResponse;
679
+ } catch (error) {
680
+ if (error instanceof UnsupportedSlackInteractiveRequestError) {
681
+ throw error;
682
+ }
683
+ if (options.streamingMode === "chat-stream") {
684
+ if (chatStream && !chatStreamStopped) {
685
+ await stopChatStream(
686
+ void 0,
687
+ fullResponse.trim() ? options.formatMessageText(fullResponse.trim()) : void 0
688
+ ).catch(() => void 0);
689
+ }
690
+ } else if (fullResponse && postedTs && postedChannel) {
691
+ await sink.updateMessage(
692
+ postedChannel,
693
+ postedTs,
694
+ options.formatMessageText(fullResponse)
695
+ ).catch(() => void 0);
696
+ }
697
+ throw error;
698
+ }
699
+ }
700
+ function formatStreamChunkOutput(value) {
701
+ if (value === void 0 || value === null) {
702
+ return void 0;
703
+ }
704
+ const text = typeof value === "string" ? value : safeJsonStringify(value) ?? String(value);
705
+ const normalized = text.trim();
706
+ if (!normalized) {
707
+ return void 0;
708
+ }
709
+ return normalized.length > 500 ? `${normalized.slice(0, 497).trimEnd()}...` : normalized;
710
+ }
711
+ function isSlackMsgTooLongError(error) {
712
+ if (typeof error === "object" && error !== null) {
713
+ const record = error;
714
+ if (record.code === "msg_too_long" || record.error === "msg_too_long") {
715
+ return true;
716
+ }
717
+ const data = record.data;
718
+ if (typeof data === "object" && data !== null) {
719
+ const dataRecord = data;
720
+ if (dataRecord.error === "msg_too_long" || dataRecord.code === "msg_too_long") {
721
+ return true;
722
+ }
723
+ }
724
+ }
725
+ return error instanceof Error && error.message.includes("msg_too_long");
726
+ }
727
+ function formatMsgTooLongFallback(text) {
728
+ const intro = "Slack could not finalize the streamed task timeline because it was too large. Task details were truncated.";
729
+ const normalized = text?.trim();
730
+ if (!normalized) {
731
+ return intro;
732
+ }
733
+ const maxLength = 8e3;
734
+ const separator = "\n\n";
735
+ const available = Math.max(0, maxLength - intro.length - separator.length);
736
+ const body = normalized.length > available ? `${normalized.slice(0, Math.max(0, available - 3)).trimEnd()}...` : normalized;
737
+ return `${intro}${separator}${body}`;
738
+ }
739
+ function safeJsonStringify(value) {
740
+ try {
741
+ return JSON.stringify(value);
742
+ } catch {
743
+ return void 0;
744
+ }
745
+ }
746
+
747
+ // src/shared/event-bridge/options.ts
748
+ var DEFAULT_OPTIONS = {
749
+ showReasoning: false,
750
+ showToolUsage: true,
751
+ showSubagentToolUsage: false,
752
+ showSubagentResultInTask: false,
753
+ formatToolTitle: (toolName) => toolName,
754
+ formatToolUpdate: (toolName) => `\u{1F50D} Using tool: ${toolName}...`,
755
+ formatToolError: (toolName, _error) => `\u26A0\uFE0F Tool ${toolName} encountered an error`,
756
+ formatReasoningUpdate: () => "Thinking...",
757
+ formatMessageText: (text) => text,
758
+ streamingMode: "progressive",
759
+ progressiveUpdateThreshold: 150,
760
+ progressiveUpdateIntervalMs: 3e3,
761
+ chatStreamBufferSize: 256,
762
+ maxTaskUpdates: 200,
763
+ maxTaskUpdateTextChars: 2e4,
764
+ maxTaskUpdateFieldChars: 500,
765
+ interactiveMode: "message-and-error",
766
+ formatApprovalRequired: (request) => `This turn requires approval for "${request.tool}", but in-channel approval is not configured for this Slack host.`,
767
+ formatHumanInputRequired: (request) => `This turn is waiting for input: ${request.title}. In-channel human input is not configured for this Slack host.`
768
+ };
769
+ function resolveSlackEventBridgeOptions(partial) {
770
+ return {
771
+ showReasoning: partial.showReasoning ?? DEFAULT_OPTIONS.showReasoning,
772
+ showToolUsage: partial.showToolUsage ?? DEFAULT_OPTIONS.showToolUsage,
773
+ showSubagentToolUsage: partial.showSubagentToolUsage ?? DEFAULT_OPTIONS.showSubagentToolUsage,
774
+ showSubagentResultInTask: partial.showSubagentResultInTask ?? DEFAULT_OPTIONS.showSubagentResultInTask,
775
+ formatToolTitle: partial.formatToolTitle ?? DEFAULT_OPTIONS.formatToolTitle,
776
+ formatToolUpdate: partial.formatToolUpdate ?? DEFAULT_OPTIONS.formatToolUpdate,
777
+ ...partial.formatToolDetails ? { formatToolDetails: partial.formatToolDetails } : {},
778
+ formatToolError: partial.formatToolError ?? DEFAULT_OPTIONS.formatToolError,
779
+ formatReasoningUpdate: partial.formatReasoningUpdate ?? DEFAULT_OPTIONS.formatReasoningUpdate,
780
+ formatMessageText: partial.formatMessageText ?? DEFAULT_OPTIONS.formatMessageText,
781
+ streamingMode: partial.streamingMode ?? DEFAULT_OPTIONS.streamingMode,
782
+ progressiveUpdateThreshold: partial.progressiveUpdateThreshold ?? DEFAULT_OPTIONS.progressiveUpdateThreshold,
783
+ progressiveUpdateIntervalMs: partial.progressiveUpdateIntervalMs ?? DEFAULT_OPTIONS.progressiveUpdateIntervalMs,
784
+ chatStreamBufferSize: partial.chatStreamBufferSize ?? DEFAULT_OPTIONS.chatStreamBufferSize,
785
+ maxTaskUpdates: resolveNonNegativeInteger(
786
+ partial.maxTaskUpdates,
787
+ DEFAULT_OPTIONS.maxTaskUpdates
788
+ ),
789
+ maxTaskUpdateTextChars: resolveNonNegativeInteger(
790
+ partial.maxTaskUpdateTextChars,
791
+ DEFAULT_OPTIONS.maxTaskUpdateTextChars
792
+ ),
793
+ maxTaskUpdateFieldChars: resolveNonNegativeInteger(
794
+ partial.maxTaskUpdateFieldChars,
795
+ DEFAULT_OPTIONS.maxTaskUpdateFieldChars
796
+ ),
797
+ interactiveMode: partial.interactiveMode ?? DEFAULT_OPTIONS.interactiveMode,
798
+ ...partial.handleInteractiveRequest ? { handleInteractiveRequest: partial.handleInteractiveRequest } : {},
799
+ formatApprovalRequired: partial.formatApprovalRequired ?? DEFAULT_OPTIONS.formatApprovalRequired,
800
+ formatHumanInputRequired: partial.formatHumanInputRequired ?? DEFAULT_OPTIONS.formatHumanInputRequired,
801
+ ...partial.onStatusChange ? { onStatusChange: partial.onStatusChange } : {},
802
+ ...partial.chatStreamFinalArgs ? { chatStreamFinalArgs: partial.chatStreamFinalArgs } : {}
803
+ };
804
+ }
805
+ function resolveNonNegativeInteger(value, fallback) {
806
+ if (value === void 0) {
807
+ return fallback;
808
+ }
809
+ if (!Number.isFinite(value) || value < 0) {
810
+ return fallback;
811
+ }
812
+ return Math.floor(value);
813
+ }
814
+
815
+ // src/shared/turn-context.ts
816
+ import {
817
+ currentSlackTurnContext,
818
+ runWithSlackTurnContext
819
+ } from "@cuylabs/channel-slack/core";
820
+
821
+ export {
822
+ UnsupportedSlackInteractiveRequestError,
823
+ bridgeAgentEventsToSlack,
824
+ resolveSlackEventBridgeOptions,
825
+ currentSlackTurnContext,
826
+ runWithSlackTurnContext
827
+ };