FabricFabricHarness
Reference

Runtime modes

inline (default), stateless (headless), and temporal (durable). When to use each.

init({ runtime }) accepts three values. Runtime is independent from the SDK entrypoint: Lite and Full agents can both use the shared session primitives, while runtime controls persistence and durability.

ModeDefault?PersistenceUse when
inlineyesIn-memory by default; pluggable SessionStore (SQLite/Postgres/file) for durabilityYou want conversation continuity, artifact persistence, approval gating, and CLI tooling like fh sessions/fh inspect.
statelessno — explicit opt-inNone. Each invocation is independent.High-volume webhooks, edge runtimes, or prototypes where state would just be discarded.
temporalno — explicit opt-inDurable workflow state via Temporal.Long-running, restartable, multi-day agents that must survive crashes or human approval delays. Requires @fabric-harness/temporal.

inline (default)

import { init } from '@fabric-harness/sdk';

const agent = await init({
  model: 'anthropic/claude-sonnet-4-6',
  // runtime: 'inline' — implicit
});

In-memory InMemorySessionStore is used unless you pass store. History, artifacts, and approval state live for the lifetime of the process. Pass a real store when you need persistence:

import { FileSessionStore } from '@fabric-harness/node';

const agent = await init({
  model: 'anthropic/claude-sonnet-4-6',
  store: new FileSessionStore({ rootDir: '.fabricharness/sessions' }),
});

Production safety

In production (FABRIC_ENV=production or NODE_ENV=production), inline without an explicit SessionStore emits a warning. The runtime still runs — you just lose state on restart. To silence the warning, either:

  • Configure a SessionStore (recommended).
  • Set FABRIC_ALLOW_EPHEMERAL_STATE=1 to acknowledge in-memory state explicitly.
  • Use runtime: 'stateless' to opt into ephemeral semantics on purpose.

stateless (headless)

import { init } from '@fabric-harness/sdk';

const agent = await init({
  model: 'anthropic/claude-sonnet-4-6',
  runtime: 'stateless',
});

The session uses NoopSessionStore — every write is discarded, every read returns the empty initial state. Use this for:

  • Webhook handlers where the response is the only output.
  • Cloudflare Workers / Vercel Edge where there's no durable storage attached.
  • Prototypes where you don't yet care about conversation continuity.

What stateless disables

  • Session entry / event persistence.
  • Artifact persistence (session.artifact(...) falls back to a fingerprint-only ArtifactRef with no stored bytes).
  • Approval waiting via the store. Approvals only work in stateless mode if you pass an onApproval callback that returns a synchronous decision.
  • fh sessions, fh inspect, fh logs will not show stateless runs.

If any of those matter, stay on inline and configure a store.

temporal (durable)

import { init } from '@fabric-harness/sdk';

const agent = await init({
  model: 'anthropic/claude-sonnet-4-6',
  runtime: 'temporal',
});

Sessions execute as Temporal workflows. The agent code is unchanged — the runtime adapter handles checkpointing, retries, and resume. See Temporal worker deployment for setup.

Temporal mode requires @fabric-harness/temporal, a Temporal cluster (or the bundled mock for tests), and a worker process running fh temporal-worker.

Switching modes

Modes are runtime-only. They are separate from Lite vs Full SDK imports and separate from deploy targets. Nothing in your agent code changes when you swap:

const dev      = await init({ runtime: 'stateless' });             // dev / preview
const prod     = await init({ runtime: 'inline', store: pgStore }); // prod web
const longJob  = await init({ runtime: 'temporal' });              // batch / nightly

Configure runtime at deploy time via FABRIC_RUNTIME env or your .fabricharness/config.ts. The agent file stays the same.

See also