@cuylabs/channel-slack 0.10.0 → 0.12.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 (42) hide show
  1. package/README.md +36 -3
  2. package/dist/adapter/index.d.ts +53 -0
  3. package/dist/adapter/index.js +13 -0
  4. package/dist/app-surface.d.ts +86 -0
  5. package/dist/app-surface.js +15 -0
  6. package/dist/app.d.ts +58 -0
  7. package/dist/app.js +86 -0
  8. package/dist/artifacts/index.d.ts +57 -3
  9. package/dist/artifacts/index.js +88 -0
  10. package/dist/assistant/index.d.ts +18 -53
  11. package/dist/assistant/index.js +15 -184
  12. package/dist/bolt-app-BM0tiL7c.d.ts +49 -0
  13. package/dist/{chunk-TWJGVDA2.js → chunk-37RN2YUI.js} +88 -1
  14. package/dist/chunk-LFQCINHI.js +187 -0
  15. package/dist/chunk-Q6YX7HHK.js +1062 -0
  16. package/dist/chunk-RHOIVQLD.js +127 -0
  17. package/dist/chunk-RTDLIYEE.js +446 -0
  18. package/dist/core.d.ts +5 -201
  19. package/dist/core.js +10 -12
  20. package/dist/feedback/index.d.ts +2 -2
  21. package/dist/feedback/index.js +5 -120
  22. package/dist/formatting-C-kwQseI.d.ts +25 -0
  23. package/dist/index.d.ts +6 -3
  24. package/dist/index.js +10 -12
  25. package/dist/interactive/index.d.ts +68 -4
  26. package/dist/interactive/index.js +432 -0
  27. package/dist/options-B0xQCaez.d.ts +221 -0
  28. package/dist/options-DQacQDmD.d.ts +368 -0
  29. package/dist/runtime/index.d.ts +6 -220
  30. package/dist/socket.d.ts +142 -0
  31. package/dist/socket.js +77 -0
  32. package/dist/transports/index.d.ts +2 -1
  33. package/dist/transports/socket/index.d.ts +4 -49
  34. package/dist/turn-BGAXddH_.d.ts +178 -0
  35. package/dist/types-wLZzyI9r.d.ts +375 -0
  36. package/docs/README.md +1 -0
  37. package/docs/concepts/interactive-requests.md +85 -0
  38. package/docs/reference/channel-slack-boundary.md +6 -3
  39. package/docs/reference/exports.md +6 -2
  40. package/docs/reference/source-layout.md +1 -0
  41. package/package.json +23 -3
  42. package/dist/chunk-ISOMBQXE.js +0 -89
