Actions and fields

Import from @hitl-sdk/hitl. Safe inside and outside workflow functions.

import { actions, field, isResolved } from "@hitl-sdk/hitl";

actions()

Build an ordered list of reviewer buttons. Index 0 renders first (left / top).

Signature

actions(): ActionsBuilder<[]>

// Builder methods (chainable)
.approve(opts?: HumanActionOpts): ActionsBuilder
.deny(opts?: HumanActionOpts): ActionsBuilder
.custom<Id>(id: Id, opts?: HumanActionOpts): ActionsBuilder
.build(): HumanActions

HumanActionOpts

OptionTypeDefaultDescription
labelstring"Approve" / "Deny" / idButton label
submitLabelstringlabelModal submit button
closeLabelstring"Cancel"Modal cancel button
style"primary" | "danger" | "default"primary for approve, danger for denyVisual style
fieldsRecord<string, HitlField>noneEditable fields on this action

Example

const approvalActions = actions()
  .approve({
    label: "Send",
    fields: {
      subject: field.textField({ label: "Subject", default: draft.subject }),
    },
  })
  .deny({
    label: "Reject",
    fields: { reason: field.textArea({ label: "Reason" }) },
  })
  .custom("request_info", {
    label: "Request info",
    fields: { question: field.textArea({ label: "What's blocking?" }) },
  })
  .build();

Pass approvalActions to waitForHuman or requestHuman. The builder preserves literal action ids for TypeScript inference on results.

field

Define reviewer-editable fields attached to an action.

Methods

MethodReturnsValue type
field.textField({ label, default? })TextFieldstring
field.textArea({ label, default? })TextAreaFieldstring
field.select({ label, options, default? })SelectField<O>union of option strings
field.confirm({ label, default? })ConfirmFieldboolean

Example

fields: {
  amount: field.textField({ label: "Amount", default: "1200" }),
  category: field.select({
    label: "Category",
    options: ["travel", "software", "other"] as const,
  }),
  acknowledged: field.confirm({ label: "I reviewed the receipt" }),
}

FeedbackValues<F> infers the shape of result.feedbacks from your field definitions.

isResolved

Type guard that narrows HumanResult to a specific action.

Signature

isResolved<Actions, Id extends Actions[number]["id"]>(
  result: HumanResult<Actions>,
  actionId: Id,
): result is Extract<HumanResult<Actions>, { actionId: Id }>

Example

const result = await waitForHuman({ message: "Deploy?", actions: approvalActions });

if (!isResolved(result, "approve")) {
  // result is TIMED_OUT, deny, request_info, etc.
  return;
}

// result.actionId is "approve"
// result.feedbacks is typed to approve action fields
const { subject } = result.feedbacks;

HumanResult shape

When type === "RESOLVED":

FieldTypeDescription
actionIdaction id literalWhich button the reviewer chose
feedbacksFeedbackValues<F>Submitted field values for that action
editedboolean?True when reviewer changed field defaults
byReviewer?Reviewer identity when available
idstringRequest id
externalRefstringAdapter-native message ref

When type === "TIMED_OUT": only id and externalRef are set.

See also