@cuylabs/channel-slack-agent-core 0.6.0 → 0.8.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 (49) hide show
  1. package/README.md +7 -0
  2. package/dist/adapter/index.d.ts +6 -3
  3. package/dist/adapter/index.js +2 -2
  4. package/dist/{adapter-Cmd2C90g.d.ts → adapter-B3CI611y.d.ts} +1 -1
  5. package/dist/app-surface.d.ts +13 -3
  6. package/dist/app-surface.js +4 -4
  7. package/dist/app.d.ts +7 -3
  8. package/dist/app.js +5 -5
  9. package/dist/artifacts/index.d.ts +57 -0
  10. package/dist/artifacts/index.js +6 -0
  11. package/dist/assistant/index.d.ts +5 -3
  12. package/dist/assistant/index.js +2 -2
  13. package/dist/{chunk-FNT4TXNQ.js → chunk-236WN6JD.js} +1 -1
  14. package/dist/{chunk-5NQYLOAW.js → chunk-2R7B7NJR.js} +1 -1
  15. package/dist/{chunk-P7KK5GQG.js → chunk-6T6N4MRK.js} +13 -25
  16. package/dist/{chunk-ZDVD46RT.js → chunk-7YZWCSML.js} +263 -4
  17. package/dist/chunk-C7CHMYV6.js +226 -0
  18. package/dist/{chunk-TIQGJ52F.js → chunk-FQWFB54C.js} +14 -2
  19. package/dist/chunk-NNCVHQC4.js +94 -0
  20. package/dist/{chunk-VCXPNQRB.js → chunk-TCNJY7QA.js} +1 -1
  21. package/dist/{chunk-NOVWLAVP.js → chunk-TMADMHBN.js} +209 -20
  22. package/dist/{chunk-J6CW2RGO.js → chunk-X7ILLZZP.js} +359 -2
  23. package/dist/{chunk-QEJ7TAZJ.js → chunk-YSDFYHPC.js} +2 -2
  24. package/dist/express-assistant.d.ts +4 -2
  25. package/dist/express-assistant.js +3 -3
  26. package/dist/express.d.ts +5 -2
  27. package/dist/express.js +3 -3
  28. package/dist/history/index.d.ts +7 -3
  29. package/dist/index.d.ts +12 -6
  30. package/dist/index.js +28 -10
  31. package/dist/interactive/index.d.ts +43 -2
  32. package/dist/interactive/index.js +9 -3
  33. package/dist/{options-C7OYeNR-.d.ts → options-BcDReOJv.d.ts} +48 -0
  34. package/dist/{options-Uf-qmQKN.d.ts → options-C7-VXmhD.d.ts} +62 -2
  35. package/dist/shared/index.d.ts +20 -6
  36. package/dist/shared/index.js +1 -1
  37. package/dist/socket.d.ts +7 -3
  38. package/dist/socket.js +5 -5
  39. package/dist/{types-BqRzb_Cd.d.ts → types-CRWzJB5G.d.ts} +35 -0
  40. package/dist/types-CiwGU6zC.d.ts +56 -0
  41. package/dist/views/index.d.ts +8 -0
  42. package/dist/views/index.js +10 -0
  43. package/docs/README.md +7 -0
  44. package/docs/concepts/final-response-artifacts.md +39 -0
  45. package/docs/concepts/interactive-requests.md +43 -0
  46. package/docs/concepts/tool-task-rendering.md +46 -0
  47. package/docs/concepts/view-workflows.md +52 -0
  48. package/docs/reference/exports.md +18 -16
  49. package/package.json +14 -4
@@ -7,10 +7,11 @@ import {
7
7
  UnsupportedSlackInteractiveRequestError,
8
8
  bridgeAgentEventsToSlack,
9
9
  resolveSlackEventBridgeOptions
10
- } from "./chunk-NOVWLAVP.js";
10
+ } from "./chunk-TMADMHBN.js";
11
11
 
12
12
  // src/assistant/bridge.ts
13
13
  import { Assistant } from "@slack/bolt";
14
+ import { resolveSlackTurnCancelActionId as resolveSlackTurnCancelActionId2 } from "@cuylabs/channel-slack/turn-controls";
14
15
 
15
16
  // src/assistant/options.ts
16
17
  var DEFAULT_INITIAL_REPLY = "Hi, how can I help?";
