UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

142 lines (141 loc) 5.9 kB
/** * Slack HITL widget rendering + click-decode helpers. * * Wire format: select-style HITL widgets mint `action_id = * HITL_ACTION_PREFIX + requestId`. Button widgets add a stable suffix so * every button in the same Slack actions block has a unique `action_id`. * The decoder requires that suffix for button payloads. * * Buttons surface the selected option on `action.value`; radio and * static selects surface it on `selected_option.value`. The decoder * picks whichever is set so the renderer can pick a widget kind on * UX grounds without changing the read path. */ import type { InputRequest } from "#runtime/input/types.js"; /** * Wire-format prefix every framework HITL widget mints onto its * `action_id`. Exposed so end-user adapters that render their own * interactive widgets can avoid collisions. */ export declare const HITL_ACTION_PREFIX = "eve_input:"; /** * `action_id` prefix for the "Type your answer" button that opens a * freeform-answer modal. Splitting the prefix from {@link HITL_ACTION_PREFIX} * lets the route handler differentiate "this click is a final answer" * (resolve via `inputResponses`) from "this click needs a modal first" * (call `views.open`, then resolve on `view_submission`). */ export declare const HITL_FREEFORM_ACTION_PREFIX = "eve_input_freeform:"; /** * `view.callback_id` carried on the freeform-answer modal. Used to * route the inbound `view_submission` back to this channel. */ export declare const HITL_FREEFORM_MODAL_CALLBACK_ID = "eve_input_freeform_submit"; /** * `block_id` of the modal's text-input block — the route reads the * submitted text out of `view.state.values[block_id][action_id]`. */ export declare const HITL_FREEFORM_MODAL_BLOCK_ID = "eve_freeform_block"; /** * `action_id` of the text input inside the freeform answer modal. */ export declare const HITL_FREEFORM_MODAL_ACTION_ID = "eve_freeform_text"; /** * Subset of one Slack interactivity action the HITL decoder reads. * Mirrors the relevant fields of `SlackInteractionAction`. */ interface SlackHitlAction { readonly actionId: string; /** `value` field on Slack `button` payloads. */ readonly value?: string; /** `selected_option.value` field on radio / static-select payloads. */ readonly selectedOptionValue?: string; } /** * Resolved HITL response derived from one Slack interactivity action. * Matches the `InputResponse` contract minus `text` — freeform answers * come back through a different interaction path. */ interface DerivedHitlResponse { readonly requestId: string; readonly optionId: string; } /** * Decodes one Slack interactivity action into an HITL response, or * returns `null` when the action does not match an HITL widget the * framework rendered. */ export declare function deriveHitlResponse(action: SlackHitlAction): DerivedHitlResponse | null; /** * Returns `true` when the action id was minted by an HITL widget the * framework rendered. Used by the channel route to split inbound * clicks into the HITL path vs. the user-owned `onInteraction` path. */ export declare function isHitlAction(actionId: string): boolean; /** * Renders one `InputRequest` as Block Kit blocks: * * - `display === "select"` with ≤ {@link RADIO_SELECT_OPTION_LIMIT} * options → `radio_buttons`. Single-click answer, options stay * visible. * - `display === "select"` with more options → `static_select` * dropdown so the picker stays scrollable. * - Anything else with options → buttons. Best for visually distinct * choices (approve / deny / cancel). * - No options (or `allowFreeform: true`) → a single "Type your answer" * button that opens a Slack modal with a plain_text_input. The modal * submission comes back as a `view_submission` webhook the channel * resolves into an {@link InputResponse} carrying `text`. * * Always emits at least the prompt section. */ export declare function renderInputRequestBlocks(request: InputRequest): unknown[]; /** * Metadata round-tripped on the freeform-answer modal's * `private_metadata` field. Threaded from the button click that opens * the modal to the `view_submission` that closes it so the route can * deliver the answer back to the right session. */ export interface HitlFreeformModalMetadata { readonly continuationToken: string; readonly channelId: string; readonly threadTs: string; readonly messageTs: string; readonly requestId: string; } /** * Builds the `views.open` payload for the freeform-answer modal. The * triggering `prompt` is preserved as a header section so the user can * re-read what they're answering inside the modal. * * Title is auto-truncated to the Slack modal-title limit. */ export declare function buildFreeformModalView(input: { readonly metadata: HitlFreeformModalMetadata; readonly prompt?: string; }): Record<string, unknown>; /** * True when an `action_id` was minted by the framework's freeform-answer * button (the click that opens a modal — not the final answer). */ export declare function isFreeformAction(actionId: string): boolean; /** * Extracts the requestId from a freeform-answer button's `action_id`. */ export declare function freeformRequestIdFromActionId(actionId: string): string | undefined; /** * Renders the "answered" replacement blocks for a previously-posted * HITL card. Preserves the original prompt block (so context stays * visible), appends a confirmation line naming the chosen answer, and * attributes the click to the user when their id is known. * * Slack's `chat.update` replaces every block in one shot, so the caller * passes the full list to `blocks` and the rendered fallback text to * `text`. */ export declare function buildAnsweredBlocks(input: { readonly promptBlock: unknown; readonly answerLabel: string; readonly userId?: string; }): unknown[]; export {};