TypeScript SDK & API Reference
Complete TypeScript API reference for @invariant/sdk primitives, workflow nodes, control-flow operators, agent decision layer, session management, and runtime contracts.
1. Application Setup (invariant())
The invariant() function creates the root application container configured with storage adapters, model registries, and session schemas.
import { invariant } from "@invariant/sdk";
import { z } from "zod";
export const app = invariant({
models: {
default: openAI({ model: "gpt-4o" }),
fast: openAI({ model: "gpt-4o-mini" }),
},
session: {
contextSchema: z.object({
customerId: z.string(),
accountTier: z.enum(["FREE", "PRO", "ENTERPRISE"]),
}),
hydrate: async ({ userId, sessionId }) => {
const customer = await crm.getCustomer(userId);
return { customerId: customer.id, accountTier: customer.tier };
},
},
});Application Lifecycle & Configuration (InvariantAppConfig)
// Start runtime workers & recovery scanner:
await app.start();
// Graceful shutdown of workers and locks:
await app.stop();| Property / Method | Type | Description |
|---|---|---|
models | Record<string, ModelAdapter> | Model registry mapping keys (default, fast) to provider adapters. |
session | SessionConfig<TSessionContext> | Configuration for contextSchema and hydrate callback. |
store | StorageAdapter | Persistence adapter (PostgreSQL, SQLite, Redis, or Memory). |
app.start() | () => Promise<void> | Starts outbox background workers and crash recovery scanner. |
app.stop() | () => Promise<void> | Stops background workers and releases active process leases gracefully. |
2. Workflows & Fragments
app.workflow()
Creates a compiled, type-safe workflow graph builder with an optional input schema.
export const refundWorkflow = app.workflow("customer-refund", {
description: "Processes customer refund requests with policy evaluation.",
inputSchema: z.object({
orderId: z.string(),
reason: z.string(),
}),
});| Option | Type | Description |
|---|---|---|
description | string | Natural language description used for Agent intent routing. |
inputSchema | ZodSchema | TypeBoxSchema | Schema validating initial workflow parameters. |
app.fragment()
Creates a reusable, composable subgraph fragment.
const issueRefundFragment = app.fragment("issue-refund-subgraph")
.capability("issue-stripe-refund", issueStripeRefund)
.step("build-receipt", buildReceipt)
.capability("send-receipt-email", sendReceiptEmail);Automatic Subgraph Namespacing: During build-time compilation,
app.fragment()automatically flattens subgraphs and namespaces inner node IDs (e.g.refund-decision.approved.issue-stripe-refund) to guarantee global node ID uniqueness across execution traces and event logs.
3. Workflow Graph Nodes
.step()
Defines a pure, synchronous, in-process computation node.
workflow.step("check-policy", {
fallback: "manual-review",
handler: ({ state }) => ({
eligible: Boolean(state.customer?.active && state.customer?.refundWindowOpen),
}),
});| Option | Type | Description |
|---|---|---|
fallback | string | Optional node ID to transition to on internal computation error (e.g. JSON parse error). |
handler | function | Pure, synchronous computation handler ({ input, state }) => Partial<TState>. |
Steps perform deterministic in-process computation without external side-effects; the runtime does not apply retry policies to steps.
.reason()
Delegates a bounded semantic decision task to an LLM inside an explicit context and output schema boundary.
workflow.reason("classify-intent", {
instruction: "Classify the support request into a category and urgency tier.",
context: ({ input }) => ({ message: input.message }),
schema: z.object({
category: z.enum(["REFUND", "DELIVERY", "ACCOUNT", "OTHER"]),
urgency: z.enum(["LOW", "MEDIUM", "HIGH"]),
}),
retry: {
maxAttempts: 3,
backoffMs: 1000,
},
fallback: "fallback-classification",
});| Option | Type | Description |
|---|---|---|
instruction | string | Explicit instruction prompt directing the LLM for this bounded task. |
context | function | Pure selector function ({ input, state }) => object isolating context passed to LLM. |
schema | ZodSchema | Output Zod schema enforced via structured outputs / JSON schema validation. |
retry | RetryPolicy | Optional retry configuration (maxAttempts, backoffMs, retryOn). |
fallback | string | Node ID to transition to on exhausted retries or model failure. |
.capability()
Defines an external side-effect boundary (API call, database write, email dispatch, queue delivery).
workflow.capability("issue-stripe-refund", {
idempotencyKey: ({ execution }) => `refund:${execution.id}`,
retry: {
maxAttempts: 5,
backoffMs: 2000,
},
fallback: "notify-ops-failure",
handler: async ({ state, execution }) => {
return await stripe.refunds.create(
{ charge: state.chargeId, amount: state.refundAmount },
{ idempotencyKey: `refund:${execution.id}` }
);
},
});| Option | Type | Description |
|---|---|---|
idempotencyKey | function | Generates a stable key ({ execution, state }) => string across retries. |
retry | RetryPolicy | Retry configuration (maxAttempts, backoffMs, retryOn). |
fallback | string | Node ID to transition to on exhausted retries or capability failure. |
handler | function | Async capability execution handler async ({ state, input, execution }) => object. |
.wait()
Suspends workflow execution durably until a matching external event or user input arrives.
workflow.wait("await-approval", {
schema: z.object({
approved: z.boolean(),
approverId: z.string(),
}),
options: ({ state }) => [
{ label: "Approve Refund", value: true },
{ label: "Reject Refund", value: false },
],
timeout: "24h",
});| Option | Type | Description |
|---|---|---|
schema | ZodSchema | Schema validating external event payload or user input. |
options | function | Optional dynamic choices builder ({ state }) => Array<{label, value}>. |
timeout | string | Duration string (e.g. "24h", "30m") after which TIMER_FIRED triggers timeout. |
4. Control-Flow & Composition Operators
.branch()
Deterministically routes execution between subgraph branches based on a selector function:
workflow.branch("route-decision", ({ state }) => (state.eligible ? "APPROVED" : "REVIEW"), {
APPROVED: autoRefundFragment,
REVIEW: manualReviewFragment,
});.repeat()
Iterates a subgraph fragment durably for bounded loops, UI automation, or polling routines:
workflow.repeat("automation-loop", {
maxIterations: 50,
do: app.fragment("loop-step")
.capability("check-status", checkStatus)
.branch("done-check", ({ state }) => (state.complete ? "EXIT" : "CONTINUE"), {
EXIT: app.fragment("exit").break(),
CONTINUE: app.fragment("continue"),
}),
});.child()
Spawns a separate child workflow execution with its own runId and event history:
workflow.child("fraud-check-child", fraudReviewWorkflow, {
input: ({ state }) => ({ userId: state.customerId, amount: state.refundAmount }),
});5. Direct Workflow Executions (Workflow.start() & Execution)
Workflows can be started directly by application code without an Agent or conversation:
// Start workflow execution directly (returns an Execution handle):
const execution = await refundWorkflow.start({
input: { orderId: "ord_99", reason: "defective" },
session, // optional session scope
});
console.log(execution.id); // "run_refund_171829200"
// Query current execution status:
const status = await execution.status(); // "RUNNING" | "SUSPENDED" | "COMPLETED" | "FAILED"
// Wait for final state result (or catch ExecutionError on failure):
try {
const finalState = await execution.result();
console.log(finalState.refundId);
} catch (error) {
if (error instanceof ExecutionError) {
console.error(`Execution ${error.runId} failed at node ${error.failedNodeId}: ${error.message}`);
}
}
// Pause / Resume / Cancel execution:
await execution.pause();
await execution.resume();
await execution.cancel("Cancelled by administrator");6. Session Management & Context API (app.sessions & Session)
A Session is the durable continuity boundary for identity, hydrated context, channels, and related executions.
// Retrieve or initialize a durable session:
const session = await app.sessions.getOrCreate("sess_101", "usr_441", {
preferredLanguage: "en", // optional initialContext fallback/defaults
});
// Read hydrated session context:
console.log(session.context.customerId);
// Update context via explicit application mutation:
await session.updateContext({ preferredLanguage: "es" });
// Rehydrate context from authoritative external sources:
await session.rehydrate();
// Start a workflow scoped to this session:
await session.startWorkflow(refundWorkflow, { orderId: "ord_99" });
// Submit user input to an active .wait() boundary:
await session.submitInput({ approved: true });Session Creation & Hydration Semantics
- New Session (
getOrCreate): If no session exists withsessionId, Invariant creates it, passesinitialContextif provided as seed defaults, and invokeshydrate({ userId, sessionId })to construct initial context. - Existing Session (
getOrCreate): If a session withsessionIdexists in durable storage, Invariant loads its stored context without callinghydrate(). session.rehydrate(): Explicitly invokeshydrate({ userId, sessionId })against external sources to refresh context.
7. Agents (app.agent() & Agent.run())
Agents interpret user intent and propose constrained Runtime Actions over registered workflows.
export const supportAgent = app.agent("customer-support", {
instructions: `
Help customers resolve order and refund inquiries.
Prefer driving registered workflows over unconstrained answers.
Never claim a refund succeeded unless the Runtime reports completion.
`,
workflows: [refundWorkflow, cancelSubscriptionWorkflow],
context: ({ session }) => ({
customerId: session.context.customerId,
tier: session.context.accountTier,
}),
model: "fast",
});AppAgentOptions Specification
| Option | Type | Description |
|---|---|---|
instructions | string | Developer-defined behavioral/system instructions for LLM orchestration. |
workflows | Array<Workflow> | Array of compiled workflows the Agent is permitted to drive. |
context | function | Selector function ({ session }) => object projecting business context to model. |
model | string | Model registry key ("default", "fast"). |
Agent Turn Execution (agent.run())
const result = await supportAgent.run({
session,
message: "I want a refund for order ord_99",
});
console.log(result.output); // Model conversational response
console.log(result.action); // Proposed Runtime Action (if any)
console.log(result.execution);// Started or active Execution reference8. Agent Runtime Contracts
Invariant enforces a strict boundary between model reasoning and runtime execution.
AgentContextProjection
The payload assembled by Invariant for each Agent turn:
export interface AgentContextProjection<TContext = Record<string, unknown>> {
/** Developer Context: Business information projected from Session Context */
readonly context: TContext;
/** Runtime Projection: Derived execution position, relevant workflows, and valid actions */
readonly runtime: RuntimeProjection;
}
export interface RuntimeProjection {
readonly revision: number;
readonly relevantWorkflows: Array<{ id: string; description?: string }>;
readonly validActions: Array<{ name: string; description: string }>;
readonly activeExecution?: { runId: string; currentNodeId: string };
readonly expectedInput?: { schema: Record<string, unknown> };
}RuntimeAction
A structured request proposed by the model and re-validated by the Runtime against fresh durable state before execution:
export type RuntimeAction =
| {
type: "start_workflow";
workflowId: string;
input: Record<string, unknown>;
revision: number;
}
| {
type: "submit_input";
input: Record<string, unknown>;
revision: number;
}
| {
type: "cancel_workflow";
reason?: string;
revision: number;
};Validation Gate (
STALE_REVISION): ARuntimeActionis a proposal, not autonomous execution authority. If the durable runtime state revision has changed between projection assembly and action execution (action.revision !== currentRevision), the action is rejected withSTALE_REVISION, fresh projection is reassembled, and the turn is safely re-evaluated.
9. Type System & State Merging
Invariant provides end-to-end static type inference across workflow steps and session contexts.
type Merge<A, B> = Omit<A, keyof B> & B;When nodes return data, the compile-time type signature extends automatically:
const workflow = app.workflow("typed-demo", {
inputSchema: z.object({ orderId: z.string() }),
})
// Node 1: state is { orderId: string }
.capability("load-order", async ({ input }) => ({
order: { id: input.orderId, amount: 150.00 },
}))
// Node 2: state is { orderId: string, order: { id: string, amount: number } }
.step("calculate-discount", ({ state }) => ({
discount: state.order.amount > 100 ? 20.00 : 0.00,
}))
// Node 3: state contains orderId, order, and discount!
.step("final-total", ({ state }) => ({
finalTotal: state.order.amount - state.discount,
}));Compile-Time vs. Runtime Parity: The compile-time state merge semantics (
Merge<A, B> = Omit<A, keyof B> & B) match the runtime state reducer semantics exactly.
10. Extensibility & Adapter Contracts
Invariant is designed to be fully extensible via pluggable storage and model adapters.
StorageAdapter Interface
export interface StorageAdapter {
commitTransition(commit: TransitionCommit): Promise<void>;
loadExecution(runId: string): Promise<DurableExecution | null>;
loadSession(sessionId: string): Promise<DurableSession | null>;
saveSession(session: DurableSession): Promise<void>;
acquireLease(runId: string, workerId: string, ttlMs: number): Promise<Lease | null>;
renewLease(runId: string, leaseId: string, ttlMs: number): Promise<boolean>;
releaseLease(runId: string, leaseId: string): Promise<void>;
findRunnableExecutions(input: { limit: number }): Promise<string[]>;
}ModelAdapter Interface
export interface ModelAdapter {
complete(params: {
system: string;
prompt: string;
schema?: Record<string, unknown>;
}): Promise<{ content: string; structuredOutput?: unknown }>;
}Ecosystem Package Mapping
@invariant/sdk— Core DSL, Session management, Agent definition, Execution handles.@invariant/postgres— Transactional Outbox & PostgreSQL event store adapter.@invariant/openai/@invariant/anthropic/@invariant/google— LLM provider adapters.@invariant/live-gemini— Gemini Live WebSocket real-time transport adapter.