FabricFabricHarness
Reference

Migrating to Fabric Harness 6

Upgrade custom persistence adapters, clients, and persistent agents to the version 6 durable runtime contract.

Fabric Harness 6 is a coordinated major for @fabric-harness/sdk, @fabric-harness/node, @fabric-harness/client, and @fabric-harness/cli. Upgrade those packages together. Adapter packages have independent versions; use the versions released from the same certified commit.

Conversation stream stores

Custom ConversationStreamStore implementations must persist and return:

  • formatVersion: 1 in ConversationStreamMeta;
  • a stable incarnation for one stream generation;
  • the same incarnation in every producer claim and append fence;
  • optional fold checkpoints fenced by incarnation and format version.

Generate a new incarnation only when a deleted stream is recreated. Reject appends from an older incarnation. Run defineConversationStreamStoreContractTests from @fabric-harness/sdk/testing/contracts against the adapter.

The HTTP and client protocols now emit stream_checkpoint updates. When reset is true, discard the previous projection and resume from the supplied offset; the React store does this automatically.

Submission stores

Custom AgentSubmissionStore implementations must implement the turn-boundary joining methods:

  • claimJoinableSubmissions();
  • finalizeJoinedSubmission();
  • revertJoiningSubmission();
  • listJoinedSubmissions();
  • settleJoinedSubmission().

Joining transitions are ownership-fenced and atomic. Use defineSubmissionStoreContractTests to prove FIFO order, adoption, recovery, and settlement.

Client deadlines

Client HTTP requests now default to a 90-second deadline. For a deliberately longer synchronous compatibility call, set an explicit budget:

const fabric = createFabricClient({
  baseUrl: process.env.FABRIC_URL!,
  requestTimeoutMs: 10 * 60_000,
});

Prefer asynchronous admission followed by wait(), read(), or updates() for durable work. Set requestTimeoutMs: 0 only when an external transport already enforces a bounded deadline.

Persistent-agent changes

Declare submission durability statically in createAgent()'s second argument. The host reads this policy before render, so it still applies when rendering or worker recovery fails:

export default createAgent(
  () => {
    useModel('anthropic/claude-sonnet-4-6');
    return 'Handle the durable request.';
  },
  { durability: { maxAttempts: 5, timeoutMs: 2 * 60 * 60_000 } },
);

A string uid can no longer be combined with initialData. Use uid: null for create-only admission, or send the existing uid without creation data.

useAgentStart() callbacks run concurrently. Work requiring ordering belongs in one callback. Their state writes and appended signals commit atomically after every callback settles, while appended signals are flattened in callback declaration order. Concurrent writes to the same state key follow callback scheduling, so keep dependent or conflicting writes in one callback. Start and finish callbacks receive signal, harness, and log.

Dynamic Native-loop model changes now resolve the provider owning the newly selected model. Hosts with custom model routing should supply modelProviderResolver; a deliberately fixed modelProvider remains fixed unless a resolver is configured. Return { provider, model } from a custom resolver when the provider expects a normalized model id rather than the rendered provider/model reference.

Static createAgent() policy remains authoritative across hook refreshes. Custom persistent hosts must preserve the static definition when they re-render dynamic capabilities; dropping it can remove approval and capability constraints and is unsupported.