@@ -0,0 +1,375 @@
1
+ import { S as SlackActivityInfo, b as SlackUserIdentity } from './activity-ByrD9Ftr.js';
2
+ import { k as SlackTurnRequestContext, S as SlackAssistantStatusUpdate, g as SlackChatStreamStartArgs, j as SlackTurnPreparation } from './turn-BGAXddH_.js';
3
+ import { h as SlackInteractiveRequestHandler } from './interactive-CbKYkkc_.js';
4
+ import { o as SlackTurnSource, i as SlackTurnEvent, S as SlackEventBridgeOptions } from './options-B0xQCaez.js';
5
+ import { L as Logger } from './logging-Bl3HfcC8.js';
6
+
7
+ /**
8
+ * Channel-options + adapter contract for the direct Slack adapter
9
+ * (`createSlackChannelAdapter`).
10
+ */
11
+
12
+ type HumanInputEvent = Extract<SlackTurnEvent, {
13
+ type: "human-input-request";
14
+ }>["request"];
15
+ type SlackToolStartEvent = Extract<SlackTurnEvent, {
16
+ type: "tool-start";
17
+ }>;
18
+ type SlackToolResultEvent = Extract<SlackTurnEvent, {
19
+ type: "tool-result";
20
+ }>;
21
+ type MaybePromise<T> = T | Promise<T>;
22
+ /**
23
+ * Session mapping strategy.
24
+ *
25
+ * - `"thread-aware"` *(default)* — Uses the Slack thread_ts as session ID
26
+ * when the message is inside a thread; falls back to the channel ID for
27
+ * DMs and top-level channel messages. Produces the most natural grouping.
28
+ *
29
+ * - `"channel-id"` — Uses the Slack channel / DM ID directly. Every
30
+ * message in the same channel/DM shares one session.
31
+ *
32
+ * - `"user-per-channel"` — Uses `${channelId}:${userId}`. Each user gets
33
+ * an isolated session per channel/DM. Useful for private-context bots.
34
+ *
35
+ * - `"user-per-thread"` — Uses `${channelId}:${threadTs}:${userId}` for
36
+ * threaded/channel messages and `${channelId}:${userId}` in DMs.
37
+ *
38
+ * - `"custom"` — Provide your own mapping via `resolveSessionId`.
39
+ */
40
+ type SlackSessionStrategy = "thread-aware" | "channel-id" | "user-per-channel" | "user-per-thread" | "custom";
41
+ /**
42
+ * Controls how the bot posts responses to Slack.
43
+ *
44
+ * - `"progressive"` *(default)* — Posts a placeholder message immediately,
45
+ * then updates it as the agent emits text. Gives a typing-like experience.
46
+ * Uses `chat.update` for mid-stream updates.
47
+ *
48
+ * - `"accumulate"` — Waits for the full response before posting. Simpler,
49
+ * uses one API call, but the user sees nothing until the agent finishes.
50
+ *
51
+ * - `"chat-stream"` — Uses Slack's native `chat.startStream`,
52
+ * `chat.appendStream`, and `chat.stopStream` APIs through
53
+ * `WebClient.chatStream`. This is the Slack-native streaming path.
54
+ */
55
+ type SlackStreamingMode = "progressive" | "accumulate" | "chat-stream";
56
+ /**
57
+ * Configuration for the Slack channel adapter.
58
+ */
59
+ interface SlackChannelOptions {
60
+ /**
61
+ * Generic chat turn source understood by this package.
62
+ */
63
+ source: SlackTurnSource;
64
+ /**
65
+ * How to map Slack conversations to runtime session IDs.
66
+ * @default "thread-aware"
67
+ */
68
+ sessionStrategy?: SlackSessionStrategy;
69
+ /**
70
+ * Custom session ID resolver. Required when `sessionStrategy` is `"custom"`.
71
+ */
72
+ resolveSessionId?: (info: SlackActivityInfo) => string | Promise<string>;
73
+ /**
74
+ * Optional full-request session resolver. Returning `undefined` falls back
75
+ * to `sessionStrategy` / `resolveSessionId`.
76
+ *
77
+ * Prefer this over `resolveSessionId` when session routing needs auth,
78
+ * prepared text, or other request metadata.
79
+ */
80
+ resolveSession?: (request: SlackTurnRequestContext) => string | undefined | Promise<string | undefined>;
81
+ /**
82
+ * Surface reasoning events as status updates in Slack.
83
+ * @default false
84
+ */
85
+ showReasoning?: boolean;
86
+ /**
87
+ * Surface tool usage as status updates (e.g. "Using tool: search...").
88
+ * This controls root-agent tool rows. Subagent child tool rows are controlled
89
+ * separately by `showSubagentToolUsage`.
90
+ * @default true
91
+ */
92
+ showToolUsage?: boolean;
93
+ /**
94
+ * Surface subagent child tool activity as Slack task rows. Disabled by
95
+ * default so delegated workers do not flood the parent turn timeline.
96
+ * Subagent start/complete/error lifecycle rows still render when
97
+ * `showToolUsage` is enabled.
98
+ * @default false
99
+ */
100
+ showSubagentToolUsage?: boolean;
101
+ /**
102
+ * Include the completed subagent output body in the subagent task row.
103
+ * Disabled by default because full child results should flow through
104
+ * `get_agent_results` and the root final answer, not the task timeline.
105
+ * @default false
106
+ */
107
+ showSubagentResultInTask?: boolean;
108
+ /**
109
+ * Format the tool-start status text.
110
+ * @default (toolName) => `🔍 Using tool: ${toolName}...`
111
+ */
112
+ formatToolUpdate?: (toolName: string) => string;
113
+ /**
114
+ * Format the title shown in Slack task cards for tool activity.
115
+ * @default (toolName) => toolName
116
+ */
117
+ formatToolTitle?: (toolName: string) => string;
118
+ /**
119
+ * Format task-card details for a tool-start event. Use this to show safe,
120
+ * redacted tool arguments in Slack's task UI.
121
+ * @default details use the tool-start status text
122
+ */
123
+ formatToolDetails?: (event: SlackToolStartEvent) => string | undefined;
124
+ /**
125
+ * Format completed tool output shown in Slack task cards.
126
+ *
127
+ * Return `undefined` to use the default generic formatter. Return `null` to
128
+ * intentionally omit output for this result.
129
+ */
130
+ formatToolResultOutput?: (event: SlackToolResultEvent) => string | null | undefined;
131
+ /**
132
+ * Format the tool-error status text.
133
+ * @default (toolName) => `⚠️ Tool ${toolName} encountered an error`
134
+ */
135
+ formatToolError?: (toolName: string, error: string) => string;
136
+ /**
137
+ * Format the reasoning status text.
138
+ * @default () => "Thinking..."
139
+ */
140
+ formatReasoningUpdate?: () => string;
141
+ /**
142
+ * Initial Slack thread status set before `prepareTurn` and `source.chat()`
143
+ * run, when Bolt provides `setStatus` for the classic message/app_mention
144
+ * listener. Use an object with `loading_messages` to enable Slack's rotating
145
+ * loading text.
146
+ *
147
+ * Return `undefined` from a function resolver to skip the initial status for
148
+ * a specific turn.
149
+ *
150
+ * @default { status: "Thinking..." }
151
+ */
152
+ initialStatus?: SlackAssistantStatusUpdate | ((request: SlackTurnRequestContext) => MaybePromise<SlackAssistantStatusUpdate | undefined>);
153
+ /**
154
+ * How to handle approval / human-input requests that Slack cannot satisfy
155
+ * in-channel. When `handleInteractiveRequest` is configured, the handler is
156
+ * tried first; this fallback is only used when the handler declines.
157
+ * @default "message-and-error"
158
+ */
159
+ interactiveMode?: "message-and-error" | "ignore";
160
+ /**
161
+ * Slack-native renderer/resolver hook for approval and human-input requests.
162
+ *
163
+ * The adapter provides the current Slack activity, resolved session, user,
164
+ * and a responder that can post/update Block Kit messages in the originating
165
+ * thread. Return `true` after rendering/recording the request to keep the
166
+ * Slack turn alive while the request is resolved out-of-band.
167
+ */
168
+ handleInteractiveRequest?: SlackInteractiveRequestHandler;
169
+ /**
170
+ * Format the fallback message shown when tool approval is required.
171
+ */
172
+ formatApprovalRequired?: (request: Extract<SlackTurnEvent, {
173
+ type: "approval-request";
174
+ }>["request"]) => string;
175
+ /**
176
+ * Format the fallback message shown when human input is required.
177
+ */
178
+ formatHumanInputRequired?: (request: HumanInputEvent) => string;
179
+ /**
180
+ * How the bot posts responses.
181
+ * @default "progressive"
182
+ */
183
+ streamingMode?: SlackStreamingMode;
184
+ /**
185
+ * Minimum number of accumulated characters before issuing a `chat.update`
186
+ * in progressive mode.
187
+ * @default 150
188
+ */
189
+ progressiveUpdateThreshold?: number;
190
+ /**
191
+ * Minimum time between `chat.update` calls in progressive mode.
192
+ *
193
+ * Slack recommends waiting at least 3 seconds between updates to longer
194
+ * messages. Set to `0` to disable time throttling.
195
+ *
196
+ * @default 3000
197
+ */
198
+ progressiveUpdateIntervalMs?: number;
199
+ /**
200
+ * Buffer size passed to Slack's `WebClient.chatStream` helper when
201
+ * `streamingMode` is `"chat-stream"`.
202
+ * @default 256
203
+ */
204
+ chatStreamBufferSize?: number;
205
+ /**
206
+ * Maximum number of Slack task-update chunks the bridge will append to a
207
+ * single chat stream. Task rows are a bounded UI projection, not a durable
208
+ * event log.
209
+ *
210
+ * @default 200
211
+ */
212
+ maxTaskUpdates?: number;
213
+ /**
214
+ * Maximum cumulative characters from task titles/details/output that the
215
+ * bridge will append to one chat stream.
216
+ *
217
+ * @default 20000
218
+ */
219
+ maxTaskUpdateTextChars?: number;
220
+ /**
221
+ * Maximum characters for a single task details/output field.
222
+ *
223
+ * @default 500
224
+ */
225
+ maxTaskUpdateFieldChars?: number;
226
+ /**
227
+ * Start arguments merged into Slack's native chat stream when
228
+ * `streamingMode` is `"chat-stream"`; useful for Slack-supported authorship
229
+ * fields such as `icon_emoji`, `icon_url`, and `username`.
230
+ *
231
+ * The adapter still owns `channel`, `thread_ts`, recipient ids, and
232
+ * `buffer_size`.
233
+ */
234
+ chatStreamStartArgs?: SlackChatStreamStartArgs;
235
+ /**
236
+ * Final arguments merged into `chatStream.stop(...)` when
237
+ * `streamingMode` is `"chat-stream"`; useful for final blocks such as a
238
+ * feedback widget.
239
+ */
240
+ chatStreamFinalArgs?: {
241
+ blocks?: unknown[];
242
+ } & Record<string, unknown>;
243
+ /**
244
+ * Optional publisher for rich artifacts derived from the final accumulated
245
+ * answer, such as creating a Slack Canvas for long responses.
246
+ */
247
+ publishFinalResponseArtifact?: SlackEventBridgeOptions["publishFinalResponseArtifact"];
248
+ /**
249
+ * Controls whether final-response artifacts are supplemental or replace long
250
+ * Slack text with a compact artifact pointer.
251
+ *
252
+ * @default "supplemental"
253
+ */
254
+ finalResponseArtifactMode?: SlackEventBridgeOptions["finalResponseArtifactMode"];
255
+ /**
256
+ * Raw-character threshold for replacement-mode streaming suppression.
257
+ *
258
+ * @default 4000
259
+ */
260
+ finalResponseArtifactStreamThreshold?: SlackEventBridgeOptions["finalResponseArtifactStreamThreshold"];
261
+ /** Notice emitted when replacement mode moves the remaining response to an artifact. */
262
+ formatFinalResponseArtifactContinuationNotice?: SlackEventBridgeOptions["formatFinalResponseArtifactContinuationNotice"];
263
+ /** Compact final Slack message emitted after artifact publication succeeds. */
264
+ formatFinalResponseArtifactMessage?: SlackEventBridgeOptions["formatFinalResponseArtifactMessage"];
265
+ /** Diagnostics hook for final-response artifact publishing failures. */
266
+ onFinalResponseArtifactError?: SlackEventBridgeOptions["onFinalResponseArtifactError"];
267
+ /**
268
+ * Convert common markdown constructs emitted by models into Slack mrkdwn.
269
+ *
270
+ * @default true
271
+ */
272
+ formatChatMarkdown?: boolean;
273
+ /**
274
+ * Custom response text formatter. When provided, this takes precedence over
275
+ * the built-in markdown-to-mrkdwn conversion.
276
+ */
277
+ formatMessageText?: (text: string) => string;
278
+ /**
279
+ * Maximum time (ms) to wait for the agent before aborting.
280
+ * @default 120_000
281
+ */
282
+ timeout?: number;
283
+ /**
284
+ * When `true` (default), replies to channel messages are posted inside the
285
+ * originating thread. When `false`, replies are posted at channel level.
286
+ * Has no effect in DMs.
287
+ * @default true
288
+ */
289
+ respondInThread?: boolean;
290
+ /**
291
+ * Welcome message sent when the bot is added to a channel or DM.
292
+ * Set to `null` to disable.
293
+ */
294
+ welcomeMessage?: string | null;
295
+ /**
296
+ * When `true` (default), `app.event('app_mention')` is registered so the
297
+ * bot responds to `@mentions` in channels.
298
+ * @default true
299
+ */
300
+ respondToMentions?: boolean;
301
+ /**
302
+ * When `true` (default), `app.message()` is registered so the bot
303
+ * can respond to direct messages and, when enabled, plain channel messages.
304
+ *
305
+ * Set to `false` if you only want @mention-triggered responses.
306
+ * @default true
307
+ */
308
+ respondToMessages?: boolean;
309
+ /**
310
+ * When `true`, plain channel/group/thread messages from `app.message()` are
311
+ * forwarded into the agent. Direct messages still work through
312
+ * `respondToMessages`.
313
+ *
314
+ * Keep this disabled unless your Slack app intentionally listens to every
315
+ * visible channel message. @mentions are handled separately by
316
+ * `respondToMentions`.
317
+ * @default false
318
+ */
319
+ respondToChannelMessages?: boolean;
320
+ /**
321
+ * Resolve additional context to inject into the agent call.
322
+ *
323
+ * Use this to customize the system prompt per user, inject Slack identity,
324
+ * etc. Called before `source.chat()` on every message.
325
+ */
326
+ resolveUserContext?: (user: SlackUserIdentity) => {
327
+ system?: string;
328
+ } | Promise<{
329
+ system?: string;
330
+ }>;
331
+ /**
332
+ * Prepare an Slack turn from the full Slack request context.
333
+ *
334
+ * Prefer this over `resolveUserContext` when you need structured ambient
335
+ * context for tools/middleware or custom scope attributes.
336
+ */
337
+ prepareTurn?: (request: SlackTurnRequestContext) => SlackTurnPreparation | Promise<SlackTurnPreparation>;
338
+ /**
339
+ * Resolve the text message forwarded to `source.chat()`.
340
+ *
341
+ * Useful when the message text needs normalisation beyond mention-stripping.
342
+ * Return `undefined` to skip processing this message.
343
+ */
344
+ resolveMessage?: (info: SlackActivityInfo) => string | undefined | Promise<string | undefined>;
345
+ /**
346
+ * Called on unhandled errors during turn processing.
347
+ */
348
+ onError?: (error: Error, info: SlackActivityInfo) => void | Promise<void>;
349
+ /**
350
+ * Optional diagnostics logger. Used for best-effort status/update failures
351
+ * and classic turn utility visibility.
352
+ */
353
+ logger?: Logger;
354
+ }
355
+ /**
356
+ * Slack channel adapter returned by `createSlackChannelAdapter`.
357
+ */
358
+ interface SlackChannelAdapter {
359
+ /**
360
+ * Register all message/event handlers on a Bolt `App`.
361
+ *
362
+ * ```ts
363
+ * const adapter = createSlackChannelAdapter({ source });
364
+ * adapter.mount(boltApp);
365
+ * ```
366
+ */
367
+ mount(app: {
368
+ message: (...args: any[]) => unknown;
369
+ event: (...args: any[]) => unknown;
370
+ }): void;
371
+ /** Resolve or look up the session ID for a given Slack activity. */
372
+ getSessionId(info: SlackActivityInfo): string | Promise<string>;
373
+ }
374
+
375
+ export type { SlackChannelAdapter as S, SlackChannelOptions as a, SlackSessionStrategy as b, SlackStreamingMode as c, SlackToolStartEvent as d };
package/docs/README.md CHANGED
@@ -15,6 +15,7 @@ your adapter or application needs.
15
15
  - [Activity parsing](concepts/activity.md)
