> ## Documentation Index
> Fetch the complete documentation index at: https://smithers-feat-claude-workflow-mirror.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Types

> Public TypeScript surface for smithers-orchestrator.

One source of truth: `tsc --emitDeclarationOnly` would produce something close to this. Import these types from `smithers-orchestrator` unless noted otherwise.

`createSmithers` is a **named export**:

```ts theme={null}
import { createSmithers } from "smithers-orchestrator";
```

It returns a local API object for that workflow module. Destructure the pieces
you use (`Workflow`, `Task`, `Branch`, `Sandbox`, `smithers`, `outputs`) or keep
the object and use property access (`api.Workflow`, `api.Task`). Both styles are
equivalent for one factory; property access is clearer in files with multiple
factories, such as parent/child workflow examples.

There is no top-level default `smithers` export. The `smithers` wrapper function
is the property returned by `createSmithers(...)`, and
`smithers((ctx) => <Workflow ... />)` returns the `SmithersWorkflow` value that
workflow files usually export as their default export.

`input` is a reserved schema key on `createSmithers(...)`. It controls the
TypeScript type of `ctx.input`; every other key is an output schema and becomes
a typed output ref under `outputs.<key>`. Fresh runs validate the input row
against the generated input table before inserting it, but Smithers does **not**
call `inputSchema.parse()` and does not write Zod default values back into
`ctx.input`. Treat required fields in the Zod schema as the authored TypeScript
contract; for fields that may be omitted by a CLI/UI caller, use `.optional()` /
`.nullable()` and coalesce inside the workflow (`ctx.input.dryRun ?? false`) or
add an early compute task that performs stricter Zod parsing.

Prefer `output={outputs.someKey}` on tasks, approvals, sandboxes, and subflows.
The `outputs` object contains the exact Zod schema objects passed to
`createSmithers(...)`, so the task can infer `outputSchema`, validate the
returned row, persist it to the matching table, and feed the same schema to
native structured-output agents.

Major sections at a glance:

* **Workflow / Context**: `SmithersWorkflow`, `SmithersCtx`, `RunOptions`, `RunResult`, the entry points for defining and running workflows.
* **Task / Graph**: `TaskDescriptor`, `TaskProps`, `GraphSnapshot`, the shape of nodes at runtime and in the JSX layer.
* **Component props**: `WorkflowProps`, `ApprovalProps`, `SignalProps`, `LoopProps`, etc., all JSX component interfaces.
* **Errors**: `SmithersError`, `KnownSmithersErrorCode`, typed error codes; see [Errors](/reference/errors) for descriptions.
* **Server / Gateway**: `ServerOptions`, `GatewayOptions`, `GatewayAuthConfig`, self-hosting configuration.
* **Scorers / Memory / OpenAPI / Observability**: sub-path imports (`smithers-orchestrator/scorers`, `/memory`, `/openapi`, `/observability`).

