@siftline/actions
Adapters that carry a Decision to a webhook or to Slack.
npm install @siftline/actionsESM only. Node 22.14 or newer. Depends on @siftline/core for the Decision and Recipe types.
defineActions(definitions) declares your Actions, keyed by Action id. Each value is
{ kind, config }, with the config typed by that kind's Adapter. Every config is parsed against
its kind's configSchema at once, and the ids stay literal keys in the result.
ActionId<typeof actions> reads those ids back as a union, for typing Rules:
Rule<Q, ActionId<typeof actions>> rejects an Action id you did not define.
dispatch(decision, actions, recipe, { fetch?, signal? }) sends a routed Decision to the Action
it selected and returns { action, request, response }, or null when the Decision went to
Review or selected no Action.
Underneath, building a request and sending it are two steps. An Adapter's
build(decision, config, recipe) turns a Decision into an ActionRequest: method, URL,
headers, body and the idempotency key. perform(request, { fetch?, signal? }) sends it.
dispatch only composes the two. Preview is build without perform, and nothing in a request
depends on a clock or randomness, so the same Decision, config and Recipe always build the same
bytes. The guide shows both requests.
Adapters
| Export | kind | Body |
|---|---|---|
webhook | webhook | The Decision line, no envelope. parseDecision reads it. |
slackIncomingWebhook | slack_incoming_webhook | One mrkdwn message with each answer and its confidence. |
adapters maps each kind to its adapter, and every adapter carries a configSchema for a
config that arrives as JSON. The webhook config is { url, secret?, headers? }; with a
secret the request carries X-Siftline-Signature: sha256=<HMAC-SHA256 of the body>. The
Slack config is { url }.
Every request carries Idempotency-Key: <decision.id>:<decision.action> and a User-Agent
naming the package version. Slack ignores the key, so a redelivery past your own guard posts
twice.
Sending
perform sends once, with no retries and no timeout of its own, and keeps the first 4096 bytes
of the response body, cut on a code-point boundary. It returns { status, body, truncated }.
dispatch sends through perform and adds no retry or timeout either. fetch defaults to the
global one, read on each call. The option is typed narrower than the global fetch, so the
global one and a stub both fit without a cast. signal goes to fetch unchanged.
Errors
| Class | code | Thrown when |
|---|---|---|
ActionBuildError | action_build | An unknown kind or an invalid config in defineActions, an Action id dispatch was not given, or build on a Decision that selected no Action. |
ActionFailedError | action_failed | A non-2xx status or a fetch that threw. Carries status and retryable, true for 408, 429, 5xx and network failures. |
Both extend core's SiftlineError.