16
16
  - [Artifacts](concepts/artifacts.md)
17
17
  - [Entrypoints](concepts/entrypoints.md)
18
+ - [Interactive requests](concepts/interactive-requests.md)
18
19
  - [Message policy](concepts/message-policy.md)
19
20
  - [Setup requirements](concepts/setup-requirements.md)
20
21
  - [Supplemental history](concepts/supplemental-history.md)
@@ -0,0 +1,85 @@
1
+ # Interactive Requests
2
+
3
+ Slack interactive requests render approval and human-input requests as Slack
4
+ buttons and modals. The controller is runtime-neutral; applications adapt their
5
+ runtime's request shapes into the generic Slack request types exported from
6
+ `@cuylabs/channel-slack/interactive`.
7
+
8
+ Use this import path for both generic Slack runtimes and runtime adapters such
9
+ as `@cuylabs/channel-slack-agent-core`:
10
+
11
+ ```typescript
12
+ import {
13
+ createPostgresSlackInteractiveRequestStore,
14
+ createSlackInteractiveController,
15
+ } from "@cuylabs/channel-slack/interactive";
16
+ import { mountSlackAppSocket } from "@cuylabs/channel-slack/socket";
17
+
18
+ const interactiveStore = createPostgresSlackInteractiveRequestStore({
19
+ connectionString: process.env.DATABASE_URL,
20
+ schema: "agent",
21
+ });
22
+
23
+ const interactive = createSlackInteractiveController({
24
+ store: interactiveStore,
25
+ namespace: "my_agent_slack",
26
+ requestTimeoutMs: 5 * 60 * 1000,
27
+ });
28
+
29
+ await mountSlackAppSocket({
30
+ source,
31
+ interactive,
32
+ appToken: process.env.SLACK_APP_TOKEN,
33
+ botToken: process.env.SLACK_BOT_TOKEN,
34
+ });
35
+ ```
36
+
37
+ `mountSlackApp`, `mountSlackAppSocket`, and `installSlackAppSurface` install the
38
+ controller on the Bolt app and route approval/human-input events to
39
+ `handleInteractiveRequest`. If you manually compose lower-level Bolt helpers,
40
+ call `interactive.install(boltApp)` and pass
41
+ `interactive.handleInteractiveRequest` to the Slack runtime or adapter options.
42
+
43
+ ## Runtime Hooks
44
+
45
+ The controller exposes request hooks for runtimes that own approval and
46
+ human-input decisions:
47
+
48
+ ```typescript
49
+ const approval = interactive.approval.onRequest;
50
+ const humanInput = interactive.humanInput.onRequest;
51
+ ```
52
+
53
+ Use those hooks at the runtime boundary where your agent pauses for an approval
54
+ or human response. The Slack package deliberately keeps the request shapes
55
+ generic; runtime-specific adapters should map their event/request types before
56
+ calling the controller.
57
+
58
+ Request IDs must be unique across pending approval and human-input requests.
59
+ The controller rejects reuse of the same ID for a different request kind so one
60
+ Slack button cannot resolve the wrong runtime wait.
61
+
62
+ ## Resolution Behavior
63
+
64
+ The controller persists pending requests before rendering Slack buttons. Once a
65
+ request is resolved, duplicate button clicks return the stored first-wins
66
+ resolution and do not overwrite it. Resolved records are not rendered again if
67
+ the same event is replayed.
68
+
69
+ By default, only the Slack user who triggered the original turn can resolve the
70
+ request. Pass `authorize(record, actor)` to allow delegated approvers, channel
71
+ owners, or workspace-admin policy.
72
+
73
+ ## Store Choices
74
+
75
+ - `createInMemorySlackInteractiveRequestStore` is useful for tests and local
76
+ single-process apps.
77
+ - `createPostgresSlackInteractiveRequestStore` persists pending requests across
78
+ restarts and lets multiple Slack workers resolve the same request safely.
79
+
80
+ The Postgres store uses one table keyed by request ID. `resolve(...)` is
81
+ idempotent: the first resolution wins, and later duplicate button clicks return
82
+ the original resolution without overwriting it.
83
+
84
+ Call `close()` during shutdown when the store owns its `pg` pool. Call
85
+ `prune()` manually when you disable the background prune timer.
@@ -25,8 +25,8 @@ contracts.
25
25
  - Target parsing and resolution.
