// =============================================================================
// 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 };