FabricFabricHarness

What is Fabric Harness

A TypeScript framework for building durable, deployable autonomous agents.

Fabric Harness is a headless TypeScript framework for building durable, deployable autonomous agents. You write agents in .fabricharness/agents/, run them locally for development, and deploy the same code to Node, Docker, a Temporal worker, Cloudflare Workers + Sandbox, or Foundry Hosted Agents on Azure.

Headless by design. Fabric Harness ships no TUI, no GUI, no chat widget. It's an SDK and a CLI. Agents are invoked via fh run, HTTP webhooks, schedules, or Temporal — and emit a typed event stream that any consumer (logs, downstream workers, your own UI if you build one) can subscribe to.

Why a framework, not just an SDK

Most agent libraries leave you to wire up the runtime, the build, the dev server, the deployment story, and the durability story yourself. Fabric Harness is opinionated about those things so the agent code stays focused on the work:

  • Workspace conventions.fabricharness/agents, roles/, skills/, policies/, sandboxes/, plus a project AGENTS.md.
  • CLI — discover, run, build, deploy, inspect, replay, and verify everything from one binary (fabric-harness or fh).
  • Runtime adapters — local Node, Docker sandbox, Temporal worker, Cloudflare Workers, Foundry-hosted, more on the way.
  • Headless by default — agents complete autonomously. Approvals are an explicit hook, not a default user prompt.
  • Durable by design — long-running sessions can survive crashes, restarts, retries, and human approval delays via Temporal.
  • Capability-scoped security — commands and tools are explicit, secrets stay out of model context.

Standard agent terminology

Fabric Harness keeps the terms that are converging across the agent ecosystem. You will find these everywhere in the docs:

TermMeaning
AgentA configured autonomous runtime.
SessionA persisted message/context thread.
SkillA reusable Markdown- or code-backed procedure.
RoleA scoped instruction/model profile.
SandboxAn isolated execution environment with filesystem/shell/tools.
TaskA child or delegated agent run.
ToolsModel-callable functions.
CommandsShell-level capabilities exposed to the sandbox.
BuildA compiled, deployable workspace artifact.

One SDK, one import, two flavours

Most users only ever need one import:

import { agent, schema } from '@fabric-harness/sdk';

The bare @fabric-harness/sdk import gives you agent({...}) with headless defaults pre-injected on every init() call (runtime: 'stateless', sandbox: 'virtual', loopRuntime: pi-agent-core, compaction: { enabled: true }). Override any of them per call. Add typed input/output schemas, policy, artifacts, custom stores, telemetry — they're all options on the same agent({...}) and init() calls.

For Temporal-backed durable agents or compliance/audit workloads where you don't want any implicit defaults, switch to @fabric-harness/sdk/strict — same call shape, defaults are not injected. Every option must be declared explicitly.

Runtime (stateless / inline / temporal) and target (node / temporal-worker / docker / cloudflare / foundry-hosted-agent) are separate independent choices configured at init() and fh build.

Default — bare import, headless defaults
import { agent } from '@fabric-harness/sdk';

export default agent<{ message: string }>({
  name: 'echo',
  model: 'openai/gpt-5.5',
  triggers: { webhook: true },
  run: async ({ init, input }) => {
    const session = await (await init()).session();
    return { reply: await session.prompt<string>(input.message) };
  },
});
Strict — every option declared, Temporal-safe
import { agent, schema } from '@fabric-harness/sdk/strict';

export default agent({
  name: 'triage',
  model: 'openai/gpt-5.5',
  input: schema.object({ issueNumber: schema.number(), title: schema.string() }),
  output: schema.object({ severity: schema.enum(['low','medium','high']), summary: schema.string() }),
  triggers: { webhook: true, schedule: '*/15 * * * *' },
  run: async ({ init, input }) => {
    const fabric = await init({
      runtime: 'temporal',
      sandbox: 'local',
      compaction: { enabled: false },
      policy: triagePolicy,
    });
    const session = await fabric.session();
    return await session.prompt(`Triage issue #${input.issueNumber}: ${input.title}`);
  },
});

Both examples deploy through the same CLI and use the same init() / session.prompt() / session.skill() / session.task() / session.shell() surface. Start with the default import; switch to /strict when you need replay-determinism (Temporal) or compliance/audit clarity. You do not rewrite the agent. See SDK entrypoints, runtimes, and targets for the full distinction.

Public API surface

const fabricAgent = await init(options);
const session = await fabricAgent.session(id?, options?);

await session.prompt(text, options?);
await session.skill(name, options?);
await session.task(text, options?);
await session.shell(command, options?);

Where to go next