26
26
  - Feedback blocks and action handler.
27
27
  - Slack response sink and chat stream contracts for runtime adapters.
28
- - Slack interactive request Block Kit/modal builders and in-memory/Postgres
29
- stores.
28
+ - Slack interactive request Block Kit/modal builders, in-memory/Postgres stores,
29
+ and controller.
30
30
  - Slack message policy resolver and in-memory/Postgres state stores.
31
31
  - Supplemental history reader, context loader, and visibility policy.
32
32
  - Assistant message parser and thread-context store.
@@ -37,7 +37,10 @@ contracts.
37
37
  - Agent-runtime scopes and context-fragment middleware.
38
38
  - Runtime-specific mapping from Slack entrypoints into an agent turn.
39
39
  - Agent event stream rendering and runtime-specific response orchestration.
40
- - Agent-specific approval and human-input controllers.
40
+ - Runtime-specific mapping from approval and human-input requests into the
41
+ generic Slack interactive request shapes.
42
+ - Runtime-specific wiring of `approval.onRequest` and `humanInput.onRequest`
43
+ into an agent runtime.
41
44
  - Product prompts, tools, audit policy, and deployment policy.
42
45
 
43
46
  Those pieces belong in runtime-specific adapters or product applications.
@@ -5,12 +5,16 @@ keep application code close to the package boundary it uses.
5
5
 
