Skip to main content

Human API

The db.human module handles requests for human input, confirmation, or approval within workflows.

Contract

  • Sync: All methods are synchronous.
  • Return shape: snake_case fields; options, response, and session_config are parsed JSON.
  • Status: pending | approved | rejected | timeout | cancelled | completed | failed.

Overview

import { createSmithersDB } from "smithers-orchestrator";

const db = createSmithersDB({ path: ".smithers/my-workflow" });

// Create a human interaction request
const requestId = db.human.request("confirmation", "Deploy to production?");

// External harness resolves the request
db.human.resolve(requestId, "approved", { confirmed: true });

Methods

request

Creates a human interaction request.
const requestId = db.human.request(
  type: string,
  prompt: string,
  options?: string[]
): string
type
string
required
The type of interaction (e.g., “confirmation”, “select”, “input”).
prompt
string
required
The prompt to display to the human.
options
string[]
Optional list of choices for selection-type interactions.
Returns: The request ID.

resolve

Resolves a pending request (called by external harness or UI).
db.human.resolve(
  id: string,
  status: 'approved' | 'rejected',
  response?: unknown
): void
id
string
required
The request ID to resolve.
status
'approved' | 'rejected'
required
Whether the human approved or rejected the request.
response
unknown
Optional response data from the human.
resolve() is for non-interactive requests. Use completeInteractive() for interactive sessions.

requestInteractive

Starts an interactive human session (multi-turn).
const sessionId = db.human.requestInteractive(
  prompt: string,
  config: InteractiveSessionConfig
): string

completeInteractive

Completes an interactive session with a lifecycle outcome.
db.human.completeInteractive(
  id: string,
  outcome: 'completed' | 'cancelled' | 'timeout' | 'failed',
  response: unknown,
  options?: { transcript?: string; duration?: number; error?: string }
): void

cancelInteractive

Cancels a pending interactive session.
db.human.cancelInteractive(id: string): void

get

Gets a request by ID.
const request = db.human.get(id: string): HumanInteraction | null

listPending

Lists all pending requests.
const pending = db.human.listPending(executionId?: string): HumanInteraction[]
If executionId is omitted, the current execution is used. Pass "*" to list pending requests across all executions.

HumanInteraction Type

interface HumanInteraction {
  id: string;
  execution_id: string;
  type: 'confirmation' | 'select' | 'input' | 'text' | 'interactive_session';
  prompt: string;
  options: string[] | null;
  status:
    | 'pending'
    | 'approved'
    | 'rejected'
    | 'timeout'
    | 'cancelled'
    | 'completed'
    | 'failed';
  response: any | null;
  created_at: string;
  resolved_at: string | null;
  session_config?: InteractiveSessionConfig | null;
  session_transcript?: string | null;
  session_duration?: number | null;
  error?: string | null;
}

useHuman Hook

For components, use the useHuman hook for a more ergonomic API:
import { useHuman } from "smithers-orchestrator/hooks";

function DeploymentWorkflow() {
  const { ask, status } = useHuman();

  const handleDeploy = async () => {
    const choice = await ask<string>("Deploy to production?", {
      options: ["Yes", "No"],
    });

    if (choice === "Yes") {
      // Proceed with deployment
    }
  };

  return (
    <button onClick={handleDeploy} disabled={status === "pending"}>
      Deploy
    </button>
  );
}

useHuman API

interface UseHumanResult {
  ask: <T = any>(prompt: string, options?: AskOptions) => Promise<T>;
  status: 'idle' | 'pending' | 'resolved';
  requestId: string | null;
}

interface AskOptions {
  options?: string[];
}
Return semantics: ask() resolves to the stored response. For options, this is typically the selected option string.

useHumanInteractive Hook

Interactive human sessions for complex decisions:
import { useHumanInteractive } from "smithers-orchestrator/hooks";

function ReviewWorkflow() {
  const { requestAsync, status } = useHumanInteractive();

  const handleReview = async () => {
    const result = await requestAsync(
      "Review the refactor and decide whether to proceed.",
      { captureTranscript: true }
    );

    if (result.outcome === "completed") {
      // Proceed
    }
  };

  return (
    <button onClick={handleReview} disabled={status === "pending"}>
      Request review
    </button>
  );
}

useHumanInteractive API

interface UseHumanInteractiveResult<T = InteractiveSessionResult> {
  request: (prompt: string, options?: AskInteractiveOptions) => void;
  requestAsync: (prompt: string, options?: AskInteractiveOptions) => Promise<T>;
  status: "idle" | "pending" | "success" | "error";
  data: T | null;
  error: Error | null;
  sessionId: string | null;
  cancel: () => void;
  reset: () => void;
}

Integration Pattern

Human interaction typically follows this pattern:
  1. Workflow creates request - db.human.request() or useHuman().ask()
  2. UI polls for pending requests - db.human.listPending()
  3. Human responds via UI - User clicks approve/reject
  4. UI resolves request - db.human.resolve()
  5. Workflow continues - Promise resolves with response
For interactive sessions, use requestInteractive() and resolve via completeInteractive() or cancelInteractive().

Example: Approval Workflow

function ApprovalWorkflow() {
  const { ask } = useHuman();

  return (
    <SmithersProvider db={db} executionId={executionId}>
      <Claude
        onFinished={async (result) => {
          // Ask for human approval before committing
          const choice = await ask<string>(
            `Ready to commit these changes?\n\n${result.output}`,
            { options: ["Approve", "Reject"] }
          );

          if (choice === "Approve") {
            db.state.set("phase", "commit");
          } else {
            db.state.set("phase", "revise");
          }
        }}
      >
        Implement the feature.
      </Claude>
    </SmithersProvider>
  );
}