| 106 | |
| 107 | /** Slack `PlatformAdapter`: ingress via Bolt, egress via Block Kit + streaming. */ |
| 108 | export class SlackAdapter implements PlatformAdapter { |
| 109 | readonly platform = "slack"; |
| 110 | readonly capabilities: SurfaceCapabilities; |
| 111 | readonly ackDeadlineMs = 3000; |
| 112 | |
| 113 | readonly app: App; |
| 114 | client: WebClient; |
| 115 | botUserId = ""; |
| 116 | private readonly store: SlackConversationStore; |
| 117 | private sink: IngressSink | undefined; |
| 118 | /** Per-id cache for sender-profile resolution (repeat turns are cheap). */ |
| 119 | private readonly userCache = new Map<string, PlatformUser>(); |
| 120 | /** Set once the Assistant middleware is attached (when assistant !== false). */ |
| 121 | private assistantHandle: AssistantHandle | undefined; |
| 122 | /** Our team id (from auth.test); native channel streams need it. */ |
| 123 | private teamId: string | undefined; |
| 124 | /** |
| 125 | * In-memory native-streaming health for this workspace. Flipped to false the |
| 126 | * first time `chat.startStream` fails, so subsequent streams skip the native |
| 127 | * path and go straight to the legacy transport. |
| 128 | */ |
| 129 | private nativeStreamingOk = true; |
| 130 | /** |
| 131 | * In-memory health of native structured `task_update` chunks for this |
| 132 | * workspace. Flipped to false the first time a chunk append fails (old |
| 133 | * workspace / missing `assistant:write`), so later turns surface tool |
| 134 | * progress as `:wrench:` rows instead of retrying chunks. |
| 135 | */ |
| 136 | private nativeTaskChunksOk = true; |
| 137 | |
| 138 | constructor(private readonly opts: SlackAdapterOptions) { |
| 139 | const assistantEnabled = opts.assistant !== false; |
| 140 | this.capabilities = { |
| 141 | supportsModals: true, |
| 142 | supportsTyping: false, |
| 143 | supportsReactions: true, |
| 144 | supportsStreaming: true, |
| 145 | supportsEphemeral: true, |
| 146 | maxBlocksPerMessage: 50, |
| 147 | supportsSuggestedPrompts: assistantEnabled, |
| 148 | supportsThreadTitle: assistantEnabled, |
| 149 | }; |
| 150 | this.app = new App({ |
| 151 | token: opts.botToken, |
| 152 | appToken: opts.appToken, |
| 153 | signingSecret: opts.signingSecret, |
| 154 | socketMode: opts.socketMode ?? true, |
| 155 | logLevel: opts.logLevel ?? LogLevel.INFO, |
| 156 | // Without this, Bolt's constructor fires a background auth.test that |
| 157 | // can't be awaited or error-handled (and phones home from unit tests). |
| 158 | // start() owns initialization: app.init() below, then our own awaited |
| 159 | // auth.test — construction stays side-effect-free. |
| 160 | deferInitialization: true, |
| 161 | }); |
| 162 | this.client = this.app.client; |
| 163 | this.store = new SlackConversationStore({ |
| 164 | client: this.client, |
| 165 | botUserId: "", |
nothing calls this directly
no outgoing calls
no test coverage detected
searching dependent graphs…