6
6
  | Export | Depends on | Notes |
7
7
  | ------------------------------------------ | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
8
+ | `@cuylabs/channel-slack/app` | `@slack/bolt`, `express` | Runtime-neutral HTTP Events API mount for Slack surfaces |
9
+ | `@cuylabs/channel-slack/app-surface` | `@slack/bolt`, `@slack/web-api` types | Runtime-neutral Assistant, mention, DM, feedback, and interactive mount |
10
+ | `@cuylabs/channel-slack/socket` | `@slack/bolt` | Runtime-neutral Socket Mode mount for Slack surfaces |
11
+ | `@cuylabs/channel-slack/adapter` | `@slack/bolt`, `@slack/web-api` types | Runtime-neutral classic app mention, DM, and channel-message adapter |
8
12
  | `@cuylabs/channel-slack/core` | no Slack SDK runtime imports | Transport-neutral parsing, formatting, types, sessions, turn context |
9
13
  | `@cuylabs/channel-slack/policy` | `pg` only when using connection-string Postgres state | Message admission and in-memory/Postgres policy state |
10
14
  | `@cuylabs/channel-slack/history` | `@slack/web-api` types | History reader accepts a Slack WebClient or minimal conversations client |
11
- | `@cuylabs/channel-slack/interactive` | `@slack/types`; `pg` only when using connection-string Postgres storage | Slack interactive Block Kit/modal builders and request stores |
15
+ | `@cuylabs/channel-slack/interactive` | `@slack/bolt`, `@slack/types`; `pg` only when using connection-string storage | Slack interactive controller, Block Kit/modal builders, and stores |
12
16
  | `@cuylabs/channel-slack/app-home` | `@slack/bolt`, `@slack/types` | Slack App Home registration helper |
