experimental-ash 0.10.4 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@ import { ASH_PACKAGE_NAME } from "#package-name.js";
6
6
  let cachedPackageInfo;
7
7
  // The package build stamps the published version into `dist` so bundled
8
8
  // deployments can still report package metadata without resolving package.json.
9
- const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.10.4";
9
+ const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.11.0";
10
10
  const BUNDLED_FALLBACK_PACKAGE_VERSION_PLACEHOLDER = "__ASH_PACKAGE_VERSION_PLACEHOLDER__";
11
11
  const WORKFLOW_MODULE_ALIASES = {
12
12
  "workflow/api": "src/compiled/@workflow/core/runtime.js",
@@ -0,0 +1,28 @@
1
+ import type { SessionAuthContext } from "#channel/types.js";
2
+ import type { Message } from "#compiled/chat/index.js";
3
+ import type { SlackChannelEvents, SlackContext, SlackMentionResult } from "#public/channels/slack/slackChannel.js";
4
+ /**
5
+ * Workspace-scoped projection of the Slack actor that produced
6
+ * `message`. Used by {@link defaultOnMention} to derive a
7
+ * {@link SessionAuthContext} when the customer hasn't supplied their
8
+ * own `onMention`.
9
+ */
10
+ export declare function defaultSlackAuth(message: Message, ctx: SlackContext): SessionAuthContext | null;
11
+ /**
12
+ * Default `onMention` — derives auth from the Slack actor and posts a
13
+ * `"Thinking…"` typing indicator before the workflow runtime starts.
14
+ */
15
+ export declare function defaultOnMention(ctx: SlackContext, message: Message): Promise<SlackMentionResult>;
16
+ /**
17
+ * Default `input.requested` handler — renders each pending HITL
18
+ * request as Slack `block_actions`. Buttons by default; radio for
19
+ * ≤6-option select requests; static_select for >6-option select
20
+ * requests. Override by declaring `events["input.requested"]`.
21
+ */
22
+ export declare function defaultInputRequestedHandler(): NonNullable<SlackChannelEvents["input.requested"]>;
23
+ /**
24
+ * Built-in Slack event handlers — typing indicators, error replies,
25
+ * and the connection-authorization status flow. Each is overridable
26
+ * per-event by passing the same key under `slackChannel({ events })`.
27
+ */
28
+ export declare const defaultEvents: SlackChannelEvents;
@@ -0,0 +1,223 @@
1
+ import { createLogger, extractErrorId, formatErrorHint } from "#internal/logging.js";
2
+ import { decodeThreadId } from "#public/channels/slack/api.js";
3
+ import { buildAuthCompletedText, buildAuthEphemeralBlocks, buildAuthRequiredPublicText, formatConnectionDisplayName, } from "#public/channels/slack/connections.js";
4
+ import { renderInputRequestBlocks } from "#public/channels/slack/hitl.js";
5
+ import { truncateTypingStatus } from "#public/channels/slack/limits.js";
6
+ const log = createLogger("slack.defaults");
7
+ function readTeamId(message) {
8
+ const raw = message.raw;
9
+ const teamId = raw?.team_id ?? raw?.team;
10
+ return typeof teamId === "string" && teamId.length > 0 ? teamId : undefined;
11
+ }
12
+ /**
13
+ * Workspace-scoped projection of the Slack actor that produced
14
+ * `message`. Used by {@link defaultOnMention} to derive a
15
+ * {@link SessionAuthContext} when the customer hasn't supplied their
16
+ * own `onMention`.
17
+ */
18
+ export function defaultSlackAuth(message, ctx) {
19
+ const author = message.author;
20
+ if (!author)
21
+ return null;
22
+ const teamId = readTeamId(message);
23
+ const isBot = author.isBot === true;
24
+ const userId = String(author.userId);
25
+ const principalId = teamId
26
+ ? isBot
27
+ ? `slack:${teamId}:bot:${userId}`
28
+ : `slack:${teamId}:${userId}`
29
+ : isBot
30
+ ? `slack:bot:${userId}`
31
+ : `slack:${userId}`;
32
+ const attributes = {
33
+ author_type: isBot ? "bot" : "user",
34
+ channel_id: ctx.slack.channelId,
35
+ thread_ts: ctx.slack.threadTs,
36
+ user_id: userId,
37
+ };
38
+ if (typeof author.userName === "string" && author.userName.length > 0) {
39
+ attributes.user_name = author.userName;
40
+ }
41
+ if (typeof author.fullName === "string" && author.fullName.length > 0) {
42
+ attributes.full_name = author.fullName;
43
+ }
44
+ if (teamId !== undefined) {
45
+ attributes.team_id = teamId;
46
+ }
47
+ return {
48
+ attributes,
49
+ authenticator: "slack-webhook",
50
+ issuer: teamId !== undefined ? `slack:${teamId}` : "slack",
51
+ principalId,
52
+ principalType: isBot ? "service" : "user",
53
+ };
54
+ }
55
+ /**
56
+ * Default `onMention` — derives auth from the Slack actor and posts a
57
+ * `"Thinking…"` typing indicator before the workflow runtime starts.
58
+ */
59
+ export async function defaultOnMention(ctx, message) {
60
+ await ctx.thread.startTyping("Thinking...");
61
+ return { auth: defaultSlackAuth(message, ctx) };
62
+ }
63
+ /**
64
+ * Reads the first non-empty line of a model-emitted message. The
65
+ * default `actions.requested` handler uses this to surface the
66
+ * model's own pre-tool-call narration as the typing indicator.
67
+ */
68
+ function firstNonEmptyLine(text) {
69
+ for (const line of text.split(/\r?\n/u)) {
70
+ const trimmed = line.trim();
71
+ if (trimmed.length > 0)
72
+ return trimmed;
73
+ }
74
+ return undefined;
75
+ }
76
+ /**
77
+ * Default `input.requested` handler — renders each pending HITL
78
+ * request as Slack `block_actions`. Buttons by default; radio for
79
+ * ≤6-option select requests; static_select for >6-option select
80
+ * requests. Override by declaring `events["input.requested"]`.
81
+ */
82
+ export function defaultInputRequestedHandler() {
83
+ return async (data, ctx) => {
84
+ if (data.requests.length === 0)
85
+ return;
86
+ const decoded = decodeThreadId(ctx.thread.id ?? "");
87
+ await ctx.slack.request("chat.postMessage", {
88
+ channel: decoded.channelId,
89
+ thread_ts: decoded.threadTs,
90
+ blocks: data.requests.flatMap(renderInputRequestBlocks),
91
+ text: data.requests.map((r) => r.prompt).join("\n"),
92
+ });
93
+ };
94
+ }
95
+ /**
96
+ * Built-in Slack event handlers — typing indicators, error replies,
97
+ * and the connection-authorization status flow. Each is overridable
98
+ * per-event by passing the same key under `slackChannel({ events })`.
99
+ */
100
+ export const defaultEvents = {
101
+ async "turn.started"(_event, ctx) {
102
+ ctx.state.pendingToolCallMessage = null;
103
+ await ctx.thread.startTyping("Working...");
104
+ },
105
+ async "actions.requested"(event, ctx) {
106
+ const buffered = ctx.state.pendingToolCallMessage;
107
+ ctx.state.pendingToolCallMessage = null;
108
+ if (buffered) {
109
+ await ctx.thread.startTyping(truncateTypingStatus(buffered));
110
+ return;
111
+ }
112
+ const labels = event.actions.map((a) => (a.kind === "tool-call" ? a.toolName : a.kind));
113
+ await ctx.thread.startTyping(truncateTypingStatus(`Running ${labels.join(", ")}...`));
114
+ },
115
+ async "message.completed"(event, ctx) {
116
+ if (event.finishReason === "tool-calls") {
117
+ // Buffer the model's prose so `actions.requested` can surface
118
+ // it as the typing indicator. Don't post — there's no
119
+ // user-visible answer yet, just narration before a tool call.
120
+ ctx.state.pendingToolCallMessage = event.message
121
+ ? (firstNonEmptyLine(event.message) ?? null)
122
+ : null;
123
+ return;
124
+ }
125
+ ctx.state.pendingToolCallMessage = null;
126
+ if (event.message)
127
+ await ctx.thread.post({ markdown: event.message });
128
+ },
129
+ async "turn.failed"(event, ctx) {
130
+ const hint = formatErrorHint(event);
131
+ const errorId = extractErrorId(event.details);
132
+ await ctx.thread.post({
133
+ markdown: [
134
+ `I hit an error while handling your request${hint}.`,
135
+ "",
136
+ "Please try again, rephrase, or reach out if it keeps failing.",
137
+ ...(errorId ? ["", `_Error id: \`${errorId}\`_`] : []),
138
+ ].join("\n"),
139
+ });
140
+ },
141
+ async "session.failed"(event, ctx) {
142
+ const hint = formatErrorHint(event);
143
+ const errorId = extractErrorId(event.details);
144
+ await ctx.thread.post({
145
+ markdown: [
146
+ `This session couldn't recover from an error${hint}.`,
147
+ "",
148
+ "Start a new thread to continue — I can't pick this one back up.",
149
+ ...(errorId ? ["", `_Error id: \`${errorId}\`_`] : []),
150
+ ].join("\n"),
151
+ });
152
+ },
153
+ async "connection.authorization_required"(event, ctx) {
154
+ const displayName = formatConnectionDisplayName(event.connectionName);
155
+ const triggeringUserId = ctx.state.triggeringUserId ?? null;
156
+ const challengeUrl = event.authorization?.url;
157
+ if (triggeringUserId && challengeUrl) {
158
+ try {
159
+ await ctx.slack.request("chat.postEphemeral", {
160
+ channel: ctx.slack.channelId,
161
+ user: triggeringUserId,
162
+ thread_ts: ctx.slack.threadTs,
163
+ blocks: buildAuthEphemeralBlocks({ displayName, url: challengeUrl }),
164
+ text: `Sign in with ${displayName}: ${challengeUrl}`,
165
+ });
166
+ }
167
+ catch (error) {
168
+ log.error("Slack auth ephemeral delivery failed", {
169
+ connectionName: event.connectionName,
170
+ error,
171
+ });
172
+ }
173
+ }
174
+ const publicText = buildAuthRequiredPublicText({
175
+ displayName,
176
+ hasUser: triggeringUserId !== null,
177
+ });
178
+ try {
179
+ const sent = await ctx.thread.post({ markdown: publicText });
180
+ const sentId = sent && typeof sent === "object" && "id" in sent ? sent.id : undefined;
181
+ if (typeof sentId === "string") {
182
+ ctx.state.pendingAuthMessageTs = {
183
+ ...ctx.state.pendingAuthMessageTs,
184
+ [event.connectionName]: sentId,
185
+ };
186
+ }
187
+ }
188
+ catch (error) {
189
+ log.error("Slack auth public message delivery failed", {
190
+ connectionName: event.connectionName,
191
+ error,
192
+ });
193
+ }
194
+ },
195
+ async "connection.authorization_completed"(event, ctx) {
196
+ const pending = ctx.state.pendingAuthMessageTs ?? {};
197
+ const ts = pending[event.connectionName];
198
+ if (ts === undefined)
199
+ return;
200
+ const displayName = formatConnectionDisplayName(event.connectionName);
201
+ const text = buildAuthCompletedText({
202
+ displayName,
203
+ outcome: event.outcome,
204
+ reason: event.reason,
205
+ });
206
+ try {
207
+ await ctx.slack.request("chat.update", {
208
+ channel: ctx.slack.channelId,
209
+ ts,
210
+ text,
211
+ });
212
+ }
213
+ catch (error) {
214
+ log.error("Slack auth status edit failed", {
215
+ connectionName: event.connectionName,
216
+ error,
217
+ });
218
+ }
219
+ const next = { ...pending };
220
+ delete next[event.connectionName];
221
+ ctx.state.pendingAuthMessageTs = next;
222
+ },
223
+ };
@@ -1,4 +1,3 @@
1
- export { slack, type SlackOptions } from "#public/channels/slack/slack.js";
2
- export { slackChannel, type SlackApiHandle, type SlackApiResponse, type SlackChannel, type SlackChannelConfig, type SlackChannelEvents, type SlackChannelCredentials, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackInteractionAction, type SlackReceiveArgs, type SlackStateAdapter, type SlackWebhookVerifier, } from "#public/channels/slack/slackChannel.js";
1
+ export { slackChannel, type SlackApiHandle, type SlackApiResponse, type SlackChannel, type SlackChannelConfig, type SlackChannelEvents, type SlackChannelCredentials, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackInteractionAction, type SlackMentionResult, type SlackMentionResultOrPromise, type SlackReceiveArgs, type SlackStateAdapter, type SlackWebhookVerifier, } from "#public/channels/slack/slackChannel.js";
3
2
  export { Actions, Button, Card, CardText, Divider, Fields, Image, LinkButton, Modal, RadioSelect, Section, Select, SelectOption, Table, TextInput, } from "#compiled/chat/index.js";
4
3
  export type { AdapterPostableMessage, Attachment, Author, CardElement, FileUpload, Message, PostableMessage, SentMessage, Thread, } from "#compiled/chat/index.js";
@@ -1,3 +1,2 @@
1
- export { slack } from "#public/channels/slack/slack.js";
2
1
  export { slackChannel, } from "#public/channels/slack/slackChannel.js";
3
2
  export { Actions, Button, Card, CardText, Divider, Fields, Image, LinkButton, Modal, RadioSelect, Section, Select, SelectOption, Table, TextInput, } from "#compiled/chat/index.js";
@@ -1,3 +1,4 @@
1
+ import type { SessionAuthContext } from "#channel/types.js";
1
2
  import { type SlackBotToken } from "#compiled/@chat-adapter/slack/index.js";
2
3
  import { type Message, type SerializedThread, type StateAdapter, type Thread } from "#compiled/chat/index.js";
3
4
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
@@ -9,7 +10,7 @@ type EventData<T extends HandleMessageStreamEvent["type"]> = Extract<HandleMessa
9
10
  data: infer D;
10
11
  } ? D : undefined;
