FabricFabricHarness
Reference

Filesystem sources

Mount read-only content (knowledge bases, docs, fixtures) into a sandbox so the agent can grep / glob / read it like ordinary files.

withFilesystemSources pre-populates a sandbox with read-only content from one or more sources. The agent's built-in grep, glob, and read tools see it as ordinary files — no vector store, no embeddings, no retrieval pipeline.

This is the right primitive for support agents, runbook lookup, FAQ assistants, internal-docs Q&A, or any workflow where the corpus is small enough to mount as files (low MB).

Quickstart

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

const sandbox = withFilesystemSources('empty', [{
  mountAt: '/workspace/kb',
  source: localDirectorySource('./knowledge-base', { include: (p) => p.endsWith('.md') }),
}]);

export default agent({
  run: async ({ init, input }) => {
  const agent = await init({ sandbox, runtime: 'stateless' });
  const session = await agent.session();
  const message = String((payload as any)?.message ?? '');
  return { reply: await session.prompt<string>(
    `Search /workspace/kb (markdown) and answer concisely.\n\nCustomer: ${message}`,
  )};
},
});

The agent sees /workspace/kb/*.md and uses its built-in tools to search.

Sources

localDirectorySource(hostPath, options?)

Reads a host directory recursively. Each file becomes an entry; the relative path inside hostPath is preserved.

localDirectorySource('./docs', {
  name: 'product-docs',
  include: (relativePath) => relativePath.endsWith('.md') && !relativePath.startsWith('drafts/'),
});
  • include — predicate to skip files. Defaults to "include everything."
  • name — label used in telemetry.

inMemorySource(files, options?)

Bundle content directly into the agent module. Useful for fixtures, tests, or small static corpora that ship with the agent.

inMemorySource({
  'faq.md': '# FAQ\n\nQ: Reset password? A: Visit /forgot-password.',
  'guides/billing.md': '# Billing\n\nCharges happen on the 1st of each month.',
});

Cloudflare R2 sources

@fabric-harness/cloudflare includes an R2 source helper for support knowledge bases and other object-backed file sets:

import { r2FilesystemSource } from '@fabric-harness/cloudflare';
import { withFilesystemSources } from '@fabric-harness/sdk';

const sandbox = withFilesystemSources('empty', [{
  mountAt: '/workspace/knowledge-base',
  source: r2FilesystemSource(env.SUPPORT_KB, { prefix: 'knowledge-base/' }),
}]);

Objects such as knowledge-base/reset-password.md are mounted as /workspace/knowledge-base/reset-password.md and become available to the built-in read, grep, and glob tools. See examples/support-agent-cloudflare-r2.

@fabric-harness/connectors ships concrete connectors for the most common backends. Each is on its own subpath export and peer-deps the vendor SDK so you only install what you use.

import { init } from '@fabric-harness/sdk';
import { s3Source } from '@fabric-harness/connectors/s3';
import { githubSource } from '@fabric-harness/connectors/github';

const fabric = await init({ sandbox: 'local' });
const session = await fabric.session();

// `session.mount()` writes the source's entries into the sandbox at the
// given path. mode: 'read' (default) blocks write tools to that prefix.
await session.mount('/mnt/logs', s3Source({ bucket: 'app-logs', prefix: '2026/' }));
await session.mount('/mnt/repo', githubSource({ owner: 'me', repo: 'demo', ref: 'main' }));

Available subpath exports:

ExportFunctionsPeer dep
@fabric-harness/connectors/s3s3Source, s3Writer@aws-sdk/client-s3
@fabric-harness/connectors/azure-blobazureBlobSource, azureBlobWriter@azure/storage-blob
@fabric-harness/connectors/gcsgcsSource, gcsWriter@google-cloud/storage
@fabric-harness/connectors/githubgithubSourceoctokit
@fabric-harness/connectors/databricks-volumedatabricksVolumeSource, databricksVolumeWriternone

S3 and Azure Blob structural sources (advanced)

For non-standard SDK clients or when you want to adapt an existing client, the older structural helpers remain available. They don't import cloud SDKs; adapt your client calls to the small interface shape.

import { s3FilesystemSource, azureBlobFilesystemSource } from '@fabric-harness/connectors';

const s3 = s3FilesystemSource(s3ClientAdapter, { prefix: 'knowledge-base/' });
const azure = azureBlobFilesystemSource(blobClientAdapter, { prefix: 'knowledge-base/' });

Use these with withFilesystemSources() the same way as local, in-memory, or R2 sources.

Custom sources

A source is just an object with an entries() method that yields { path, content } pairs. Implement one for S3, R2, GCS, an MCP filesystem server, or any other backend:

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

function s3Source(bucket: string, prefix: string): FilesystemSource {
  return {
    name: `s3://${bucket}/${prefix}`,
    async *entries() {
      // ...list & fetch from S3
      yield { path: 'doc-1.md', content: '...' };
    },
  };
}

Composition

withFilesystemSources(base, sources) accepts any sandbox base — a backend name, a SandboxFactory, or an existing SandboxEnv. It returns a SandboxFactory you pass to init({ sandbox }).

Mount multiple sources at different paths:

withFilesystemSources('empty', [
  { mountAt: '/workspace/kb',       source: localDirectorySource('./kb') },
  { mountAt: '/workspace/runbooks', source: localDirectorySource('./runbooks') },
  { mountAt: '/workspace/legal',    source: localDirectorySource('./legal-templates') },
]);

Wrap a non-empty backend so mounts coexist with the workspace:

withFilesystemSources('local', [{
  mountAt: '/workspace/reference',
  source: localDirectorySource('./reference'),
}]);

Conventions and limits

  • Read-only by default with session.mount(). Calling session.mount(mountAt, source) (no options) marks the mount as read-only — write tools (write, edit, mkdir, rm) targeting paths under it are rejected at the policy layer. Pass { mode: 'write' } to allow writes.
  • Read-only by convention with withFilesystemSources(). The legacy withFilesystemSources() factory does not enforce read-only — it relies on the agent's role/skill prompts. For hard enforcement, use session.mount() (default) or use a sandbox backend that supports it (Docker --read-only mounts via metadata.mountReadOnly, for example).
  • Eager population. Both withFilesystemSources and session.mount() walk the source(s) and write every file into the sandbox. This is the right model for small corpora; for very large knowledge bases use a retrieval-augmented approach instead.
  • One source per path. Mounting two sources at the same path is allowed but the second overwrites the first. Mount at distinct paths.
  • Path rerooting. session.mount('/mnt/kb', source) with sandbox cwd /workspace is rerooted to /workspace/mnt/kb since paths outside the sandbox boundary are blocked.

See also