13
- | `@cuylabs/channel-slack/artifacts` | no Slack SDK runtime imports; requires a Web API-like client at call time | Text, file, image, link, and Canvas artifact publishing |
17
+ | `@cuylabs/channel-slack/artifacts` | no Slack SDK runtime imports; requires a Web API-like client at call time | Text, file, image, link, Canvas, and final-response artifact publishing |
14
18
  | `@cuylabs/channel-slack/auth` | no Slack SDK runtime imports; `pg` is not required | Auth option types, auth resolution, OAuth installation stores |
15
19
  | `@cuylabs/channel-slack/responses` | no Slack SDK runtime imports | Slack response sink and chat stream contracts for runtime adapters |
16
20
  | `@cuylabs/channel-slack/transports` | `@slack/bolt`, `express`; `pg` only when using connection-string Postgres locks | HTTP and Socket Mode transport helpers |
@@ -21,6 +21,7 @@ src/
21
21
  reader.ts Slack history API reader and prompt formatter
22
22
  visibility-policy.ts model-visible history filters
23
23
  inclusion-policy.ts direct-message supplemental-history inclusion policy
24
+ interactive/ approval/human-input controller, blocks, stores
24
25
  policy/
25
26
  message/ message admission and state-store component
26
27
  setup/ scopes, events, manifests, setup inspection
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cuylabs/channel-slack",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Agent-runtime-agnostic Slack channel primitives for AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,6 +21,21 @@
21
21
  "import": "./dist/app-home.js",
