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 { defineAgent, localDirectorySource, withFilesystemSources } from '@fabric-harness/sdk/lite';
const sandbox = withFilesystemSources('empty', [{
mountAt: '/workspace/kb',
source: localDirectorySource('./knowledge-base', { include: (p) => p.endsWith('.md') }),
}]);
export default defineAgent(async ({ init, payload }) => {
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/lite';
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.
S3 and Azure Blob structural sources
@fabric-harness/connectors includes dependency-free structural helpers for S3-compatible and Azure Blob-compatible clients. They do not import cloud SDKs; adapt your cloud SDK calls to the small client shape.
import { s3FilesystemSource, azureBlobFilesystemSource } from '@fabric-harness/connectors';
const s3Source = s3FilesystemSource(s3ClientAdapter, { prefix: 'knowledge-base/' });
const azureSource = 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 convention. The SDK doesn't block writes to mounted paths — it relies on the agent's role/skill prompts to enforce that. If you need hard read-only enforcement, use a sandbox backend that supports it (Docker
--read-onlymounts viametadata.mountReadOnly, for example). - Eager population.
withFilesystemSourceswalks the source(s) and writes every file into the sandbox at creation time. 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.
See also
- Headless agents with the Lite SDK — the minimal entrypoint story this primitive belongs to.
- examples/support-agent — a runnable end-to-end example.
- Sandboxes — the broader sandbox model.