@@ -239,6 +240,11 @@ function createAssistantSink(params) {
239
240
  return stream;
240
241
  }
241
242
  return {
243
+ artifactClient: params.client,
244
+ artifactTarget: {
245
+ channelId: params.channel,
246
+ threadTs: params.threadTs
247
+ },
242
248
  async postMessage() {
243
249
  throw new Error(
244
250
  "Assistant bridge uses chat-stream mode and does not support postMessage."
@@ -289,7 +295,7 @@ function createAssistantInteractiveResponder(params) {
289
295
 
290
296
  // src/assistant/lifecycle/user-message.ts
291
297
  function createUserMessageHandler(deps) {
292
- const { options, feedbackBlock, sessionStrategy } = deps;
298
+ const { options, feedbackBlock, sessionStrategy, turnControlController } = deps;
293
299
  const taskDisplayMode = options.taskDisplayMode ?? "timeline";
294
300
  const messageFormatter = resolveSlackMessageFormatter(
295
301
  options.formatChatMarkdown ?? {}
@@ -427,6 +433,7 @@ function createUserMessageHandler(deps) {
427
433
  ...options.formatToolTitle ? { formatToolTitle: options.formatToolTitle } : {},
428
434
  ...options.formatToolUpdate ? { formatToolUpdate: options.formatToolUpdate } : {},
429
435
  ...options.formatToolDetails ? { formatToolDetails: options.formatToolDetails } : {},
436
+ ...options.formatToolResultOutput ? { formatToolResultOutput: options.formatToolResultOutput } : {},
430
437
  ...options.formatToolError ? { formatToolError: options.formatToolError } : {},
431
438
  ...options.formatReasoningUpdate ? { formatReasoningUpdate: options.formatReasoningUpdate } : {},
432
439
  ...options.chatStreamBufferSize !== void 0 ? { chatStreamBufferSize: options.chatStreamBufferSize } : {},
@@ -434,6 +441,22 @@ function createUserMessageHandler(deps) {
434
441
  ...options.maxTaskUpdateTextChars !== void 0 ? { maxTaskUpdateTextChars: options.maxTaskUpdateTextChars } : {},
435
442
  ...options.maxTaskUpdateFieldChars !== void 0 ? { maxTaskUpdateFieldChars: options.maxTaskUpdateFieldChars } : {},
436
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
+ } : {},
437
460
  formatMessageText: messageFormatter,
438
461
  onStatusChange: async (label, event) => {
439
462
  const update = await resolveStatusUpdate(
@@ -475,6 +498,17 @@ function createUserMessageHandler(deps) {
475
498
  });
476
499
  const abortController = new AbortController();
477
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");
478
512
  await runWithSlackTurnContext(
479
513
  {
480
514
  slackActivity: parsed,
@@ -511,6 +545,7 @@ function createUserMessageHandler(deps) {
511
545
  const translated = (async function* () {
512
546
  try {
513
547
  for await (const event of events) {
548
+ cancelControl?.sync(event);
514
549
  if (event.type === "error") {
515
550
  translatedError = toError(
516
551
  event.error
@@ -539,6 +574,8 @@ ${formatStreamError(translatedError)}`
539
574
  ${formatStreamError(translatedError)}`
540
575
  };
541
576
  yield { type: "complete" };
577
+ } finally {
578
+ cancelControl?.clear("turn-finished");
542
579
  }
543
580
  })();
544
581
  const finalText = await bridgeAgentEventsToSlack(
@@ -595,6 +632,213 @@ ${formatStreamError(translatedError)}`
595
632
  };
596
633
  }
597
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
+
598
842
  // src/assistant/bridge.ts
599
843
  import { createSlackAssistantThreadContextStore } from "@cuylabs/channel-slack/assistant";
600
844
  function createSlackAssistantBridge(options) {
@@ -612,13 +856,20 @@ function createSlackAssistantBridge(options) {
612
856
  const feedbackConfig = options.feedback === false ? void 0 : options.feedback ?? {};
613
857
  const feedbackBlock = feedbackConfig ? createSlackFeedbackBlock(feedbackConfig) : void 0;
614
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;
615
865
  const threadStarted = createThreadStartedHandler(options);
616
866
  const threadContextChanged = createThreadContextChangedHandler();
617
867
  const threadContextStore = options.threadContextStore ?? createSlackAssistantThreadContextStore();
618
868
  const userMessage = createUserMessageHandler({
619
869
  options,
620
870
  feedbackBlock,
621
- sessionStrategy
871
+ sessionStrategy,
872
+ ...turnControlController ? { turnControlController } : {}
622
873
  });
623
874
  const config = {
624
875
  threadStarted,
@@ -640,13 +891,21 @@ function createSlackAssistantBridge(options) {
640
891
  })
641
892
  });
642
893
  }
894
+ turnControlController?.install(app);
643
895
  }
644
896
  return {
645
897
  assistant,
646
898
  install,
647
- ...feedbackConfig ? { feedbackActionId } : {}
899
+ ...feedbackConfig ? { feedbackActionId } : {},
900
+ ...turnCancelActionId ? { turnCancelActionId } : {}
648
901
  };
649
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
+ }
650
909
 
651
910
  // src/assistant/index.ts
652
911
  import {
@@ -0,0 +1,226 @@
1
+ // src/views/controller.ts
2
+ import { registerSlackViewWorkflow } from "@cuylabs/channel-slack/views";
3
+
4
+ // src/views/state.ts
5
+ function extractSlackAgentViewStateValues(viewOrState) {
6
+ const state = isRecord(viewOrState) ? isRecord(viewOrState.state) ? viewOrState.state : viewOrState : {};
7
+ const rawValues = isRecord(state.values) ? state.values : {};
8
+ const values = {};
9
+ for (const [blockId, rawActions] of Object.entries(rawValues)) {
10
+ if (!isRecord(rawActions)) {
11
+ continue;
12
+ }
13
+ const actions = {};
14
+ for (const [actionId, rawAction] of Object.entries(rawActions)) {
15
+ actions[actionId] = extractSlackAgentViewActionValue(rawAction);
16
+ }
17
+ values[blockId] = actions;
18
+ }
19
+ return values;
20
+ }
21
+ function readSlackAgentViewStateValue(valuesOrView, blockId, actionId) {
22
+ const values = looksLikeExtractedStateValues(valuesOrView) ? valuesOrView : extractSlackAgentViewStateValues(valuesOrView);
23
+ return values[blockId]?.[actionId];
24
+ }
25
+ function extractSlackAgentViewActionValue(rawAction) {
26
+ if (!isRecord(rawAction)) {
27
+ return void 0;
28
+ }
29
+ if ("value" in rawAction && typeof rawAction.value !== "undefined") {
30
+ return normalizeValue(rawAction.value);
31
+ }
32
+ if (isRecord(rawAction.selected_option)) {
33
+ return readOptionValue(rawAction.selected_option);
34
+ }
35
+ if (Array.isArray(rawAction.selected_options)) {
36
+ return rawAction.selected_options.map(readOptionValue).filter(isString);
37
+ }
38
+ for (const field of [
39
+ "selected_user",
40
+ "selected_users",
41
+ "selected_channel",
42
+ "selected_channels",
43
+ "selected_conversation",
44
+ "selected_conversations",
45
+ "selected_date",
46
+ "selected_time",
47
+ "selected_date_time"
48
+ ]) {
49
+ const value = rawAction[field];
50
+ if (typeof value !== "undefined") {
51
+ return normalizeValue(value);
52
+ }
53
+ }
54
+ if (typeof rawAction.checked === "boolean") {
55
+ return rawAction.checked;
56
+ }
57
+ return rawAction;
58
+ }
59
+ function readOptionValue(option) {
60
+ if (!isRecord(option)) {
61
+ return "";
62
+ }
63
+ if (typeof option.value === "string") {
64
+ return option.value;
65
+ }
66
+ const text = option.text;
67
+ if (isRecord(text) && typeof text.text === "string") {
68
+ return text.text;
69
+ }
70
+ return "";
71
+ }
72
+ function normalizeValue(value) {
73
+ if (typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
74
+ return value;
75
+ }
76
+ if (Array.isArray(value)) {
77
+ return value.filter(isString);
78
+ }
79
+ return isRecord(value) ? value : void 0;
80
+ }
81
+ function looksLikeExtractedStateValues(value) {
82
+ if (!isRecord(value) || "state" in value || "values" in value) {
83
+ return false;
84
+ }
85
+ return Object.values(value).every(isRecord);
86
+ }
87
+ function isRecord(value) {
88
+ return typeof value === "object" && value !== null;
89
+ }
90
+ function isString(value) {
91
+ return typeof value === "string";
92
+ }
93
+
94
+ // src/views/controller.ts
95
+ var DEFAULT_NAMESPACE = "agent_slack_view";
96
+ var installedCallbackIds = /* @__PURE__ */ new WeakMap();
97
+ function createSlackAgentViewWorkflowController({
98
+ namespace = DEFAULT_NAMESPACE,
99
+ workflows = [],
100
+ onError
101
+ } = {}) {
102
+ const normalizedNamespace = normalizeIdentifier(namespace, "namespace");
103
+ const registeredWorkflows = /* @__PURE__ */ new Map();
104
+ const installedApps = /* @__PURE__ */ new Set();
105
+ function register(workflow) {
106
+ const workflowId = normalizeIdentifier(workflow.id, "workflow id");
107
+ if (!workflow.onSubmission && !workflow.onClose) {
108
+ throw new Error(
109
+ `Slack agent view workflow '${workflowId}' must define onSubmission or onClose.`
110
+ );
111
+ }
112
+ if (registeredWorkflows.has(workflowId)) {
113
+ throw new Error(`Duplicate Slack agent view workflow id: ${workflowId}`);
114
+ }
115
+ const callbackId = normalizeOptionalIdentifier(workflow.callbackId) ?? callbackIdFromWorkflowId(normalizedNamespace, workflowId);
116
+ if ([...registeredWorkflows.values()].some(
117
+ (item) => item.callbackId === callbackId
118
+ )) {
119
+ throw new Error(`Duplicate Slack view callback id: ${callbackId}`);
120
+ }
121
+ const registered = {
122
+ ...workflow,
123
+ id: workflowId,
124
+ callbackId
125
+ };
126
+ registeredWorkflows.set(workflowId, registered);
127
+ for (const app of installedApps) {
128
+ installWorkflow(app, registered);
129
+ }
130
+ return callbackId;
131
+ }
132
+ function callbackIdFor(workflowId) {
133
+ const normalizedWorkflowId = normalizeIdentifier(workflowId, "workflow id");
134
+ return registeredWorkflows.get(normalizedWorkflowId)?.callbackId ?? callbackIdFromWorkflowId(normalizedNamespace, normalizedWorkflowId);
135
+ }
136
+ function list() {
137
+ return [...registeredWorkflows.values()].map(({ id, callbackId }) => ({
138
+ id,
139
+ callbackId
140
+ }));
141
+ }
142
+ function install(app) {
143
+ installedApps.add(app);
144
+ for (const workflow of registeredWorkflows.values()) {
145
+ installWorkflow(app, workflow);
146
+ }
147
+ }
148
+ function installWorkflow(app, workflow) {
149
+ const callbackIds = callbackIdsForApp(app);
150
+ if (callbackIds.has(workflow.callbackId)) {
151
+ throw new Error(
152
+ `Slack view callback id '${workflow.callbackId}' is already installed on this Bolt app.`
153
+ );
154
+ }
155
+ callbackIds.add(workflow.callbackId);
156
+ registerSlackViewWorkflow({
157
+ boltApp: app,
158
+ callbackId: workflow.callbackId,
159
+ ...workflow.decodePrivateMetadata ? { decodePrivateMetadata: workflow.decodePrivateMetadata } : {},
160
+ ...workflow.onSubmission ? {
161
+ onSubmission: async (context) => await workflow.onSubmission(enrichContext(workflow, context))
162
+ } : {},
163
+ ...workflow.onClose ? {
164
+ onClose: async (context) => await workflow.onClose(enrichContext(workflow, context))
165
+ } : {},
166
+ onError: async (error, context) => {
167
+ const enriched = enrichContext(workflow, {
168
+ ...context,
169
+ metadata: context.metadata
170
+ });
171
+ if (workflow.onError) {
172
+ await workflow.onError(error, enriched);
173
+ return;
174
+ }
175
+ await onError?.(error, enriched);
176
+ }
177
+ });
178
+ }
179
+ for (const workflow of workflows) {
180
+ register(workflow);
181
+ }
182
+ return {
183
+ namespace: normalizedNamespace,
184
+ register,
185
+ callbackIdFor,
186
+ list,
187
+ install
188
+ };
189
+ }
190
+ function enrichContext(workflow, context) {
191
+ const stateValues = extractSlackAgentViewStateValues(context.view);
192
+ return {
193
+ ...context,
194
+ workflow: { id: workflow.id, callbackId: workflow.callbackId },
195
+ stateValues,
196
+ getStateValue: (blockId, actionId) => readSlackAgentViewStateValue(stateValues, blockId, actionId)
197
+ };
198
+ }
199
+ function callbackIdsForApp(app) {
200
+ let callbackIds = installedCallbackIds.get(app);
201
+ if (!callbackIds) {
202
+ callbackIds = /* @__PURE__ */ new Set();
203
+ installedCallbackIds.set(app, callbackIds);
204
+ }
205
+ return callbackIds;
206
+ }
207
+ function callbackIdFromWorkflowId(namespace, workflowId) {
208
+ return `${namespace}_${workflowId}`;
209
+ }
210
+ function normalizeOptionalIdentifier(value) {
211
+ const normalized = value?.trim();
212
+ return normalized ? normalized : void 0;
213
+ }
214
+ function normalizeIdentifier(value, label) {
215
+ const normalized = value.trim().replace(/[^A-Za-z0-9_-]+/g, "_").replace(/^_+|_+$/g, "");
216
+ if (!normalized) {
217
+ throw new Error(`Slack agent view workflow ${label} cannot be empty.`);
218
+ }
219
+ return normalized;
220
+ }
221
+
222
+ export {
223
+ extractSlackAgentViewStateValues,
224
+ readSlackAgentViewStateValue,
225
+ createSlackAgentViewWorkflowController
226
+ };
@@ -2,7 +2,7 @@ import {
2
2
  UnsupportedSlackInteractiveRequestError,
3
3
  bridgeAgentEventsToSlack,
4
4
  resolveSlackEventBridgeOptions
5
- } from "./chunk-NOVWLAVP.js";
5
+ } from "./chunk-TMADMHBN.js";
6
6
 
7
7
  // src/adapter/adapter.ts
8
8
  import { withinScope } from "@cuylabs/agent-core";
@@ -73,6 +73,11 @@ function buildThreadPayload(info, respondInThread) {
73
73
  function buildResponseSink(say, client, info, respondInThread, chatStreamStartArgs, sayStream) {
74
74
  const threadPayload = buildThreadPayload(info, respondInThread);
75
75
  return {
76
+ artifactClient: client,
77
+ artifactTarget: {
78
+ channelId: info.channelId,
79
+ ...threadPayload.thread_ts ? { threadTs: threadPayload.thread_ts } : {}
80
+ },
76
81
  async postMessage(text) {
77
82
  const result = await say({ text, ...threadPayload });
78
83
  const response = result;
@@ -167,6 +172,7 @@ function createSlackChannelAdapter(options) {
167
172
  formatToolTitle: options.formatToolTitle,
168
173
  formatToolUpdate: options.formatToolUpdate,
169
174
  formatToolDetails: options.formatToolDetails,
175
+ formatToolResultOutput: options.formatToolResultOutput,
170
176
  formatToolError: options.formatToolError,
171
177
  formatReasoningUpdate: options.formatReasoningUpdate,
172
178
  interactiveMode: options.interactiveMode,
@@ -180,7 +186,13 @@ function createSlackChannelAdapter(options) {
180
186
  maxTaskUpdates: options.maxTaskUpdates,
181
187
  maxTaskUpdateTextChars: options.maxTaskUpdateTextChars,
182
188
  maxTaskUpdateFieldChars: options.maxTaskUpdateFieldChars,
183
- chatStreamFinalArgs: options.chatStreamFinalArgs
189
+ chatStreamFinalArgs: options.chatStreamFinalArgs,
190
+ publishFinalResponseArtifact: options.publishFinalResponseArtifact,
191
+ finalResponseArtifactMode: options.finalResponseArtifactMode,
192
+ finalResponseArtifactStreamThreshold: options.finalResponseArtifactStreamThreshold,
193
+ formatFinalResponseArtifactContinuationNotice: options.formatFinalResponseArtifactContinuationNotice,
194
+ formatFinalResponseArtifactMessage: options.formatFinalResponseArtifactMessage,
195
+ onFinalResponseArtifactError: options.onFinalResponseArtifactError
184
196
  }
185
197
  );
186
198
  const timeout = options.timeout ?? 12e4;