22
22
  "default": "./dist/app-home.js"
23
23
  },
24
+ "./adapter": {
25
+ "types": "./dist/adapter/index.d.ts",
26
+ "import": "./dist/adapter/index.js",
27
+ "default": "./dist/adapter/index.js"
28
+ },
29
+ "./app": {
30
+ "types": "./dist/app.d.ts",
31
+ "import": "./dist/app.js",
32
+ "default": "./dist/app.js"
33
+ },
34
+ "./app-surface": {
35
+ "types": "./dist/app-surface.d.ts",
36
+ "import": "./dist/app-surface.js",
37
+ "default": "./dist/app-surface.js"
38
+ },
24
39
  "./artifacts": {
25
40
  "types": "./dist/artifacts/index.d.ts",
26
41
  "import": "./dist/artifacts/index.js",
@@ -81,6 +96,11 @@
81
96
  "import": "./dist/setup/index.js",
82
97
  "default": "./dist/setup/index.js"
83
98
  },
99
+ "./socket": {
100
+ "types": "./dist/socket.d.ts",
101
+ "import": "./dist/socket.js",
102
+ "default": "./dist/socket.js"
103
+ },
84
104
  "./targets": {
85
105
  "types": "./dist/targets/index.d.ts",
86
106
  "import": "./dist/targets/index.js",
@@ -168,9 +188,9 @@
168
188
  "node": ">=20"
169
189
  },
