eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
654 lines (653 loc) • 32.7 kB
TypeScript
import type { UserContent } from "ai";
import type { ChannelResolveSession, ChannelSource } from "#channel/channel-operations.js";
import type { Session } from "#channel/session.js";
import type { SessionAuthContext, TurnPolicy, TaskDeliveryPolicy } from "#channel/types.js";
import type { CardElement } from "#compiled/chat/index.js";
import type { SessionContext } from "#public/definitions/callback-context.js";
import type { ChannelContinuationOps } from "#public/definitions/channel.js";
import type { UnstampedMessageStreamEvent } from "#protocol/message.js";
import type { InputRequest, InputResponse, StrictInputResponses, ValidatedInputResponse } from "#shared/input.js";
import { type SlackBotToken, type SlackHandle, type SlackThread, type SlackWorkspaceHandle } from "#public/channels/slack/api.js";
import { type SlackEvent, type SlackEventEnvelope, type SlackMessage } from "#public/channels/slack/inbound.js";
import { type LoadThreadContextMessagesOptions } from "#public/channels/slack/thread.js";
import { type SlackSessionOperations } from "#public/channels/slack/session-operations.js";
import { type UploadPolicyInput } from "#public/channels/upload-policy.js";
import { type SlackWebhookVerifier } from "#public/channels/slack/verify.js";
import { type Channel } from "#public/definitions/channel.js";
import { type ChannelAudience } from "#shared/channel-audience.js";
import { type SlackActivityRenderer } from "#public/channels/slack/activity.js";
export type { SlackRespondOptions, SlackSendOptions, SlackSessionOperations, } from "#public/channels/slack/session-operations.js";
type EventData<T extends UnstampedMessageStreamEvent["type"]> = Extract<UnstampedMessageStreamEvent, {
type: T;
}> extends {
data: infer D;
} ? D : undefined;
/**
* Base Slack context for inbound webhook handlers. These hooks run before the
* runtime hydrates session state, so `state` is absent here.
* {@link thread} owns thread-scoped operations (`post`, `postEphemeral`,
* `startTyping`, `refresh`, `listParticipants`, `recentMessages`,
* `mentionUser`); {@link slack} owns Slack identity (`channelId`, `threadTs`,
* `teamId`) plus the raw-API escape hatch (`request`, `uploadFiles`).
*/
export interface SlackContext {
readonly thread: SlackThread;
readonly slack: SlackHandle;
}
/**
* {@link SlackContext} plus the persisted per-session
* {@link SlackChannelState}. Built by the channel's `context()` hook and
* extended by {@link SlackEventContext}.
*/
export interface SlackChannelContext extends SlackContext {
state: SlackChannelState;
}
/**
* Slack context handed to `events[type]` handlers. Extends
* {@link SlackChannelContext} (`thread`, `slack`, hydrated `state`) with
* continuation routing ({@link ChannelContinuationOps}). Unlike the pre-dispatch
* {@link SlackContext}, `state` is hydrated here.
*/
export interface SlackEventContext extends SlackChannelContext, ChannelContinuationOps {
}
export type { SlackApiResponse, SlackBotToken, SlackHandle, SlackThread, SlackWorkspaceHandle, } from "#public/channels/slack/api.js";
export type { SlackWebhookVerifier } from "#public/channels/slack/verify.js";
type SlackEventHandler<T extends UnstampedMessageStreamEvent["type"]> = (data: EventData<T>, channel: SlackEventContext, ctx: SessionContext) => void | Promise<void>;
export type SlackInputRequestedEvent = EventData<"input.requested">;
/** Bound access to eve's standard Slack input delivery. */
export type SlackInputRequestedDefaultDeliver = (data: SlackInputRequestedEvent) => Promise<void>;
export type SlackInputRequestedHandler = (data: SlackInputRequestedEvent, channel: SlackEventContext, ctx: SessionContext, defaultDeliver: SlackInputRequestedDefaultDeliver) => void | Promise<void>;
/**
* Delivery surface handed to `authorization.required` overrides. The
* connection challenge is a credential: anyone who completes the sign-in
* binds their identity to this session's connection. So the only
* delivery capabilities here are private ones, an ephemeral reply in the
* thread or a direct message. There is deliberately no public `post`,
* no raw `slack.request` escape hatch, and no full thread handle. An
* override can change the words, not the audience.
*/
export interface SlackAuthorizationEventContext {
/**
* Ephemeral message in the current thread, visible only to `userId`.
* Same contract as {@link SlackThread.postEphemeral}.
*/
readonly postEphemeral: SlackThread["postEphemeral"];
/**
* Direct message to `userId`'s IM conversation with the bot. Same
* contract as {@link SlackThread.postDirectMessage} (requires the
* `im:write` scope).
*/
readonly postDirectMessage: SlackThread["postDirectMessage"];
/**
* Hydrated per-session channel state — read `triggeringUserId` to
* target the delivery.
*/
readonly state: SlackChannelState;
}
/**
* Signature of an `authorization.required` override. Unlike every other
* event handler, it receives {@link SlackAuthorizationEventContext}
* instead of the full {@link SlackEventContext} — see the context type
* for why.
*/
export type SlackAuthorizationRequiredHandler = (data: EventData<"authorization.required">, channel: SlackAuthorizationEventContext, ctx: SessionContext) => void | Promise<void>;
type SlackSessionFailedHandler = (data: EventData<"session.failed">, channel: SlackEventContext) => void | Promise<void>;
/**
* JSON-serializable per-session state, stored verbatim across workflow
* step boundaries. Anything written here must round-trip through
* `JSON.stringify` / `JSON.parse`.
*/
export interface SlackPendingApprovalCard {
/** Channel containing the approval card; differs from the session channel for DM delivery. */
readonly messageChannelId?: string;
readonly messageBlocks: readonly unknown[];
readonly messageTs: string;
}
export interface SlackChannelState {
/** Audience classification captured before the session is dispatched. */
audience?: ChannelAudience;
/** Slack channel id seeded by the inbound mention. */
channelId: string | null;
/** Slack thread root ts. */
threadTs: string | null;
/** Slack team id, when the inbound event carried one. */
teamId: string | null;
/** Slack message ts that triggered the active turn. */
triggeringMessageTs?: string | null;
/** Slack workspace whose app installation supplies this session's credentials. */
installationTeamId?: string | null;
/**
* Slack user id of the actor that triggered the current session/turn.
* Captured on every inbound mention so default handlers (e.g.
* `authorization.required`) can target ephemeral feedback at the right
* user without re-parsing the mention payload.
*/
triggeringUserId?: string | null;
/**
* Buffered text from a `message.completed` event whose `finishReason`
* was `"tool-calls"`. The default `actions.requested` handler uses the
* first non-empty line as the next typing indicator, surfacing the
* model's pre-tool narration instead of the action label. Cleared at
* `turn.started` and after use.
*/
pendingToolCallMessage?: string | null;
/**
* Last reasoning-derived typing indicator sent by the default
* `reasoning.appended` handler. Used to surface substantial progressive
* extensions immediately while throttling smaller streamed deltas.
*/
lastReasoningTypingAtMs?: number | null;
lastReasoningTypingStatus?: string | null;
/**
* Connection name to Slack message ts. Each entry is the public
* link-free status post created by the default
* `authorization.required` handler; the matching
* `authorization.completed` handler edits it in place to surface the
* resolution outcome.
*/
pendingAuthMessageTs?: Record<string, string>;
pendingApprovalCards?: Record<string, SlackPendingApprovalCard>;
pendingApprovalCandidateUsers?: Record<string, string>;
approvalResponderUsers?: Record<string, string>;
}
/**
* Per-session metadata attached to tracing spans, projected by the
* channel's `metadata(state)` hook. Fields mirror the inbound mention
* (channel, team, thread, triggering user) and are `null` until an inbound
* event seeds them. Open-ended (`Record<string, unknown>`) so deployments
* can attach extra span attributes.
*/
export interface SlackInstrumentationMetadata extends Record<string, unknown> {
readonly channelId: string | null;
readonly teamId: string | null;
readonly threadTs: string | null;
readonly triggeringUserId: string | null;
}
/**
* Slack channel credentials: outbound bot token plus inbound webhook
* verification. Any field may be omitted to fall back to its env-var /
* signing-secret default.
*/
export interface SlackChannelCredentials {
/**
* Bot token for all outbound Slack Web API calls. Function providers receive
* the app installation workspace id when Slack supplies one. Falls back to
* `process.env.SLACK_BOT_TOKEN` when omitted.
*/
readonly botToken?: SlackBotToken;
/**
* Signing secret used to HMAC-verify inbound webhook requests. Falls
* back to `process.env.SLACK_SIGNING_SECRET` when neither this nor
* `webhookVerifier` is supplied.
*/
readonly signingSecret?: string;
/**
* Custom inbound webhook verifier. When supplied, eve skips the
* `SLACK_SIGNING_SECRET` fallback and delegates to it. Typically set by
* integrations (e.g. Connect) that authenticate webhooks out-of-band.
*/
readonly webhookVerifier?: SlackWebhookVerifier;
}
/** Target accepted by `ctx.to(slack, target)` from route and schedule handlers. */
export interface SlackReceiveTarget {
readonly channelId: string;
readonly threadTs?: string;
/**
* Optional audience for proactive receives. Omit to leave `unknown`.
* Pass when the caller already knows channel visibility, for example a
* webhook or schedule that classified the Slack destination before handoff.
*/
readonly audience?: ChannelAudience;
/** Slack workspace whose app installation supplies credentials for this send. */
readonly installationTeamId?: string;
/**
* Optional message posted into the Slack channel before the agent runs.
* The post becomes the thread root and the first turn is threaded under
* it, giving cross-channel handoffs a visible context anchor. Mutually
* exclusive with {@link threadTs}.
*/
readonly initialMessage?: SlackInitialMessage;
}
/**
* Pre-agent post issued by `slackChannel().receive` when the caller
* provides `target.initialMessage`. Mirrors `ctx.thread.post`'s card
* variant so the same `Card({...})` construction can be reused.
*/
export interface SlackInitialMessage {
readonly card: CardElement;
readonly fallbackText?: string;
}
/**
* Options for one turn requested by a generic Slack event handler.
*/
export interface SlackEventSendOptions {
/** Updates the session policy; omission preserves it. */
readonly taskDeliveryPolicy?: TaskDeliveryPolicy;
readonly auth: SessionAuthContext | null;
readonly target: SlackReceiveTarget;
/** Overrides the workflow run title without changing the message sent to the model. */
readonly title?: string;
}
/** Options for answering pending input requests from a generic Slack event handler. */
export interface SlackEventRespondOptions {
readonly auth: SessionAuthContext | null;
readonly target: SlackSessionTarget;
}
/**
* Starts a session on the current Slack channel from `onEvent`. Call it zero,
* one, or many times; each invocation returns the resulting session.
*/
export type SlackEventSendFn = (message: string | UserContent, options: SlackEventSendOptions) => Promise<Session>;
/** Answers pending input requests on one Slack thread. */
export type SlackEventRespondFn = <const TResponses extends readonly InputResponse[]>(inputResponses: StrictInputResponses<TResponses>, options: SlackEventRespondOptions) => ReturnType<SlackSessionOperations["respond"]>;
/**
* Slack thread identity used by workspace-scoped inbound helpers.
*/
export interface SlackSessionTarget {
readonly channelId: string;
readonly threadTs: string;
}
/**
* Imperative surface handed to `slackChannel({ onEvent })`. Generic Events API
* payloads are not necessarily tied to one thread, so the context exposes a
* workspace API handle plus Slack-bound operations rather than the
* thread-scoped {@link SlackContext} used by message handlers.
*/
export interface SlackInboundEventContext {
/** Starts a turn on this Slack channel using the proactive send contract. */
readonly send: SlackEventSendFn;
/** Answers pending input requests on one Slack thread. */
readonly respond: SlackEventRespondFn;
/** Cancels the active turn for one Slack thread. */
cancel(input: {
readonly target: SlackSessionTarget;
readonly turnId?: string;
}): ReturnType<ChannelSource<SlackChannelState>["cancel"]>;
/** Compacts the current session for one Slack thread. */
compact(input: {
readonly target: SlackSessionTarget;
}): ReturnType<ChannelSource<SlackChannelState>["compact"]>;
/** Clears the current session for one Slack thread. */
clear(input: {
readonly target: SlackSessionTarget;
}): ReturnType<ChannelSource<SlackChannelState>["clear"]>;
/** Resets the current session for one Slack thread. */
reset(input: {
readonly reason?: string;
readonly target: SlackSessionTarget;
}): ReturnType<ChannelSource<SlackChannelState>["reset"]>;
/** Resolves the current session for one Slack thread. */
resolveSession(input: {
readonly target: SlackSessionTarget;
}): ReturnType<ChannelResolveSession>;
/** The complete signed Events API callback envelope. */
readonly envelope: SlackEventEnvelope;
/** Workspace-scoped Slack identity and raw Web API escape hatch. */
readonly slack: SlackWorkspaceHandle;
/** Keeps detached handler work alive after the Slack webhook is acknowledged. */
readonly waitUntil: (task: Promise<unknown>) => void;
}
/**
* Message-scoped context handed to `onMessage`, `onAppMention`, and
* `onDirectMessage`.
*/
export interface SlackInboundMessageContext extends SlackContext, SlackSessionOperations {
/**
* Returns whether the inbound message belongs to a DM, group DM, or private channel.
* Unknown conversation types fail closed and return `true`.
*/
isDMOrPrivateChannel(): Promise<boolean>;
/** Returns whether this message belongs to a thread with an active eve session. */
isSubscribed(): Promise<boolean>;
/** Returns whether the inbound event explicitly mentions this bot. */
isBotMentioned(): boolean;
}
/** Interaction-scoped context handed to `slackChannel({ onInteraction })`. */
export interface SlackInteractionContext extends SlackContext, SlackSessionOperations {
}
/** Workspace-scoped context handed to `slackChannel({ onShortcut })`. */
export interface SlackShortcutContext {
/** Slack workspace identity and raw Web API escape hatch. */
readonly slack: SlackWorkspaceHandle;
}
/** Workspace-scoped context handed to `slackChannel({ onSlashCommand })`. */
export interface SlackSlashCommandContext {
/** Slack workspace identity and raw Web API escape hatch. */
readonly slack: SlackWorkspaceHandle;
}
/** Context handed to `slackChannel({ onInputResponse })` before eve resumes HITL. */
export interface SlackInputResponseContext extends SlackContext {
/** Auth derived from the Slack user who submitted the signed interaction. */
readonly defaultAuth: SessionAuthContext;
}
export interface SlackInteractionAction {
readonly actionId: string;
readonly value?: string;
readonly blockId?: string;
/**
* `selected_option.value` for radio / select / external_select
* widgets. `undefined` for buttons and multi-select widgets.
*/
readonly selectedOptionValue?: string;
/**
* `ts` of the Slack message hosting the clicked component. Required to
* update that message in place via `chat.update`, since `ctx.slack.threadTs`
* resolves to the thread root (not the clicked message) for components
* inside thread replies.
*/
readonly messageTs?: string;
/**
* Display label of the clicked widget: `text.text` for buttons,
* `selected_option.text.text` for radio/static_select. Renders the
* "answered" card without re-fetching the original request.
*/
readonly label?: string;
/**
* Slack actor who triggered the interaction, letting `onInteraction`
* handlers attribute resolutions back to the clicker without re-parsing
* the raw payload. Always present, since Slack requires `user` on every
* `block_actions` payload.
*/
readonly user: SlackInteractionUser;
}
/** Slack actor on an interactive payload, mirroring `body.user`. */
export interface SlackInteractionUser {
readonly id: string;
/** Modern canonical display handle. */
readonly username?: string;
/** Legacy display handle, kept for older workspaces. */
readonly name?: string;
}
/** Decoded Slack slash command invocation. */
export interface SlackSlashCommand {
/** Configured command name, including its leading slash. */
readonly command: string;
readonly text: string;
readonly user: SlackInteractionUser;
readonly teamId?: string;
readonly channelId: string;
readonly channelName?: string;
readonly enterpriseId?: string;
readonly isEnterpriseInstall: boolean;
readonly triggerId?: string;
readonly responseUrl?: string;
}
/** Message selected through a Slack message shortcut. */
export interface SlackShortcutMessage {
readonly text: string;
readonly ts: string;
readonly threadTs?: string;
readonly userId?: string;
}
/** Decoded Slack message shortcut or global shortcut. */
export type SlackShortcut = {
readonly type: "message_action";
readonly callbackId: string;
readonly triggerId: string;
readonly user: SlackInteractionUser;
readonly teamId?: string;
readonly channelId: string;
readonly message: SlackShortcutMessage;
readonly responseUrl?: string;
} | {
readonly type: "shortcut";
readonly callbackId: string;
readonly triggerId: string;
readonly user: SlackInteractionUser;
readonly teamId?: string;
};
/** Decoded eve-owned HITL response submitted through Slack interactivity. */
export type SlackInputResponseSubmission = {
readonly type: "block_actions";
readonly inputResponses: readonly ValidatedInputResponse[];
readonly actions: readonly SlackInteractionAction[];
readonly messageTs?: string;
readonly user: SlackInteractionUser;
} | {
readonly type: "view_submission";
readonly inputResponses: readonly ValidatedInputResponse[];
readonly messageTs: string;
readonly user: SlackInteractionUser;
};
/** Result of a Slack HITL response admission hook. Return `null` to reject. */
export type SlackInputResponseResult = {
readonly auth: SessionAuthContext | null;
} | null;
/**
* Result of an `onAppMention` or `onDirectMessage` callback. Return an
* object (auth may be `null`) to dispatch a turn, or `null` to drop the
* inbound message. `context` strings are appended as user messages to
* session history before the delivery message. `title` overrides the
* workflow run title without changing the message sent to the model.
*/
export type SlackMentionResult = {
readonly auth: SessionAuthContext | null;
readonly context?: readonly string[];
readonly title?: string;
} | null;
export type SlackMentionResultOrPromise = SlackMentionResult | Promise<SlackMentionResult>;
/**
* Alias of {@link SlackMentionResult} for the `onDirectMessage` signature,
* so DM handlers do not read in terms of "mention".
*/
export type SlackInboundResult = SlackMentionResult;
/** {@link SlackInboundResult}, or a promise resolving to one. */
export type SlackInboundResultOrPromise = SlackMentionResultOrPromise;
/**
* Per-event Slack handlers keyed by harness stream-event type, passed to
* `slackChannel({ events })`. Each key is optional; supplying one replaces
* only that event's built-in default (see {@link defaultEvents}). Handlers
* receive the event data, the {@link SlackEventContext}, and the session
* {@link SessionContext}; `session.failed` receives only data and channel
* context and exposes the ID as `data.sessionId`.
*/
export interface SlackChannelEvents {
readonly "approval.candidate"?: SlackEventHandler<"approval.candidate">;
readonly "approval.settled"?: SlackEventHandler<"approval.settled">;
readonly "turn.started"?: SlackEventHandler<"turn.started">;
readonly "actions.requested"?: SlackEventHandler<"actions.requested">;
readonly "action.partial"?: SlackEventHandler<"action.partial">;
readonly "action.result"?: SlackEventHandler<"action.result">;
readonly "message.completed"?: SlackEventHandler<"message.completed">;
readonly "message.appended"?: SlackEventHandler<"message.appended">;
readonly "reasoning.appended"?: SlackEventHandler<"reasoning.appended">;
readonly "reasoning.completed"?: SlackEventHandler<"reasoning.completed">;
/**
* An override may pass selected requests to `defaultDeliver()` while
* rendering the rest itself. The default delivery honors `approvalChannel`.
*/
readonly "input.requested"?: SlackInputRequestedHandler;
readonly "turn.failed"?: SlackEventHandler<"turn.failed">;
readonly "turn.completed"?: SlackEventHandler<"turn.completed">;
readonly "turn.cancelled"?: SlackEventHandler<"turn.cancelled">;
readonly "session.failed"?: SlackSessionFailedHandler;
readonly "session.completed"?: SlackEventHandler<"session.completed">;
readonly "session.waiting"?: SlackEventHandler<"session.waiting">;
/**
* Override receives {@link SlackAuthorizationEventContext}, a
* private-delivery context (ephemeral or DM), not the full
* {@link SlackEventContext}. The challenge is a credential, so a
* public post is not expressible here.
*/
readonly "authorization.required"?: SlackAuthorizationRequiredHandler;
readonly "authorization.completed"?: SlackEventHandler<"authorization.completed">;
}
/**
* Full-context variant of {@link SlackChannelEvents} consumed by the
* channel internals. The framework's default `authorization.required`
* handler keeps the full {@link SlackEventContext} because it owns the
* public link-free status while user overrides remain private-only. The
* factory adapts user overrides into this shape with
* {@link constrainAuthorizationRequired}.
*/
export interface SlackChannelInternalEvents extends Omit<SlackChannelEvents, "authorization.required" | "input.requested"> {
readonly "authorization.required"?: SlackEventHandler<"authorization.required">;
readonly "input.requested"?: SlackEventHandler<"input.requested">;
}
export type SlackApprovalChannel = "direct-message" | "thread";
/** Input request passed to a {@link SlackApprovalChannelResolver}. */
export type SlackApprovalRequest = InputRequest;
/** Chooses the Slack destination for one input request. */
export type SlackApprovalChannelResolver = (request: SlackApprovalRequest, ctx: SessionContext) => SlackApprovalChannel | Promise<SlackApprovalChannel>;
export interface SlackChannelConfig {
readonly credentials?: SlackChannelCredentials;
readonly botName?: string;
/**
* Chooses where each input request is delivered. Direct-message requests go to the
* Slack user who triggered the active turn; the session thread names that reviewer
* without exposing the request. Defaults to `"thread"` when omitted.
*/
readonly approvalChannel?: SlackApprovalChannelResolver;
/** Optional presentation-only activity rendered without starting parent turns. */
readonly activity?: {
readonly renderers: readonly SlackActivityRenderer[];
};
/** Override the default webhook route path (`/eve/v1/slack`). */
readonly route?: string;
/** Policy for accepted messages that arrive while a turn is active. */
readonly turnPolicy?: TurnPolicy;
/**
* Inbound upload policy applied to file attachments before they reach
* the harness. Violating attachments are dropped with a warning so the
* mention's text portion still gets delivered. Pass `"disabled"` to
* reject every attachment. Defaults to the framework's 25 MB cap with
* unrestricted media types.
*/
readonly uploadPolicy?: UploadPolicyInput;
/**
* Adds earlier replies from the current Slack thread to each triggering
* turn. Messages are rendered with their Slack sender ids attached so a
* multi-user transcript retains unambiguous speaker attribution. Omit this
* option to avoid fetching thread history.
*/
readonly threadContext?: LoadThreadContextMessagesOptions;
/**
* Handles human-authored Slack messages. Specialized `onAppMention` and
* `onDirectMessage` handlers take precedence for their event types. Other
* channel messages are ignored when this hook is omitted.
*/
onMessage?(ctx: SlackInboundMessageContext, message: SlackMessage): SlackInboundResultOrPromise;
/**
* Invoked when a Slack `app_mention` event arrives (only `app_mention`;
* other event types are ignored). Decides whether to dispatch and with
* what auth, and may run pre-dispatch side effects (e.g.
* `ctx.thread.startTyping("Thinking...")`) on the inbound webhook side
* before the runtime cold-starts.
*
* Return `{ auth }` to dispatch with that session auth context, or `null`
* to drop the mention. May be sync or async; the result is awaited before
* dispatching. Thrown errors are caught and logged and the mention is
* dropped; wrap best-effort side effects in `try/catch` to keep them
* non-fatal. Defaults to a workspace-scoped auth derivation that posts a
* `"Thinking..."` typing indicator; replacing this replaces both.
*/
onAppMention?(ctx: SlackInboundMessageContext, message: SlackMessage): SlackMentionResultOrPromise;
/**
* Invoked on a direct message: a Slack `message` event with
* `channel_type: "im"`. Subtype messages (edits, deletes, joins, etc.)
* and bot messages (`bot_id` set, including the bot's own replies) are
* filtered out first, so handlers only see plain user-authored DMs.
* Decides whether to dispatch and with what auth, and may run
* pre-dispatch side effects on the inbound webhook side before cold-start.
*
* Return `{ auth }` to dispatch with that session auth context, or `null`
* to drop the message. May be sync or async; the result is awaited before
* dispatching. Thrown errors are caught and logged and the message is
* dropped; wrap best-effort side effects in `try/catch` to keep them
* non-fatal. Defaults to a workspace-scoped auth derivation that posts a
* `"Thinking..."` typing indicator; replacing this replaces both.
* Requires the bot's Slack app to subscribe to `message.im` with the
* `im:history` scope.
*/
onDirectMessage?(ctx: SlackInboundMessageContext, message: SlackMessage): SlackInboundResultOrPromise;
/**
* Fallback handler for signed Slack Events API callbacks. An authored
* `onAppMention` or `onDirectMessage` takes precedence for events accepted
* by that specialized handler; otherwise the raw event arrives here. When
* neither a specialized handler nor `onEvent` is authored, mentions and DMs
* retain their built-in defaults and other event types are ignored.
*
* The handler owns control flow. Call `ctx.send(...)` zero, one, or many
* times to start turns on Slack, and use `ctx.waitUntil(...)` for detached
* work. The return value is ignored. Runs after the webhook has been
* acknowledged through the host's `waitUntil` mechanism. Errors are caught
* and logged and never fall through to another handler.
*
* URL verification, slash commands, and interactive payloads are not Events
* API callbacks and do not reach this handler.
*/
onEvent?(ctx: SlackInboundEventContext, event: SlackEvent): void | Promise<void>;
/**
* Handler for Slack `block_actions` interactive callbacks (button
* clicks, select changes, etc.) **not** consumed by the framework's
* HITL pipeline. Slack POSTs interactive payloads to the same webhook
* route as mentions; the framework decodes them, routes any action whose
* `action_id` starts with `eve_input:` to the runtime as an HITL
* response (resuming a paused session), and forwards everything else
* here, one invocation per non-HITL action.
*
* Runs on the inbound webhook side via `waitUntil()`, so the channel
* returns `200 OK` immediately. Errors are caught and logged; they do
* not affect the webhook response or sibling invocations.
*
* The `SlackContext` here is rebuilt from the interaction payload
* (channel id, thread ts, team id), **not** the persisted thread state
* used by event handlers. Use `ctx.slack.request(...)` for arbitrary
* Slack Web API calls and `action.messageTs` to target `chat.update`.
*/
onInteraction?(action: SlackInteractionAction, ctx: SlackInteractionContext): void | Promise<void>;
/**
* Handles Slack message shortcuts (`message_action`) and global shortcuts
* (`shortcut`) with workspace-scoped Slack Web API access.
*
* eve returns `200 OK` immediately and keeps the handler alive through
* `waitUntil()`. Errors are caught and logged.
*/
onShortcut?(shortcut: SlackShortcut, ctx: SlackShortcutContext): void | Promise<void>;
/**
* Handles Slack slash commands configured with this channel's webhook route.
* The command includes the configured command name, arguments, invoking user,
* channel, workspace, trigger ID, and response URL.
*
* eve returns an empty `200 OK` immediately and keeps the handler alive
* through `waitUntil()`. Errors are caught and logged.
*/
onSlashCommand?(command: SlackSlashCommand, ctx: SlackSlashCommandContext): void | Promise<void>;
/**
* Authorizes an eve-owned HITL answer before the pending input resolves.
* Return `{ auth }` to accept the response, or `null` to reject it and keep
* the request pending. Thrown errors are logged and treated as rejection.
*
* When this hook is omitted, Slack preserves its built-in behavior and
* accepts the response with the submitting user's auth, regardless of other
* authored handlers.
*/
onInputResponse?(ctx: SlackInputResponseContext, submission: SlackInputResponseSubmission): SlackInputResponseResult | Promise<SlackInputResponseResult>;
readonly events?: SlackChannelEvents;
}
/**
* Concrete return type of {@link slackChannel}. Named so consumers can
* default-export a `slackChannel(...)` call under `declaration: true`
* without TypeScript emitting an internal path for `Channel`.
*/
export interface SlackChannel extends Channel<SlackChannelState, SlackReceiveTarget, SlackInstrumentationMetadata> {
}
/**
* Slack channel factory. Wires up the webhook route, mention dispatch,
* interaction handling, and a baseline set of typing / error /
* connection-auth event handlers. Defaults apply per field: pass
* `onAppMention` to fully replace the default mention pipeline (auth
* derivation plus `"Thinking..."` typing), or an `events[type]` handler to
* replace only that one event. When `onEvent` is authored it becomes the
* fallback ahead of unsupplied mention and DM defaults; otherwise unsupplied
* fields keep their defaults.
*/
export declare function slackChannel(config?: SlackChannelConfig): SlackChannel;
/**
* Adapts a user-supplied `authorization.required` override to the full
* internal event signature while handing it only the private-delivery
* surface ({@link SlackAuthorizationEventContext}). Override code never
* receives `thread.post` or the raw `slack.request` escape hatch, so the
* challenge it renders cannot be addressed to the shared thread.
*/
export declare function constrainAuthorizationRequired(handler: SlackAuthorizationRequiredHandler): NonNullable<SlackChannelInternalEvents["authorization.required"]>;