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 projectAGENTS.md. - CLI — discover, run, build, deploy, inspect, replay, and verify everything from one binary (
fabric-harnessorfh). - 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:
| Term | Meaning |
|---|---|
| Agent | A configured autonomous runtime. |
| Session | A persisted message/context thread. |
| Skill | A reusable Markdown- or code-backed procedure. |
| Role | A scoped instruction/model profile. |
| Sandbox | An isolated execution environment with filesystem/shell/tools. |
| Task | A child or delegated agent run. |
| Tools | Model-callable functions. |
| Commands | Shell-level capabilities exposed to the sandbox. |
| Build | A 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.
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) };
},
});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
- Want the 10-line agent? Headless agents with the minimal entrypoint.
- Confused by entrypoints, stateless, inline, or Temporal? SDK entrypoints, runtimes, and targets.
- New here? Use cases → Installation → Your first agent.
- Want CLI specifics? CLI reference.
- Ready to deploy? Deployment.
- Curious about scope? See the capability matrix.