170
190
  "scripts": {
171
- "build": "tsup src/index.ts src/app-home.ts src/artifacts/index.ts src/core.ts src/assistant/index.ts src/auth/index.ts src/diagnostics/index.ts src/entrypoints/index.ts src/feedback/index.ts src/history/index.ts src/interactive/index.ts src/policy/index.ts src/responses/index.ts src/runtime/index.ts src/setup/index.ts src/targets/index.ts src/turn-controls/index.ts src/transports/index.ts src/transports/http/index.ts src/transports/socket/index.ts src/users/index.ts src/views/index.ts --format esm --dts --clean",
191
+ "build": "tsup src/index.ts src/adapter/index.ts src/app.ts src/app-home.ts src/app-surface.ts src/artifacts/index.ts src/core.ts src/assistant/index.ts src/auth/index.ts src/diagnostics/index.ts src/entrypoints/index.ts src/feedback/index.ts src/history/index.ts src/interactive/index.ts src/policy/index.ts src/responses/index.ts src/runtime/index.ts src/setup/index.ts src/socket.ts src/targets/index.ts src/turn-controls/index.ts src/transports/index.ts src/transports/http/index.ts src/transports/socket/index.ts src/users/index.ts src/views/index.ts --format esm --dts --clean",
172
192
  "clean": "rm -rf dist",
173
- "dev": "tsup src/index.ts src/app-home.ts src/artifacts/index.ts src/core.ts src/assistant/index.ts src/auth/index.ts src/diagnostics/index.ts src/entrypoints/index.ts src/feedback/index.ts src/history/index.ts src/interactive/index.ts src/policy/index.ts src/responses/index.ts src/runtime/index.ts src/setup/index.ts src/targets/index.ts src/turn-controls/index.ts src/transports/index.ts src/transports/http/index.ts src/transports/socket/index.ts src/users/index.ts src/views/index.ts --format esm --dts --watch",
193
+ "dev": "tsup src/index.ts src/adapter/index.ts src/app.ts src/app-home.ts src/app-surface.ts src/artifacts/index.ts src/core.ts src/assistant/index.ts src/auth/index.ts src/diagnostics/index.ts src/entrypoints/index.ts src/feedback/index.ts src/history/index.ts src/interactive/index.ts src/policy/index.ts src/responses/index.ts src/runtime/index.ts src/setup/index.ts src/socket.ts src/targets/index.ts src/turn-controls/index.ts src/transports/index.ts src/transports/http/index.ts src/transports/socket/index.ts src/users/index.ts src/views/index.ts --format esm --dts --watch",
174
194
  "lint": "eslint \"src/**/*.{ts,tsx}\" \"tests/**/*.{ts,tsx}\" --max-warnings=0",
175
195
  "test": "vitest run",
176
196
  "test:watch": "vitest",
@@ -1,89 +0,0 @@
1
- // src/shared/turn-context.ts
2
- import { AsyncLocalStorage } from "async_hooks";
3
- var store = new AsyncLocalStorage();
4
- function currentSlackTurnContext() {
5
- return store.getStore();
6
- }
7
- function runWithSlackTurnContext(value, fn) {
8
- return Promise.resolve(store.run({ ...value }, fn));
9
- }
10
-
11
- // src/shared/follow-up.ts
12
- function formatSlackAttributedFollowUp(message) {
13
- const slack = currentSlackTurnContext();
14
- if (!slack) {
15
- return message;
16
- }
17
- const sender = formatSlackSender(slack);
18
- const conversation = formatSlackConversation(slack);
19
- if (!sender && !conversation) {
20
- return message;
21
- }
22
- const lines = ["Additional Slack follow-up for the running request."];
23
- if (sender) {
24
- lines.push(`Sender: ${sender}`);
25
- }
26
- if (conversation) {
27
- lines.push(`Conversation: ${conversation}`);
28
- }
29
- lines.push("Message:", message);
30
- return lines.join("\n");
31
- }
32
- function formatSlackSender(slack) {
33
- const userId = slack.slackActivity.userId || slack.user.userId;
34
- const displayName = readPreparedSlackDisplayName(slack.context) ?? slack.user.userName;
35
- if (displayName && userId) {
36
- return `${displayName} (${userId})`;
37
- }
38
- return displayName ?? userId ?? "unknown Slack user";
39
- }
40
- function formatSlackConversation(slack) {
41
- const activity = slack.slackActivity;
42
- const surface = formatSlackSurface(activity.channelType);
43
- const parts = [
44
- activity.teamId ? `team ${activity.teamId}` : void 0,
45
- activity.channelId ? `channel ${activity.channelId}` : void 0,
46
- activity.threadTs ? `thread ${activity.threadTs}` : activity.messageTs ? `message ${activity.messageTs}` : void 0
47
- ].filter((part) => Boolean(part));
48
- return parts.length > 0 ? `${surface} (${parts.join(", ")})` : surface;
49
- }
50
- function formatSlackSurface(channelType) {
51
- switch (channelType) {
52
- case "dm":
53
- return "private Slack DM";
54
- case "thread":
55
- return "shared Slack thread";
56
- case "channel":
57
- return "shared Slack channel";
58
- case "group":
59
- return "shared Slack group conversation";
60
- default:
61
- return "Slack conversation";
62
- }
63
- }
64
- function readPreparedSlackDisplayName(context) {
65
- const slack = context?.slack;
66
- if (!slack || typeof slack !== "object") {
67
- return void 0;
68
- }
69
- const value = slack.userDisplayName;
70
- return typeof value === "string" && value.length > 0 ? value : void 0;
71
- }
72
-
73
- // src/shared/session.ts
74
- function resolveThreadAwareSlackSessionId(info) {
75
- if (info.channelType === "dm") {
76
- return info.channelId;
77
- }
78
- if (info.threadTs) {
79
- return `${info.channelId}:${info.threadTs}`;
80
- }
81
- return `${info.channelId}:${info.messageTs ?? info.channelId}`;
82
- }
83
-
84
- export {
85
- currentSlackTurnContext,
86
- runWithSlackTurnContext,
87
- formatSlackAttributedFollowUp,
88
- resolveThreadAwareSlackSessionId
89
- };