```ts theme={null}
// =============================================================================
// Workflow
// =============================================================================

interface SmithersWorkflow<Schema = unknown> {
  readonly readableName?: string;
  readonly description?: string;
  readonly db?: unknown;
  readonly build: (ctx: SmithersCtx<Schema>) => JSX.Element;
  readonly opts: SmithersWorkflowOptions;
  readonly schemaRegistry?: Map<string, SchemaRegistryEntry>;
  readonly zodToKeyName?: Map<import("zod").ZodObject<import("zod").ZodRawShape>, string>;
}

type SmithersWorkflowOptions = {
  alertPolicy?: SmithersAlertPolicy;
  cache?: boolean;
  workflowHash?: string;
};

type SchemaRegistryEntry = {
  table: any;
  zodSchema: import("zod").ZodObject<any>;
};

type SmithersAlertPolicy = {
  defaults?: SmithersAlertPolicyDefaults;
  rules?: Record<string, SmithersAlertPolicyRule>;
  reactions?: Record<string, SmithersAlertReaction>;
};

type SmithersAlertSeverity = "info" | "warning" | "critical";
type SmithersAlertLabels = Record<string, string>;
type SmithersAlertPolicyDefaults = {
  owner?: string;
  severity?: SmithersAlertSeverity;
  runbook?: string;
  labels?: SmithersAlertLabels;
};
type SmithersAlertPolicyRule = SmithersAlertPolicyDefaults & {
  afterMs?: number;
  reaction?: string | SmithersAlertReaction;
};
type SmithersAlertReaction =
  | { kind: "emit-only" }
  | { kind: "pause" }
  | { kind: "cancel" }
  | { kind: "open-approval" }
  | { kind: "deliver"; destination: string };

// =============================================================================
// Context
// =============================================================================

declare class SmithersCtx<Schema extends unknown = unknown> {
  runId: string;
  iteration: number;
  iterations: Record<string, number> | undefined;
  input: Schema extends { input: infer T } ? T : unknown;
  auth: RunAuthContext | null;
  outputs: OutputAccessor<Schema>;

  // table is the schema key or output target from createSmithers,
  // such as "review" or outputs.review, not the generated SQL table name.
  output(table: any, key: OutputKey): any;
  outputMaybe(table: any, key: OutputKey): any | undefined;
  latest(table: any, nodeId: string): any | undefined;
  latestArray(value: unknown, schema: SafeParser): unknown[];
  iterationCount(table: any, nodeId: string): number;
  resolveTableName(table: any): string;
  resolveRow(table: any, key: OutputKey): any | undefined;
}

type OutputKey = { nodeId: string; iteration?: number };
type SafeParser = {
  safeParse(value: unknown):
    | { success: true; data: unknown }
    | { success: false; error?: unknown };
};
type InferRow<TTable> = TTable extends { $inferSelect: infer R } ? R : never;
type InferOutputEntry<T> =
  T extends import("zod").ZodTypeAny ? import("zod").infer<T>
  : T extends { $inferSelect: any } ? InferRow<T>
  : never;
type FallbackTableName<Schema> = [keyof Schema & string] extends [never] ? string : never;
type OutputAccessor<Schema, TRow = unknown> = {
  (table: FallbackTableName<Schema>): Array<TRow>;
  <K extends keyof Schema & string>(table: K): Array<InferOutputEntry<Schema[K]>>;
} & {
  [K in keyof Schema & string]: Array<InferOutputEntry<Schema[K]>>;
};
type OutputSnapshot<TFallback = unknown> = {
  [tableName: string]: Array<TFallback>;
};

type RunAuthContext = {
  triggeredBy: string;
  scopes: string[];
  role: string;
  createdAt: string;
};

// =============================================================================
// Run
// =============================================================================

type RunOptions = {
  runId?: string;
  parentRunId?: string | null;
  input: Record<string, unknown>;
  maxConcurrency?: number;          // default 4
  requireRerenderOnOutputChange?: boolean;  // default true; re-render the frame on every task completion
  onProgress?: (e: SmithersEvent) => void;
  signal?: AbortSignal;
  resume?: boolean;
  force?: boolean;                  // resume even if marked running
  workflowPath?: string;
  rootDir?: string;
  logDir?: string | null;
  allowNetwork?: boolean;           // default false; bash tool network access
  maxOutputBytes?: number;          // default 200000
  toolTimeoutMs?: number;           // default 60000
  hot?: boolean | HotReloadOptions;
  annotations?: Record<string, string | number | boolean>;
  auth?: RunAuthContext | null;
  config?: Record<string, unknown>;
  cliAgentToolsDefault?: "all" | "explicit-only";  // default "all"
  initialOutputs?: OutputSnapshot;  // seed prior outputs (resume/fork)
  initialIteration?: number;        // seed the starting loop iteration
  initialIterations?: Record<string, number> | ReadonlyMap<string, number>;  // per-loop iteration seeds
  resumeClaim?: {                   // internal supervisor coordination
    claimOwnerId: string;
    claimHeartbeatAtMs: number;
    restoreRuntimeOwnerId?: string | null;
    restoreHeartbeatAtMs?: number | null;
  };
};

type HotReloadOptions = {
  rootDir?: string;
  outDir?: string;                  // default .smithers/hmr under rootDir
  maxGenerations?: number;          // default 3
  cancelUnmounted?: boolean;        // default false
  debounceMs?: number;              // default 100
};

type RunResult = {
  readonly runId: string;
  readonly status: RunStatus;
  readonly output?: unknown;
  readonly error?: unknown;
  readonly nextRunId?: string;      // set when the run continued-as-new
};

type RunStatus =
  | "running"
  | "waiting-approval"
  | "waiting-event"
  | "waiting-timer"
  | "waiting-quota"
  | "finished"
  | "continued"
  | "failed"
  | "cancelled";

type RetryTaskOptions = {
  runId: string;
  nodeId: string;
  iteration?: number;
  resetDependents?: boolean;        // default true
  force?: boolean;                  // default false
  onProgress?: (e: SmithersEvent) => void;
};

type RetryTaskResult = {
  success: boolean;
  resetNodes: string[];
  error?: string;
};

// =============================================================================
// Task
// =============================================================================

type TaskDescriptor = {
  nodeId: string;
  ordinal: number;
  iteration: number;
  ralphId?: string;
  dependsOn?: string[];
  needs?: Record<string, string>;
  forkSource?: string;              // logical id of the task whose session this task forks
  worktreeId?: string;
  worktreePath?: string;
  worktreeBranch?: string;
  worktreeBaseBranch?: string;
  outputTable: unknown | null;
  outputTableName: string;
  outputRef?: import("zod").ZodObject<any>;
  outputSchema?: import("zod").ZodObject<any>;
  parallelGroupId?: string;
  parallelMaxConcurrency?: number;
  needsApproval: boolean;
  waitAsync?: boolean;
  approvalMode?: "gate" | "decision" | "select" | "rank";
  approvalOnDeny?: "fail" | "continue" | "skip";
  approvalOptions?: ApprovalOption[];
  approvalAllowedScopes?: string[];
  approvalAllowedUsers?: string[];
  approvalAutoApprove?: {
    after?: number;
    audit?: boolean;
    conditionMet?: boolean;
    revertOnMet?: boolean;
  };
  skipIf: boolean;
  retries: number;
  retryPolicy?: RetryPolicy;
  timeoutMs: number | null;
  heartbeatTimeoutMs: number | null;
  continueOnFail: boolean;
  cachePolicy?: CachePolicy;
  hijack?: boolean;
  onHijackExit?: "complete" | "reopen";
  agent?: AgentLike | AgentLike[];
  prompt?: string;
  staticPayload?: unknown;
  computeFn?: () => unknown | Promise<unknown>;
  label?: string;
  meta?: Record<string, unknown>;
  scorers?: ScorersMap;
  memoryConfig?: TaskMemoryConfig;
};

type RetryPolicy = {
  backoff?: "fixed" | "linear" | "exponential";   // default "fixed"
  initialDelayMs?: number;                         // default 0
};

type CachePolicy<Ctx = unknown> = {
  by?: (ctx: Ctx) => unknown;
  version?: string;
  key?: string;
  ttlMs?: number;
  scope?: "run" | "workflow" | "global";
  [key: string]: unknown;
};

type AgentToolDescriptor = {
  description?: string;
  source?: "builtin" | "mcp" | "extension" | "skill" | "runtime";
};

type AgentCapabilityRegistry = {
  version: 1;
  engine: "claude-code" | "codex" | "antigravity" | "gemini" | "kimi" | "pi" | "amp" | "forge" | "opencode" | "vibe";
  runtimeTools: Record<string, AgentToolDescriptor>;
  mcp: {
    bootstrap: "inline-config" | "project-config" | "allow-list" | "unsupported";
    supportsProjectScope: boolean;
    supportsUserScope: boolean;
  };
  skills: {
    supportsSkills: boolean;
    installMode?: "files" | "dir" | "plugin";
    smithersSkillIds: string[];
  };
  humanInteraction: {
    supportsUiRequests: boolean;
    methods: string[];
  };
  builtIns: string[];
};

type AgentGenerateOptions = {
  prompt?: unknown;
  messages?: unknown;
  timeout?: unknown;
  abortSignal?: AbortSignal;
  rootDir?: string;
  resumeSession?: string;
  maxOutputBytes?: number;
  onStdout?: (text: string) => void;
  onStderr?: (text: string) => void;
  onEvent?: (event: unknown) => unknown;
  retry?: unknown;
  isRetry?: unknown;
  retryAttempt?: unknown;
  schemaRetry?: unknown;
  taskContext?: {
    runId?: string;
    nodeId?: string;
    iteration?: number;
    attempt?: number;
  };
  [key: string]: unknown;
};

type AgentLike = {
  id?: string;
  tools?: Record<string, unknown>;
  supportsNativeStructuredOutput?: boolean;
  capabilities?: AgentCapabilityRegistry;
  generate: (args?: AgentGenerateOptions) => Promise<unknown>;
};

type SdkAgentOptions<CALL_OPTIONS = never, TOOLS extends import("ai").ToolSet = {}, MODEL = any> =
  Omit<import("ai").ToolLoopAgentSettings<CALL_OPTIONS, TOOLS>, "model"> & {
    model: string | MODEL;
  };

type AnthropicAgentOptions<CALL_OPTIONS = never, TOOLS extends import("ai").ToolSet = {}> =
  SdkAgentOptions<CALL_OPTIONS, TOOLS, ReturnType<typeof import("@ai-sdk/anthropic").anthropic>>;

type OpenAIAgentOptions<CALL_OPTIONS = never, TOOLS extends import("ai").ToolSet = {}> =
  Omit<SdkAgentOptions<CALL_OPTIONS, TOOLS, ReturnType<typeof import("@ai-sdk/openai").openai>>, "model"> & {
    nativeStructuredOutput?: boolean;
  } & (
    | { model: string; baseURL?: string; apiKey?: string }
    | { model: ReturnType<typeof import("@ai-sdk/openai").openai>; baseURL?: never; apiKey?: never }
  );

type HermesAgentOptions<CALL_OPTIONS = never, TOOLS extends import("ai").ToolSet = {}> =
  Omit<SdkAgentOptions<CALL_OPTIONS, TOOLS, ReturnType<typeof import("@ai-sdk/openai").openai>>, "model"> & {
    model?: string;                 // default "hermes"
    baseURL?: string;               // falls back to HERMES_BASE_URL; required at runtime
    apiKey?: string;                // falls back to HERMES_API_KEY, then "hermes"
    nativeStructuredOutput?: boolean; // default false
  };

type BaseCliAgentOptions = {
  id?: string;
  model?: string;
  systemPrompt?: string;
  instructions?: string;
  cwd?: string;
  env?: Record<string, string>;
  yolo?: boolean;
  timeoutMs?: number;
  idleTimeoutMs?: number;
  maxOutputBytes?: number;
  extraArgs?: string[];
};

type PiExtensionUiRequest = {
  type: "extension_ui_request";
  id: string;
  method: string;
  title?: string;
  placeholder?: string;
  [key: string]: unknown;
};

type PiExtensionUiResponse = {
  type: "extension_ui_response";
  id: string;
  value?: string;
  cancelled?: boolean;
  [key: string]: unknown;
};

type PiAgentOptions = BaseCliAgentOptions & {
  provider?: string;
  model?: string;
  apiKey?: string;
  systemPrompt?: string;
  appendSystemPrompt?: string;
  mode?: "text" | "json" | "rpc";
  print?: boolean;
  continue?: boolean;
  resume?: boolean;
  session?: string;
  sessionDir?: string;
  noSession?: boolean;
  models?: string | string[];
  listModels?: boolean | string;
  tools?: string[];
  noTools?: boolean;
  extension?: string[];
  noExtensions?: boolean;
  skill?: string[];
  noSkills?: boolean;
  promptTemplate?: string[];
  noPromptTemplates?: boolean;
  theme?: string[];
  noThemes?: boolean;
  thinking?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
  export?: string;
  files?: string[];
  verbose?: boolean;
  onExtensionUiRequest?: (
    request: PiExtensionUiRequest,
  ) => Promise<PiExtensionUiResponse | null> | PiExtensionUiResponse | null;
};

type VibeAgentOptions = BaseCliAgentOptions & {
  agent?: string;
  maxTurns?: number;
  maxPrice?: number;
  maxTokens?: number;
  enabledTools?: string[];
  sessionId?: string;
  continueSession?: boolean;
};

type OpenCodeAgentOptions = BaseCliAgentOptions & {
  model?: string;
  agentName?: string;
  attachFiles?: string[];
  continueSession?: boolean;
  sessionId?: string;
  variant?: string;
};

type TaskMemoryConfig = {
  namespace?: string | MemoryNamespace;
  recall?: { namespace?: MemoryNamespace; query?: string; topK?: number };
  remember?: { namespace?: MemoryNamespace; key?: string };
  threadId?: string;
};

type MemoryNamespace = { kind: MemoryNamespaceKind; id: string };
type MemoryNamespaceKind = "workflow" | "agent" | "user" | "global";

// =============================================================================
// Graph
// =============================================================================

type GraphSnapshot = {
  readonly runId: string;
  readonly frameNo: number;
  readonly xml: XmlNode | null;
  readonly tasks: readonly TaskDescriptor[];
};

type XmlNode = XmlElement | XmlText;

type XmlElement = {
  readonly kind: "element";
  readonly tag: string;             // "Workflow" | "Task" | "Sequence" | ...
  readonly props: Record<string, string>;
  readonly children: readonly XmlNode[];
};

type XmlText = { readonly kind: "text"; readonly text: string };

// =============================================================================
// Events
// =============================================================================
//
// `SmithersEvent` is the discriminated union understood by the runtime and
// observability layer. Most variants are emitted by the runtime; reserved
// variants are called out in Event Types. The full union is documented
// separately to keep this file usable as the everyday type reference.
//
// See: /reference/event-types (rendered) or /llms-full.txt (LLM bundle).

type SmithersEvent = { type: string; runId: string; timestampMs: number } & Record<string, unknown>;
// (Each variant has additional fields per its `type`. See event-types.)

// =============================================================================
// Component props
// =============================================================================

type WorkflowProps = {
  name: string;
  cache?: boolean;
  children?: React.ReactNode;
};

// OutputTarget accepts a Zod output schema (recommended, usually outputs.key),
// a custom Drizzle table object, or a string schema key escape hatch.
type OutputTarget = import("zod").ZodObject<any> | { $inferSelect: any } | string;
type DepsSpec = Record<string, OutputTarget>;
type InferDeps<D extends DepsSpec> = {
  [K in keyof D]: D[K] extends string ? unknown : InferOutputEntry<D[K]>;
};

type TaskProps<Row, Output extends OutputTarget = OutputTarget, D extends DepsSpec = {}> = {
  key?: string;
  id: string;
  output: Output;
  outputSchema?: import("zod").ZodObject<any>;
  agent?: AgentLike | AgentLike[];
  fallbackAgent?: AgentLike;
  dependsOn?: string[];
  needs?: Record<string, string>;
  deps?: D;
  fork?: string;                    // start from another task's final agent session snapshot
  skipIf?: boolean;
  needsApproval?: boolean;
  async?: boolean;                  // only with needsApproval
  timeoutMs?: number;
  heartbeatTimeoutMs?: number;
  heartbeatTimeout?: number;       // alias for heartbeatTimeoutMs
  noRetry?: boolean;
  retries?: number;                 // default Infinity (set 0 to disable)
  retryPolicy?: RetryPolicy;
  continueOnFail?: boolean;
  cache?: CachePolicy;
  scorers?: ScorersMap;
  memory?: TaskMemoryConfig;
  hijack?: boolean;
  onHijackExit?: "complete" | "reopen";
  allowTools?: string[];            // CLI-agent tool allowlist
  label?: string;
  meta?: Record<string, unknown>;
  // string = prompt literal; Row = static result; () => Row = compute fn;
  // (deps) => result = deps-aware fn; React.ReactNode = JSX subtree
  children: string | Row | (() => Row | Promise<Row>) | React.ReactNode | ((deps: InferDeps<D>) => Row | React.ReactNode);
};

type SequenceProps  = { key?: string; skipIf?: boolean; children?: React.ReactNode };
type ParallelProps  = { id?: string; maxConcurrency?: number; skipIf?: boolean; children?: React.ReactNode };
type BranchProps    = { if: boolean; then: React.ReactElement; else?: React.ReactElement | null; skipIf?: boolean };
type LoopProps      = {
  key?: string;
  id?: string;
  until?: boolean;
  maxIterations?: number;
  onMaxReached?: "fail" | "return-last";   // default "return-last"
  continueAsNewEvery?: number;
  skipIf?: boolean;
  children?: React.ReactNode;
};
type RalphProps     = LoopProps;            // deprecated alias

type ApprovalDecision  = { approved: boolean; note: string | null; decidedBy: string | null; decidedAt: string | null };
type ApprovalSelection = { selected: string; notes: string | null };
type ApprovalRanking   = { ranked: string[]; notes: string | null };
type ApprovalRequest   = { title: string; summary?: string; metadata?: Record<string, unknown> };
type ApprovalMode      = "approve" | "select" | "rank";
type ApprovalOption    = { key: string; label: string; summary?: string; metadata?: Record<string, unknown> };
type ApprovalAutoApprove = {
  after?: number;
  condition?: ((ctx: SmithersCtx<unknown> | null) => boolean) | (() => boolean);
  audit?: boolean;
  revertOn?: ((ctx: SmithersCtx<unknown> | null) => boolean) | (() => boolean);
};

type ApprovalProps<Row = ApprovalDecision, Output extends OutputTarget = OutputTarget> = {
  id: string;
  mode?: ApprovalMode;
  options?: ApprovalOption[];
  output: Output;
  outputSchema?: import("zod").ZodObject<any>;
  request: ApprovalRequest;
  onDeny?: "fail" | "continue" | "skip";
  allowedScopes?: string[];
  allowedUsers?: string[];
  autoApprove?: ApprovalAutoApprove;
  async?: boolean;
  dependsOn?: string[];
  needs?: Record<string, string>;
  skipIf?: boolean;
  timeoutMs?: number;
  heartbeatTimeoutMs?: number;
  heartbeatTimeout?: number;       // alias for heartbeatTimeoutMs
  retries?: number;
  retryPolicy?: RetryPolicy;
  continueOnFail?: boolean;
  cache?: CachePolicy;
  label?: string;
  meta?: Record<string, unknown>;
  key?: string;
  children?: React.ReactNode;
};

type SignalProps<S extends import("zod").ZodObject<any> = import("zod").ZodObject<any>> = {
  id: string;
  schema: S;
  correlationId?: string;
  timeoutMs?: number;
  onTimeout?: "fail" | "skip" | "continue";
  async?: boolean;
  skipIf?: boolean;
  dependsOn?: string[];
  needs?: Record<string, string>;
  label?: string;
  meta?: Record<string, unknown>;
  key?: string;
  children?: (data: import("zod").infer<S>) => React.ReactNode;
};

type WaitForEventProps = {
  id: string;
  event: string;
  correlationId?: string;
  output: OutputTarget;
  outputSchema?: import("zod").ZodObject<any>;
  timeoutMs?: number;
  onTimeout?: "fail" | "skip" | "continue";
  async?: boolean;
  skipIf?: boolean;
  dependsOn?: string[];
  needs?: Record<string, string>;
  label?: string;
  meta?: Record<string, unknown>;
  key?: string;
};

type TimerProps = {
  id: string;
  duration?: string;                // e.g. "30s", "5m"
  until?: string | Date;            // absolute timestamp
  every?: string;                   // reserved; recurring timers are not supported yet
  skipIf?: boolean;
  dependsOn?: string[];
  needs?: Record<string, string>;
  label?: string;
  meta?: Record<string, unknown>;
  key?: string;
};

type SagaStepDef    = { id: string; action: React.ReactElement; compensation: React.ReactElement; label?: string };
type SagaProps      = { id?: string; steps?: SagaStepDef[]; onFailure?: "compensate" | "compensate-and-fail" | "fail"; skipIf?: boolean; children?: React.ReactNode };
type SagaStepProps  = { id: string; compensation: React.ReactElement; children: React.ReactElement };

type TryCatchFinallyProps = {
  id?: string;
  try: React.ReactElement;
  catch?: React.ReactElement | ((error: SmithersError) => React.ReactElement);
  catchErrors?: SmithersErrorCode[];
  finally?: React.ReactElement;
  skipIf?: boolean;
};

// Higher-level composites
type PollerProps = {
  id?: string;
  check: AgentLike | (() => unknown | Promise<unknown>);
  checkOutput: OutputTarget;
  maxAttempts?: number;
  backoff?: "fixed" | "linear" | "exponential";
  intervalMs?: number;
  onTimeout?: "fail" | "return-last";
  skipIf?: boolean;
  children?: React.ReactNode;
};

type ColumnTaskProps = Omit<Partial<TaskProps<unknown>>, "agent" | "children" | "id" | "key" | "output" | "smithersContext">;

type ColumnDef = {
  name: string;
  agent: AgentLike;
  output: OutputTarget;
  prompt?: (ctx: { item: unknown; column: string }) => string;
  task?: ColumnTaskProps;
};

type KanbanProps = {
  id?: string;
  columns: ColumnDef[];
  useTickets: () => Array<{ id: string; [key: string]: unknown }>;
  agents?: Record<string, AgentLike>;
  maxConcurrency?: number;
  onComplete?: OutputTarget;
  until?: boolean;
  maxIterations?: number;
  skipIf?: boolean;
  children?: React.ReactNode | Record<string, unknown>;
};

type ApprovalGateProps = {
  id: string;
  output: OutputTarget;
  request: ApprovalRequest;
  when: boolean;                    // false auto-approves
  onDeny?: "fail" | "continue" | "skip";
  skipIf?: boolean;
  timeoutMs?: number;
  heartbeatTimeoutMs?: number;
  heartbeatTimeout?: number;
  retries?: number;
  retryPolicy?: RetryPolicy;
  continueOnFail?: boolean;
};

type HumanTaskProps = {
  id: string;
  output: OutputTarget;
  outputSchema?: import("zod").ZodObject<any>;
  prompt: string | React.ReactNode;
  maxAttempts?: number;
  async?: boolean;
  skipIf?: boolean;
  timeoutMs?: number;
  continueOnFail?: boolean;
  dependsOn?: string[];
  needs?: Record<string, string>;
  label?: string;
  meta?: Record<string, unknown>;
  key?: string;
};

type CheckConfig = {
  id: string;
  agent?: AgentLike;
  command?: string;
  label?: string;
};
type CheckSuiteProps = {
  id?: string;
  checks: CheckConfig[] | Record<string, Omit<CheckConfig, "id">>;
  verdictOutput: OutputTarget;
  strategy?: "all-pass" | "majority" | "any-pass";
  maxConcurrency?: number;
  continueOnFail?: boolean;
  skipIf?: boolean;
};

type TokenBudgetConfig = { max: number; perTask?: number; onExceeded?: "fail" | "warn" | "skip-remaining" };
type LatencySloConfig = { maxMs: number; perTask?: number; onExceeded?: "fail" | "warn" };
type TrackingConfig = { tokens?: boolean; latency?: boolean };
type AspectsProps = {
  tokenBudget?: TokenBudgetConfig;
  latencySlo?: LatencySloConfig;
  tracking?: TrackingConfig;
  children?: React.ReactNode;
};

type CategoryConfig = {
  agent: AgentLike;
  output?: OutputTarget;
  prompt?: (item: unknown) => string;
};
type ClassifyAndRouteProps = {
  id?: string;
  items: unknown | unknown[];
  categories: Record<string, AgentLike | CategoryConfig>;
  classifierAgent: AgentLike;
  classifierOutput: OutputTarget;
  routeOutput: OutputTarget;
  classificationResult?: {
    classifications: Array<{ itemId?: string; category: string; [key: string]: unknown }>;
  } | null;
  maxConcurrency?: number;
  skipIf?: boolean;
  children?: React.ReactNode;
};

type SourceDef = {
  agent: AgentLike;
  prompt?: string;
  output?: OutputTarget;
  children?: React.ReactNode;
};
type GatherAndSynthesizeProps = {
  id?: string;
  sources: Record<string, SourceDef>;
  synthesizer: AgentLike;
  gatherOutput: OutputTarget;
  synthesisOutput: OutputTarget;
  gatheredResults?: Record<string, unknown> | null;
  maxConcurrency?: number;
  synthesisPrompt?: string;
  skipIf?: boolean;
  children?: React.ReactNode;
};

type ContentPipelineStage = { id: string; agent: AgentLike; output: OutputTarget; label?: string };
type ContentPipelineProps = {
  id?: string;
  stages: ContentPipelineStage[];
  skipIf?: boolean;
  children: string | React.ReactNode;
};

type DebateProps = {
  id?: string;
  proposer: AgentLike;
  opponent: AgentLike;
  judge: AgentLike;
  rounds?: number;
  argumentOutput: OutputTarget;
  verdictOutput: OutputTarget;
  topic: string | React.ReactNode;
  skipIf?: boolean;
};

type DecisionRule = { when: boolean; then: React.ReactElement; label?: string };
type DecisionTableProps = {
  id?: string;
  rules: DecisionRule[];
  default?: React.ReactElement;
  strategy?: "first-match" | "all-match";
  skipIf?: boolean;
};

type DriftDetectorProps = {
  id?: string;
  captureAgent: AgentLike;
  compareAgent: AgentLike;
  captureOutput: OutputTarget;
  compareOutput: OutputTarget;
  baseline: unknown;
  alertIf?: (comparison: unknown) => boolean;
  alert?: React.ReactElement;
  poll?: { intervalMs?: number; maxPolls?: number };
  skipIf?: boolean;
};

type EscalationLevel = {
  agent: AgentLike;
  output: OutputTarget;
  label?: string;
  escalateIf?: (result: unknown) => boolean;
};
type EscalationChainProps = {
  id?: string;
  levels: EscalationLevel[];
  humanFallback?: boolean;
  humanRequest?: ApprovalRequest;
  escalationOutput: OutputTarget;
  skipIf?: boolean;
  children?: React.ReactNode;
};

type MergeQueueProps = { id?: string; maxConcurrency?: number; skipIf?: boolean; children?: React.ReactNode };

type OptimizerProps = {
  id?: string;
  generator: AgentLike;
  evaluator: AgentLike | ((candidate: unknown) => unknown | Promise<unknown>);
  generateOutput: OutputTarget;
  evaluateOutput: OutputTarget;
  targetScore?: number;
  maxIterations?: number;
  onMaxReached?: "return-last" | "fail";
  skipIf?: boolean;
  children: string | React.ReactNode;
};

type PanelistConfig = { agent: AgentLike; role?: string; label?: string };
type PanelProps = {
  id?: string;
  panelists: PanelistConfig[] | AgentLike[];
  moderator: AgentLike;
  panelistOutput: OutputTarget;
  moderatorOutput: OutputTarget;
  strategy?: "synthesize" | "vote" | "consensus";
  minAgree?: number;
  maxConcurrency?: number;
  skipIf?: boolean;
  children: string | React.ReactNode;
};

type SidecarProps = {
  id?: string;
  agent: AgentLike;
  sidecar: AgentLike;
  output: OutputTarget;
  sidecarOutput?: OutputTarget;
  scorers?: ScorersMap;
  prompt?: string | React.ReactNode;
  input?: string | React.ReactNode;
  maxConcurrency?: number;
  groundTruth?: unknown;
  context?: unknown;
  primaryLabel?: string;
  sidecarLabel?: string;
  skipIf?: boolean;
  children?: string | React.ReactNode;
};
type SidecarDelta = {
  primaryScore: number | null;
  sidecarScore: number | null;
  delta: number | null;
  cheaperWins: boolean;
};

type ReviewLoopProps = {
  id?: string;
  producer: AgentLike;
  reviewer: AgentLike | AgentLike[];
  produceOutput: OutputTarget;
  reviewOutput: OutputTarget;
  maxIterations?: number;
  onMaxReached?: "return-last" | "fail";
  skipIf?: boolean;
  children: string | React.ReactNode;
};

type RunbookStep = {
  id: string;
  agent?: AgentLike;
  command?: string;
  risk: "safe" | "risky" | "critical";
  label?: string;
  output?: OutputTarget;
};
type RunbookProps = {
  id?: string;
  steps: RunbookStep[];
  defaultAgent?: AgentLike;
  stepOutput: OutputTarget;
  approvalRequest?: Partial<ApprovalRequest>;
  onDeny?: "fail" | "skip";
  skipIf?: boolean;
};

type ScanFixVerifyProps = {
  id?: string;
  scanner: AgentLike;
  fixer: AgentLike | AgentLike[];
  verifier: AgentLike;
  scanOutput: OutputTarget;
  fixOutput: OutputTarget;
  verifyOutput: OutputTarget;
  reportOutput: OutputTarget;
  maxConcurrency?: number;
  maxRetries?: number;
  skipIf?: boolean;
  children?: React.ReactNode;
};

type SupervisorProps = {
  id?: string;
  boss: AgentLike;
  workers: Record<string, AgentLike>;
  planOutput: OutputTarget;
  workerOutput: OutputTarget;
  reviewOutput: OutputTarget;
  finalOutput: OutputTarget;
  maxIterations?: number;
  maxConcurrency?: number;
  useWorktrees?: boolean;
  skipIf?: boolean;
  children: string | React.ReactNode;
};

type ContinueAsNewProps = { state?: unknown };

// Sandbox
type SandboxRuntime = "bubblewrap" | "docker" | "codeplane";
type SandboxEgressConfig = {
  env?: Record<string, string>;
  httpProxy?: string;
  httpsProxy?: string;
  noProxy?: string | string[];
  caCertPem?: string;
  caCertPath?: string;
  secretBindings?: Record<string, string>;
};
type SandboxVolumeMount = { host: string; container: string; readonly?: boolean };
type SandboxWorkspaceSpec = {
  name: string;
  snapshotId?: string;
  idleTimeoutSecs?: number;
  persistence?: "ephemeral" | "sticky";
};
type SandboxWorkflow = {
  db?: unknown;
  build: (ctx: unknown) => unknown;
  opts?: Record<string, unknown>;
  schemaRegistry?: unknown;
  zodToKeyName?: unknown;
};
type SandboxChildWorkflowDefinition =
  | SandboxWorkflow
  | (() => SandboxWorkflow | unknown);
type ExecuteSandboxChildWorkflowOptions = {
  workflow: SandboxChildWorkflowDefinition;
  input?: unknown;
  runId?: string;
  parentRunId?: string;
  rootDir?: string;
  allowNetwork?: boolean;
  maxOutputBytes?: number;
  toolTimeoutMs?: number;
  workflowPath?: string;
  signal?: AbortSignal;
};
type ExecuteSandboxChildWorkflow = (
  parentWorkflow: SandboxWorkflow | undefined,
  options: ExecuteSandboxChildWorkflowOptions,
) => Promise<{ runId: string; status: string; output: unknown }>;
type SandboxDiffBundleLike = {
  seq: number;
  baseRef: string;
  patches: Array<{
    path: string;
    operation: "add" | "modify" | "delete";
    diff: string;
    binaryContent?: string;
  }>;
};
type SandboxProviderRequest = {
  runId: string;
  sandboxId: string;
  input?: unknown;
  rootDir: string;
  requestBundlePath: string;
  resultBundlePath: string;
  workflow: SandboxChildWorkflowDefinition;
  parentWorkflow?: SandboxWorkflow;
  executeChildWorkflow: ExecuteSandboxChildWorkflow;
  allowNetwork: boolean;
  maxOutputBytes: number;
  toolTimeoutMs: number;
  egress?: SandboxEgressConfig;
  config: Record<string, unknown>;
  signal?: AbortSignal;
  heartbeat: (data?: unknown) => void;
};
type SandboxProviderResult =
  | { bundlePath: string; remoteRunId?: string; workspaceId?: string; containerId?: string }
  | {
      status: "finished" | "failed" | "cancelled";
      output?: unknown;
      outputs?: unknown;
      runId?: string;
      remoteRunId?: string;
      workspaceId?: string;
      containerId?: string;
      diffBundle?: SandboxDiffBundleLike;
      patches?: Array<{ path: string; content: string }>;
      artifacts?: Array<{ path: string; content: string }>;
      streamLogPath?: string | null;
    };
type SandboxProvider = {
  id: string;
  run(request: SandboxProviderRequest): Promise<SandboxProviderResult> | SandboxProviderResult;
  cleanup?(request: SandboxProviderRequest): Promise<void> | void;
};
type ExecuteSandboxOptions = {
  parentWorkflow?: SandboxWorkflow;
  sandboxId: string;
  provider?: SandboxProvider | string;
  runtime?: SandboxRuntime;
  workflow: SandboxChildWorkflowDefinition;
  executeChildWorkflow: ExecuteSandboxChildWorkflow;
  applyDiffBundle?: (bundle: SandboxDiffBundleLike, targetDir: string) => Promise<void>;
  input?: unknown;
  rootDir: string;
  allowNetwork: boolean;
  maxOutputBytes: number;
  toolTimeoutMs: number;
  reviewDiffs?: boolean;
  autoAcceptDiffs?: boolean;
  allowNested?: boolean;
  config?: Record<string, unknown>;
};

type SandboxProps = {
  id: string;
  workflow?: SmithersWorkflow<unknown>;
  input?: unknown;
  output: OutputTarget;
  provider?: unknown;              // runtime accepts a provider object or registered provider id
  runtime?: SandboxRuntime;            // legacy local transports
  allowNetwork?: boolean;
  reviewDiffs?: boolean;
  autoAcceptDiffs?: boolean;
  allowNested?: boolean;
  image?: string;
  env?: Record<string, string>;
  egress?: SandboxEgressConfig;
  ports?: Array<{ host: number; container: number }>;
  volumes?: SandboxVolumeMount[];
  memoryLimit?: string;
  cpuLimit?: string;
  command?: string;
  workspace?: SandboxWorkspaceSpec;
  skipIf?: boolean;
  timeoutMs?: number;
  heartbeatTimeoutMs?: number;
  heartbeatTimeout?: number;       // alias for heartbeatTimeoutMs
  retries?: number;
  retryPolicy?: RetryPolicy;
  continueOnFail?: boolean;
  cache?: CachePolicy;
  dependsOn?: string[];
  needs?: Record<string, string>;
  label?: string;
  meta?: Record<string, unknown>;
  key?: string;
  children?: React.ReactNode;
};

type SubflowProps = {
  id: string;
  workflow: SmithersWorkflow<unknown>;
  input?: unknown;
  mode?: "childRun" | "inline";
  output: OutputTarget;
  skipIf?: boolean;
  timeoutMs?: number;
  heartbeatTimeoutMs?: number;
  heartbeatTimeout?: number;       // alias for heartbeatTimeoutMs
  retries?: number;
  retryPolicy?: RetryPolicy;
  continueOnFail?: boolean;
  cache?: CachePolicy;
  dependsOn?: string[];
  needs?: Record<string, string>;
  label?: string;
  meta?: Record<string, unknown>;
  key?: string;
  children?: React.ReactNode;
};

type WorktreeProps = {
  key?: string;
  id?: string;
  path: string;
  branch?: string;
  baseBranch?: string;             // default "main"
  skipIf?: boolean;
  children?: React.ReactNode;
};

type SuperSmithersProps = {
  id?: string;                     // default "super-smithers"; prefixes internal task ids
  strategy: string | React.ReactElement;
  agent: AgentLike;
  targetFiles?: string[];
  reportOutput?: OutputTarget;
  dryRun?: boolean;                // default false
  skipIf?: boolean;
};

// =============================================================================
// Errors
// =============================================================================
//
// Every Smithers error is a SmithersError with a typed code. See the Errors page
// for the full list of built-in codes.

declare class SmithersError extends Error {
  readonly code: SmithersErrorCode;
  readonly summary: string;
  readonly docsUrl: string;
  readonly details?: Record<string, unknown>;
  readonly cause?: unknown;
}

type SmithersErrorCode = KnownSmithersErrorCode | (string & {});
type KnownSmithersErrorCode =
  | "INVALID_INPUT" | "MISSING_INPUT" | "MISSING_INPUT_TABLE" | "RESUME_METADATA_MISMATCH"
  | "UNKNOWN_OUTPUT_SCHEMA" | "INVALID_OUTPUT" | "WORKTREE_CREATE_FAILED" | "VCS_NOT_FOUND"
  | "SNAPSHOT_NOT_FOUND" | "VCS_WORKSPACE_CREATE_FAILED" | "TASK_TIMEOUT"
  | "TASK_HIJACK_UNSUPPORTED" | "TASK_FORK_SOURCE_NOT_FOUND" | "TASK_FORK_SOURCE_NOT_COMPLETE"
  | "TASK_FORK_SESSION_UNAVAILABLE" | "TASK_FORK_CYCLE" | "RUN_NOT_FOUND" | "NODE_NOT_FOUND" | "INVALID_EVENTS_OPTIONS"
  | "SANDBOX_BUNDLE_INVALID" | "SANDBOX_BUNDLE_TOO_LARGE" | "WORKFLOW_EXECUTION_FAILED"
  | "SANDBOX_EXECUTION_FAILED" | "TASK_HEARTBEAT_TIMEOUT" | "HEARTBEAT_PAYLOAD_TOO_LARGE"
  | "HEARTBEAT_PAYLOAD_NOT_JSON_SERIALIZABLE" | "TASK_ABORTED" | "RUN_CANCELLED" | "RUN_NOT_RESUMABLE"
  | "RUN_OWNER_ALIVE" | "RUN_STILL_RUNNING" | "RUN_RESUME_CLAIM_LOST" | "RUN_RESUME_CLAIM_FAILED"
  | "RUN_RESUME_ACTIVATION_FAILED" | "RUN_HIJACKED" | "CONTINUATION_STATE_TOO_LARGE"
  | "INVALID_CONTINUATION_STATE" | "RALPH_MAX_REACHED" | "SCHEDULER_ERROR" | "SESSION_ERROR"
  | "TASK_ID_REQUIRED" | "TASK_MISSING_OUTPUT" | "DUPLICATE_ID" | "NESTED_LOOP"
  | "WORKTREE_EMPTY_PATH" | "MDX_PRELOAD_INACTIVE" | "CONTEXT_OUTSIDE_WORKFLOW"
  | "MISSING_OUTPUT" | "DEP_NOT_SATISFIED" | "ASPECT_BUDGET_EXCEEDED" | "APPROVAL_OUTSIDE_TASK"
  | "APPROVAL_OPTIONS_REQUIRED" | "WORKFLOW_MISSING_DEFAULT"
  | "TOOL_PATH_INVALID" | "TOOL_PATH_ESCAPE" | "TOOL_FILE_TOO_LARGE" | "TOOL_CONTENT_TOO_LARGE"
  | "TOOL_PATCH_TOO_LARGE" | "TOOL_PATCH_FAILED" | "TOOL_NETWORK_DISABLED"
  | "TOOL_GIT_REMOTE_DISABLED" | "TOOL_COMMAND_FAILED" | "TOOL_GREP_FAILED"
  | "AGENT_CLI_ERROR" | "AGENT_QUOTA_EXCEEDED" | "AGENT_CONFIG_INVALID" | "AGENT_RPC_FILE_ARGS" | "AGENT_BUILD_COMMAND" | "AGENT_DIAGNOSTIC_TIMEOUT"
  | "DB_MISSING_COLUMNS" | "DB_REQUIRES_BUN_SQLITE" | "DB_QUERY_FAILED" | "DB_WRITE_FAILED"
  | "SMITHERS_MIGRATION_REQUIRED"
  | "STORAGE_ERROR" | "INTERNAL_ERROR" | "PROCESS_ABORTED" | "PROCESS_TIMEOUT"
  | "PROCESS_IDLE_TIMEOUT" | "PROCESS_SPAWN_FAILED" | "TASK_RUNTIME_UNAVAILABLE"
  | "SCHEMA_CHANGE_HOT" | "HOT_OVERLAY_FAILED" | "HOT_RELOAD_INVALID_MODULE"
  | "SCORER_FAILED" | "WORKFLOW_EXISTS" | "CLI_DB_NOT_FOUND" | "CLI_AGENT_UNSUPPORTED"
  | "PI_HTTP_ERROR" | "EXTERNAL_BUILD_FAILED" | "SCHEMA_DISCOVERY_FAILED"
  | "OPENAPI_SPEC_LOAD_FAILED" | "OPENAPI_OPERATION_NOT_FOUND" | "OPENAPI_TOOL_EXECUTION_FAILED"
  | "ACCOUNT_INVALID" | "ACCOUNT_NOT_FOUND" | "ACCOUNT_DUPLICATE_LABEL" | "ACCOUNTS_FILE_INVALID";

// =============================================================================
// Server
// =============================================================================

type SmithersDb = import("@smithers-orchestrator/db/adapter").SmithersDb;

type ServerOptions = {
  port?: number;
  db?: unknown;
  authToken?: string;
  maxBodyBytes?: number;
  rootDir?: string;
  allowNetwork?: boolean;
  headersTimeout?: number;
  requestTimeout?: number;
};

type ServeOptions = {
  workflow: SmithersWorkflow<unknown>;
  adapter: SmithersDb;
  runId: string;
  abort: AbortController;
  authToken?: string;
  metrics?: boolean;
};

type GatewayTokenGrant = {
  role: string;
  scopes: string[];
  userId?: string;
  tokenId?: string;
  issuedAtMs?: number;
  expiresAtMs?: number;
  revokedAtMs?: number;
};
type GatewayAuthConfig =
  | {
      mode: "token";
      tokens: Record<string, GatewayTokenGrant>;
      allowedOrigins?: string[];     // default [] (no Origin allowlist)
    }
  | {
      mode: "jwt";
      issuer: string;
      audience: string | string[];
      secret: string;
      scopesClaim?: string;          // default "scope"
      roleClaim?: string;            // default "role"
      userClaim?: string;            // default "sub"
      defaultRole?: string;          // default "operator"
      defaultScopes?: string[];      // default [] when scope claim is absent
      clockSkewSeconds?: number;     // default 60; negative values clamp to 0
      allowedOrigins?: string[];     // default [] (no Origin allowlist)
    }
  | {
      mode: "trusted-proxy";
      trustedHeaders?: string[];     // default ["x-user-id","x-user-scopes","x-user-role"]
      allowedOrigins?: string[];     // default [] (no Origin allowlist)
      defaultRole?: string;          // default "operator"
      defaultScopes?: string[];      // default ["*"] when scopes header is absent
    };
type GatewayDefaults = { cliAgentTools?: "all" | "explicit-only" };

type GatewayOperatorUiConfig = {
  path?: string;                    // default "/console"
  title?: string;
  props?: Record<string, unknown>;
};

type GatewayUiConfig =
  | true
  | {
      entry: string;
      path?: string;                // gateway default "/"; workflow default "/workflows/<workflowKey>"
      title?: string;
      props?: Record<string, unknown>;
    };

type GatewayWebhookSignalConfig = {
  name: string;
  correlationIdPath?: string;
  runIdPath?: string;
  payloadPath?: string;
};

type GatewayWebhookRunConfig = {
  enabled?: boolean;
  inputPath?: string;
};

type GatewayWebhookConfig = {
  secret: string;
  signatureHeader?: string;
  signaturePrefix?: string;
  signal?: GatewayWebhookSignalConfig;
  run?: GatewayWebhookRunConfig;
};

type GatewayRegisterOptions = {
  schedule?: string;
  webhook?: GatewayWebhookConfig;
  ui?: GatewayUiConfig;
};

type GatewayOptions = {
  protocol?: number;
  features?: string[];
  heartbeatMs?: number;
  auth?: GatewayAuthConfig;
  ui?: GatewayUiConfig;
  operatorUi?: GatewayOperatorUiConfig | false;
  defaults?: GatewayDefaults;
  maxBodyBytes?: number;
  maxPayload?: number;
  maxConnections?: number;
  eventWindowSize?: number;
  headersTimeout?: number;
  requestTimeout?: number;
};

// =============================================================================
// Scorers (smithers-orchestrator/scorers)
// =============================================================================

type ScoreResult  = { score: number; reason?: string; meta?: Record<string, unknown> };
type ScorerInput  = { input: unknown; output: unknown; groundTruth?: unknown; context?: unknown; latencyMs?: number; outputSchema?: import("zod").ZodObject<any> };
type ScorerFn     = (input: ScorerInput) => Promise<ScoreResult>;
type Scorer       = { id: string; name: string; description: string; score: ScorerFn };
type SamplingConfig =
  | { type: "all" }
  | { type: "ratio"; rate: number }
  | { type: "none" };
type ScorerBinding = { scorer: Scorer; sampling?: SamplingConfig };
type ScorersMap    = Record<string, ScorerBinding>;

type ScoreRow = {
  id: string;
  runId: string;
  nodeId: string;
  iteration: number;
  attempt: number;
  scorerId: string;
  scorerName: string;
  source: "live" | "batch";
  score: number;
  reason: string | null;
  metaJson: string | null;
  inputJson: string | null;
  outputJson: string | null;
  latencyMs: number | null;
  scoredAtMs: number;
  durationMs: number | null;
};

type AggregateScore = {
  scorerId: string;
  scorerName: string;
  count: number;
  mean: number;
  min: number;
  max: number;
  p50: number;
  stddev: number;
};

type AggregateOptions = {
  runId?: string;
  nodeId?: string;
  scorerId?: string;
};

type ScorerContext = {
  runId: string;
  nodeId: string;
  iteration: number;
  attempt: number;
  input: unknown;
  output: unknown;
  latencyMs?: number;
  outputSchema?: import("zod").ZodObject<any>;
};

type LlmJudgeConfig = {
  id: string;
  name: string;
  description: string;
  judge: AgentLike;
  instructions: string;
  promptTemplate: (input: ScorerInput) => string;
};
type CreateScorerConfig = {
  id: string;
  name: string;
  description: string;
  score: ScorerFn;
};

// =============================================================================
// Memory (smithers-orchestrator/memory)
// =============================================================================

type MemoryFact    = { namespace: string; key: string; valueJson: string; schemaSig?: string | null; createdAtMs: number; updatedAtMs: number; ttlMs?: number | null };
type MemoryMessage = { id: string; threadId: string; role: string; contentJson: string; runId?: string | null; nodeId?: string | null; createdAtMs: number };
type MemoryThread  = { threadId: string; namespace: string; title?: string | null; metadataJson?: string | null; createdAtMs: number; updatedAtMs: number };

type WorkingMemoryConfig<
  T extends import("zod").ZodObject<import("zod").ZodRawShape> = import("zod").ZodObject<import("zod").ZodRawShape>,
> = {
  schema?: T;
  namespace: MemoryNamespace;
  ttlMs?: number;
};

type SemanticRecallConfig = {
  topK?: number;
  namespace?: MemoryNamespace;
  similarityThreshold?: number;
};

type MessageHistoryConfig = {
  lastMessages?: number;
  threadId?: string;
};

type MemoryStore = {
  getFact(ns: MemoryNamespace, key: string): Promise<MemoryFact | undefined>;
  setFact(ns: MemoryNamespace, key: string, value: unknown, ttlMs?: number): Promise<void>;
  deleteFact(ns: MemoryNamespace, key: string): Promise<void>;
  listFacts(ns: MemoryNamespace): Promise<MemoryFact[]>;
  createThread(ns: MemoryNamespace, title?: string): Promise<MemoryThread>;
  getThread(threadId: string): Promise<MemoryThread | undefined>;
  deleteThread(threadId: string): Promise<void>;
  saveMessage(msg: Omit<MemoryMessage, "createdAtMs"> & { createdAtMs?: number }): Promise<void>;
  listMessages(threadId: string, limit?: number): Promise<MemoryMessage[]>;
  countMessages(threadId: string): Promise<number>;
  deleteExpiredFacts(): Promise<number>;
  getFactEffect(ns: MemoryNamespace, key: string): Effect.Effect<MemoryFact | undefined, SmithersError>;
  setFactEffect(ns: MemoryNamespace, key: string, value: unknown, ttlMs?: number): Effect.Effect<void, SmithersError>;
  deleteFactEffect(ns: MemoryNamespace, key: string): Effect.Effect<void, SmithersError>;
  listFactsEffect(ns: MemoryNamespace): Effect.Effect<MemoryFact[], SmithersError>;
  createThreadEffect(ns: MemoryNamespace, title?: string): Effect.Effect<MemoryThread, SmithersError>;
  getThreadEffect(threadId: string): Effect.Effect<MemoryThread | undefined, SmithersError>;
  deleteThreadEffect(threadId: string): Effect.Effect<void, SmithersError>;
  saveMessageEffect(msg: Omit<MemoryMessage, "createdAtMs"> & { createdAtMs?: number }): Effect.Effect<void, SmithersError>;
  listMessagesEffect(threadId: string, limit?: number): Effect.Effect<MemoryMessage[], SmithersError>;
  countMessagesEffect(threadId: string): Effect.Effect<number, SmithersError>;
  deleteExpiredFactsEffect(): Effect.Effect<number, SmithersError>;
};

type MemoryServiceApi = {
  readonly getFact: (ns: MemoryNamespace, key: string) => Effect.Effect<MemoryFact | undefined, SmithersError>;
  readonly setFact: (ns: MemoryNamespace, key: string, value: unknown, ttlMs?: number) => Effect.Effect<void, SmithersError>;
  readonly deleteFact: (ns: MemoryNamespace, key: string) => Effect.Effect<void, SmithersError>;
  readonly listFacts: (ns: MemoryNamespace) => Effect.Effect<MemoryFact[], SmithersError>;
  readonly createThread: (ns: MemoryNamespace, title?: string) => Effect.Effect<MemoryThread, SmithersError>;
  readonly getThread: (threadId: string) => Effect.Effect<MemoryThread | undefined, SmithersError>;
  readonly deleteThread: (threadId: string) => Effect.Effect<void, SmithersError>;
  readonly saveMessage: (msg: Omit<MemoryMessage, "createdAtMs"> & { createdAtMs?: number }) => Effect.Effect<void, SmithersError>;
  readonly listMessages: (threadId: string, limit?: number) => Effect.Effect<MemoryMessage[], SmithersError>;
  readonly countMessages: (threadId: string) => Effect.Effect<number, SmithersError>;
  readonly deleteExpiredFacts: () => Effect.Effect<number, SmithersError>;
  readonly store: MemoryStore;
};

type MemoryProcessorConfig = {
  processors?: string[];
};

type MemoryProcessor = {
  name: string;
  process: (store: MemoryStore) => Promise<void>;
  processEffect: (store: MemoryStore) => Effect.Effect<void, SmithersError>;
};

type MemoryLayerConfig = {
  db: import("drizzle-orm/bun-sqlite").BunSQLiteDatabase<Record<string, unknown>>;
};

// =============================================================================
// OpenAPI tools (smithers-orchestrator/openapi)
// =============================================================================

type OpenApiAuth =
  | { type: "apiKey"; name: string; in: "header" | "query"; value: string }
  | { type: "bearer"; token: string }
  | { type: "basic"; username: string; password: string };

type OpenApiRefObject = {
  $ref: string;
};

type OpenApiSchemaObject = {
  type?: string;
  format?: string;
  description?: string;
  properties?: Record<string, OpenApiSchemaObject | OpenApiRefObject>;
  required?: string[];
  items?: OpenApiSchemaObject | OpenApiRefObject;
  enum?: unknown[];
  default?: unknown;
  nullable?: boolean;
  oneOf?: Array<OpenApiSchemaObject | OpenApiRefObject>;
  anyOf?: Array<OpenApiSchemaObject | OpenApiRefObject>;
  allOf?: Array<OpenApiSchemaObject | OpenApiRefObject>;
  additionalProperties?: boolean | OpenApiSchemaObject | OpenApiRefObject;
  minimum?: number;
  maximum?: number;
  minLength?: number;
  maxLength?: number;
  pattern?: string;
  $ref?: string;
};

type OpenApiParameterObject = {
  name: string;
  in: "query" | "header" | "path" | "cookie";
  description?: string;
  required?: boolean;
  schema?: OpenApiSchemaObject | OpenApiRefObject;
  deprecated?: boolean;
};

type OpenApiRequestBodyObject = {
  description?: string;
  required?: boolean;
  content: Record<string, { schema?: OpenApiSchemaObject | OpenApiRefObject }>;
};

type OpenApiOperationObject = {
  operationId?: string;
  summary?: string;
  description?: string;
  parameters?: Array<OpenApiParameterObject | OpenApiRefObject>;
  requestBody?: OpenApiRequestBodyObject | OpenApiRefObject;
  responses?: Record<string, unknown>;
  tags?: string[];
  deprecated?: boolean;
};

type OpenApiPathItem = {
  get?: OpenApiOperationObject;
  post?: OpenApiOperationObject;
  put?: OpenApiOperationObject;
  delete?: OpenApiOperationObject;
  patch?: OpenApiOperationObject;
  parameters?: Array<OpenApiParameterObject | OpenApiRefObject>;
};

type OpenApiSpec = {
  openapi: string;
  info: {
    title: string;
    version: string;
    description?: string;
  };
  servers?: Array<{
    url: string;
    description?: string;
  }>;
  paths: Record<string, OpenApiPathItem>;
  components?: {
    schemas?: Record<string, OpenApiSchemaObject>;
    parameters?: Record<string, OpenApiParameterObject>;
    requestBodies?: Record<string, OpenApiRequestBodyObject>;
  };
};

type OpenApiToolsOptions = {
  baseUrl?: string;
  headers?: Record<string, string>;
  auth?: OpenApiAuth;
  include?: string[];
  exclude?: string[];
  namePrefix?: string;
  operations?: Record<
    string,
    | false
    | {
        include?: boolean;
        name?: string;
        description?: string;
        responseExamples?: Array<{
          status?: string | number;
          description?: string;
          value: unknown;
        }>;
      }
  >;
};

// =============================================================================
// CreateSmithers
// =============================================================================

type CreateSmithersOptions = {
  readableName?: string;
  description?: string;
  alertPolicy?: SmithersAlertPolicy;
  dbPath?: string;
  journalMode?: string;
};

type CreateSmithersPostgresOptions =
  CreateSmithersOptions & (
    | { provider?: "postgres"; connectionString?: string; connection?: object }
    | { provider: "pglite"; dataDir?: string }
  );

// Named export from "smithers-orchestrator". The returned `smithers` property is
// the workflow wrapper; there is no default-exported top-level smithers function.
declare function createSmithers<Schemas extends Record<string, import("zod").ZodObject<any>>>(
  schemas: Schemas,
  opts?: CreateSmithersOptions,
): CreateSmithersApi<Schemas>;

declare function createSmithersPostgres<Schemas extends Record<string, import("zod").ZodObject<any>>>(
  schemas: Schemas,
  opts?: CreateSmithersPostgresOptions,
): Promise<CreateSmithersApi<Schemas> & { close: () => Promise<void> }>;

type SchemaOutput<Schema> = Extract<
  Schema[keyof Schema],
  import("zod").ZodObject<import("zod").ZodRawShape>
>;
type RuntimeSchema<Schema> =
  Schema extends { input: infer Input }
    ? Omit<Schema, "input"> & { input: Input extends import("zod").ZodTypeAny ? import("zod").infer<Input> : Input }
    : Schema;

type CreateSmithersApi<Schema = unknown> = {
  Workflow: (props: WorkflowProps) => React.ReactElement;
  Approval: <Row>(props: ApprovalProps<Row, SchemaOutput<Schema>>) => React.ReactElement;
  Task: <Row, D extends DepsSpec = {}>(props: TaskProps<Row, SchemaOutput<Schema>, D>) => React.ReactElement;
  Sequence: typeof Sequence;
  Parallel: typeof Parallel;
  MergeQueue: typeof MergeQueue;
  Branch: typeof Branch;
  Loop: typeof Loop;
  Ralph: typeof Ralph;
  ContinueAsNew: typeof ContinueAsNew;
  continueAsNew: typeof continueAsNew;
  Worktree: typeof Worktree;
  Sandbox: (props: SandboxProps) => React.ReactElement;
  Signal: <SignalSchema extends import("zod").ZodObject<import("zod").ZodRawShape>>(props: SignalProps<SignalSchema>) => React.ReactElement;
  Timer: typeof Timer;
  useCtx: () => SmithersCtx<RuntimeSchema<Schema>>;
  smithers: (
    build: (ctx: SmithersCtx<RuntimeSchema<Schema>>) => React.ReactElement,
    opts?: SmithersWorkflowOptions,
  ) => SmithersWorkflow<RuntimeSchema<Schema>>;
  db: import("drizzle-orm/bun-sqlite").BunSQLiteDatabase<Record<string, unknown>>;
  tables: { [K in keyof Schema]: unknown };
  outputs: { [K in keyof Schema]: Schema[K] };
};

type SerializedCtx = {
  runId: string;
  iteration: number;
  iterations: Record<string, number>;
  input: unknown;
  outputs: OutputSnapshot;
};

type HostNodeJson =
  | {
      kind: "element";
      tag: string;
      props: Record<string, string>;
      rawProps: Record<string, any>;
      children: HostNodeJson[];
    }
  | {
      kind: "text";
      text: string;
    };

type ExternalSmithersConfig<S extends Record<string, import("zod").ZodObject<any>>> = {
  schemas: S;
  agents: Record<string, AgentLike>;
  buildFn: (ctx: SerializedCtx) => HostNodeJson;
  dbPath?: string;
};

declare function createExternalSmithers<S extends Record<string, import("zod").ZodObject<any>>>(
  config: ExternalSmithersConfig<S>,
): SmithersWorkflow<S> & { tables: Record<string, any>; cleanup: () => void };

// =============================================================================
// Observability (smithers-orchestrator/observability)
// =============================================================================

type SmithersLogFormat = "json" | "pretty";
type SmithersObservabilityService = { emit(event: SmithersEvent): void | Promise<void> };
type SmithersObservabilityOptions = { service?: SmithersObservabilityService; logFormat?: SmithersLogFormat };
type ResolvedSmithersObservabilityOptions = SmithersObservabilityOptions & { metricsPort?: number; metricsPath?: string };
```

For canonical, machine-checked types, install `smithers-orchestrator` and use editor go-to-definition. For runtime errors, see [Errors](/reference/errors).