11
12
  /**
12
- * Pre-dispatch Slack context — handed to `run`, `onMention`, and
13
+ * Pre-dispatch Slack context — handed to `onMention` and
13
14
  * `onInteraction`. These hooks run on the inbound webhook side, before
14
15
  * the runtime hydrates any session state, so `state` is intentionally
15
16
  * absent here.
@@ -124,10 +125,15 @@ export interface SlackInteractionAction {
124
125
  */
125
126
  readonly label?: string;
126
127
  }
127
- export type SlackRunResult = {
128
- auth: import("#channel/types.js").SessionAuthContext | null;
128
+ /**
129
+ * Result of an `onMention` callback. Return `{ auth }` (auth may be
130
+ * `null`) to dispatch a turn with that session auth context, or `null`
131
+ * to silently drop the mention.
132
+ */
133
+ export type SlackMentionResult = {
134
+ auth: SessionAuthContext | null;
129
135
  } | null;
130
- export type SlackRunResultOrPromise = SlackRunResult | Promise<SlackRunResult>;
136
+ export type SlackMentionResultOrPromise = SlackMentionResult | Promise<SlackMentionResult>;
131
137
  export interface SlackChannelEvents {
132
138
  readonly "turn.started"?: SlackEventHandler<"turn.started">;
133
139
  readonly "actions.requested"?: SlackEventHandler<"actions.requested">;
@@ -170,25 +176,25 @@ export interface SlackChannelConfig {
170
176
  */
171
177
  readonly uploadPolicy?: Partial<UploadPolicy>;
172
178
  /**
173
- * Synchronous-or-async filter and auth resolver invoked the moment a
174
- * Slack `app_mention` arrives, before the framework dispatches a
175
- * turn. Return `{ auth }` to dispatch with that session auth context,
176
- * or `null` to silently drop the mention. Runs on the inbound webhook
177
- * side, so cold-start latency does not affect it.
178
- */
179
- run?(ctx: SlackContext, message: Message): SlackRunResultOrPromise;
180
- /**
181
- * Free-form pre-dispatch hook invoked after `run()` accepts but
182
- * before the framework enqueues the turn. Use it for side effects
183
- * that should fire on the inbound webhook side — for example,
184
- * starting a typing indicator so the user sees feedback before the
185
- * workflow runtime cold-starts.
179
+ * Invoked the moment a Slack `app_mention` arrives, before the
180
+ * framework dispatches a turn. Decides whether to dispatch and
181
+ * with what auth, and may perform pre-dispatch side effects (e.g.
182
+ * `ctx.thread.startTyping("Thinking…")`) on the inbound webhook
183
+ * side before the workflow runtime cold-starts.
186
184
  *
187
- * The framework awaits this handler and catches thrown errors (the
188
- * turn still dispatches if `onMention` throws). It does not alter
189
- * dispatch to skip a turn, return `null` from `run()`.
185
+ * Return `{ auth }` to dispatch with that session auth context, or
186
+ * `null` to silently drop the mention. May be sync or async; the
187
+ * framework awaits the result before dispatching.
188
+ *
189
+ * Thrown errors are caught and logged, and the mention is dropped
190
+ * (no dispatch). Wrap best-effort side effects in `try/catch` if
191
+ * you want them to be non-fatal.
192
+ *
193
+ * Defaults to a workspace-scoped auth derivation that posts a
194
+ * `"Thinking…"` typing indicator. Replacing this option fully
195
+ * replaces both behaviors.
190
196
  */
191
- onMention?(ctx: SlackContext, message: Message): void | Promise<void>;
197
+ onMention?(ctx: SlackContext, message: Message): SlackMentionResultOrPromise;
192
198
  /**
193
199
  * Handler for Slack `block_actions` interactive callbacks (button
194
200
  * clicks, select changes, etc.) that are **not** consumed by the
@@ -222,5 +228,15 @@ export interface SlackChannelConfig {
222
228
  */
223
229
  export interface SlackChannel extends Channel<SlackChannelState> {
224
230
  }
231
+ /**
232
+ * Slack channel factory. Wires up the Slack webhook route, mention
233
+ * dispatch, interaction handling, and a baseline set of typing /
234
+ * error / connection-auth event handlers.
235
+ *
236
+ * Defaults apply per field — pass `onMention` to fully replace the
237
+ * default mention pipeline (auth derivation + `"Thinking…"` typing),
238
+ * or pass an `events[type]` handler to replace only that one event.
239
+ * Fields you don't supply keep their defaults.
240
+ */
225
241
  export declare function slackChannel(config?: SlackChannelConfig): SlackChannel;
226
242
  export {};
@@ -4,7 +4,7 @@ import { ThreadImpl, } from "#compiled/chat/index.js";
4
4
  import { createLogger } from "#internal/logging.js";
5
5
  import { buildSlackApiHandle, decodeThreadId } from "#public/channels/slack/api.js";
6
6
  import { buildSlackTurnMessage, collectInboundFileParts, createSlackFetchFile, } from "#public/channels/slack/attachments.js";
7
- import { renderInputRequestBlocks } from "#public/channels/slack/hitl.js";
7
+ import { defaultEvents, defaultInputRequestedHandler, defaultOnMention, } from "#public/channels/slack/defaults.js";
8
8
  import { prependSlackContext, renderInboundText, } from "#public/channels/slack/inbound.js";
9
9
  import { handleInteractionPost } from "#public/channels/slack/interactions.js";
10
10
  import { mergeUploadPolicy } from "#public/channels/upload-policy.js";
@@ -51,25 +51,6 @@ function rebuildSlackContext(state, credentials, stateAdapter) {
51
51
  state,
52
52
  };
53
53
  }
54
- /**
55
- * Default `input.requested` handler — renders each pending HITL
56
- * request as Slack `block_actions`. Buttons by default; radio for
57
- * ≤6-option select requests; static_select for >6-option select
58
- * requests. Override by declaring `events["input.requested"]`.
59
- */
60
- function defaultInputRequestedHandler() {
61
- return async (data, ctx) => {
62
- if (data.requests.length === 0)
63
- return;
64
- const decoded = decodeThreadId(ctx.thread.id ?? "");
65
- await ctx.slack.request("chat.postMessage", {
66
- channel: decoded.channelId,
67
- thread_ts: decoded.threadTs,
68
- blocks: data.requests.flatMap(renderInputRequestBlocks),
69
- text: data.requests.map((r) => r.prompt).join("\n"),
70
- });
71
- };
72
- }
73
54
  /**
74
55
  * Build the once-registered `onNewMention` listener for a `Chat`
75
56
  * instance. The chat SDK defers handler invocation through the
@@ -84,6 +65,7 @@ function defaultInputRequestedHandler() {
84
65
  * lambda may terminate.
85
66
  */
86
67
  function buildMentionListener(config, uploadPolicy, getSend) {
68
+ const onMention = config.onMention ?? defaultOnMention;
87
69
  return async (thread, message) => {
88
70
  const raw = message.raw;
89
71
  // Slack delivers `app_mention` and `message.channels` for the same
@@ -100,17 +82,16 @@ function buildMentionListener(config, uploadPolicy, getSend) {
100
82
  thread,
101
83
  slack: buildSlackApiHandle(thread, config.credentials?.botToken, teamId),
102
84
  };
103
- const runOpts = config.run ? await config.run(slackCtx, message) : { auth: null };
104
- if (runOpts === null)
85
+ let mentionResult;
86
+ try {
87
+ mentionResult = await onMention(slackCtx, message);
88
+ }
89
+ catch (error) {
90
+ log.error("onMention handler failed", { error });
105
91
  return;
106
- if (config.onMention) {
107
- try {
108
- await config.onMention(slackCtx, message);
109
- }
110
- catch (error) {
111
- log.error("onMention handler failed", { error });
112
- }
113
92
  }
93
+ if (mentionResult === null)
94
+ return;
114
95
  const decoded = decodeThreadId(thread.id ?? "");
115
96
  const continuationToken = `slack:${decoded.channelId}:${decoded.threadTs}`;
116
97
  const renderedText = renderInboundText(message);
@@ -133,7 +114,7 @@ function buildMentionListener(config, uploadPolicy, getSend) {
133
114
  : baseMessage;
134
115
  try {
135
116
  await send(turnMessage, {
136
- auth: runOpts.auth,
117
+ auth: mentionResult.auth,
137
118
  continuationToken,
138
119
  state: {
139
120
  serializedThread: thread.toJSON(),
@@ -147,11 +128,25 @@ function buildMentionListener(config, uploadPolicy, getSend) {
147
128
  }
148
129
  };
149
130
  }
131
+ /**
132
+ * Slack channel factory. Wires up the Slack webhook route, mention
133
+ * dispatch, interaction handling, and a baseline set of typing /
134
+ * error / connection-auth event handlers.
135
+ *
136
+ * Defaults apply per field — pass `onMention` to fully replace the
137
+ * default mention pipeline (auth derivation + `"Thinking…"` typing),
138
+ * or pass an `events[type]` handler to replace only that one event.
139
+ * Fields you don't supply keep their defaults.
140
+ */
150
141
  export function slackChannel(config = {}) {
151
142
  const uploadPolicy = mergeUploadPolicy(config.uploadPolicy);
152
143
  const slackFetchFile = createSlackFetchFile({ botToken: config.credentials?.botToken });
153
144
  const stateAdapter = config.stateAdapter ?? createMemoryState();
154
- const inputHandler = config.events?.["input.requested"] ?? defaultInputRequestedHandler();
145
+ const mergedEvents = {
146
+ ...defaultEvents,
147
+ ...config.events,
148
+ "input.requested": config.events?.["input.requested"] ?? defaultInputRequestedHandler(),
149
+ };
155
150
  // The chat SDK defers mention handler invocation past the route
156
151
  // handler returning, so the listener can't read `send` off the
157
152
  // current request. Capture it on the first request and reuse it —
@@ -166,7 +161,7 @@ export function slackChannel(config = {}) {
166
161
  chatPromise = (async () => {
167
162
  const { botToken, signingSecret, webhookVerifier } = resolveSlackAdapterCredentials(config.credentials);
168
163
  if (!botToken) {
169
- throw new Error("slackChannel requires a bot token. Pass credentials.botToken or set SLACK_BOT_TOKEN.");
164
+ throw new Error("slackChannel() requires a bot token. Pass credentials.botToken or set SLACK_BOT_TOKEN.");
170
165
  }
171
166
  const [slackModule, chatModule] = await Promise.all([
172
167
  import("#compiled/@chat-adapter/slack/index.js"),
@@ -226,7 +221,7 @@ export function slackChannel(config = {}) {
226
221
  await getChat();
227
222
  const channelId = input.args.channelId;
228
223
  if (!channelId) {
229
- throw new Error("slackChannel.receive requires args.channelId.");
224
+ throw new Error("slackChannel().receive requires args.channelId.");
230
225
  }
231
226
  const chatModule = await import("#compiled/chat/index.js");
232
227
  const thread = new chatModule.ThreadImpl({
@@ -245,9 +240,6 @@ export function slackChannel(config = {}) {
245
240
  },
246
241
  });
247
242
  },
248
- events: {
249
- ...config.events,
250
- "input.requested": inputHandler,
251
- },
243
+ events: mergedEvents,
252
244
  });
253
245
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "experimental-ash",
3
- "version": "0.10.4",
3
+ "version": "0.11.0",
4
4
  "bin": {
5
5
  "ash": "./bin/ash.js",
6
6
  "experimental-ash": "./bin/ash.js"
@@ -1,20 +0,0 @@
1
- import type { SessionAuthContext } from "#channel/types.js";
2
- import type { Channel } from "#public/definitions/defineChannel.js";
3
- import { type SlackChannelCredentials, type SlackChannelState, type SlackContext, type SlackEventContext, type SlackStateAdapter } from "#public/channels/slack/slackChannel.js";
4
- export interface SlackOptions {
5
- /**
6
- * Maps a verified inbound Slack mention to a {@link SessionAuthContext}.
7
- * Receives the chat SDK `Message` plus a {@link SlackContext} carrying
8
- * the live `thread` and `slack` API handle so policies can read
9
- * channel/thread metadata without re-parsing `message.raw`.
10
- *
11
- * Defaults to a workspace-scoped projection of the Slack actor; return
12
- * `null` to silently drop the mention.
13
- */
14
- readonly auth?: (message: import("#compiled/chat/index.js").Message, ctx: SlackContext) => SessionAuthContext | null;
15
- readonly credentials?: SlackChannelCredentials;
16
- readonly botName?: string;
17
- readonly stateAdapter?: SlackStateAdapter;
18
- }
19
- export declare function slack(options?: SlackOptions): Channel<SlackChannelState>;
20
- export type { SlackEventContext };