Editable workspace sources
Let a custom source-backed mount write changes back to its provider.
Use this when a file tree should stay provider-owned, but the agent may update provider documents through workspace operations. Common examples are internal documents, CMS drafts, connected-drive files, and app-owned resources with existing authorization rules.
Provider Contract
Custom sources are read-only by default. To make one editable:
- Set the mount to
access: "readwrite". - Implement
write()forwrite,edit,append, and provider-destinationcopy. - Implement
delete()when workspace deletes should remove provider files.
Crux still owns path normalization and mount-boundary validation. The provider owns authorization, conflict behavior, revision ids, and native history.
import type { WorkspaceCustomMountSource } from "@use-crux/core/workspace";
export function editableAppDocumentSource(): WorkspaceCustomMountSource {
return {
kind: "custom",
list: async (path, options) => ({
entries: await appDocuments.list({
workspacePath: path,
limit: options?.limit,
}),
}),
read: async (path) => appDocuments.readWorkspaceText(path),
write: async (path, content, options) => {
if (typeof content !== "string") {
throw new Error("app documents only accept text content");
}
const saved = await appDocuments.upsertText({
workspacePath: path,
text: content,
mimeType: options?.mimeType,
metadata: options?.metadata,
});
return {
kind: "text" as const,
path,
mimeType: saved.mimeType,
content: saved.text,
size: new TextEncoder().encode(saved.text).byteLength,
metadata: saved.metadata,
};
},
delete: async (path) => {
await appDocuments.delete(path);
},
};
}Mount It
import { workspace } from "@use-crux/core/workspace";
const files = workspace({
id: "research-files",
namespace,
storage,
mounts: [
{ path: "/workspace", access: "readwrite" },
{
path: "/sources",
access: "readwrite",
source: editableAppDocumentSource(),
},
],
});The agent can use normal workspace operations:
await files.write("/sources/draft.md", "# Draft", {
mimeType: "text/markdown",
});
await files.edit("/sources/draft.md", {
find: "# Draft",
replace: "# Final",
});
await files.append("/sources/draft.md", "\n\nReviewed.");
await files.copy("/workspace/review.md", "/sources/review.md");
await files.delete("/sources/draft.md");edit() and append() read the provider file first, then call the same
write() hook with the updated text. Copying from a local workspace file into
the provider also calls write(). If the source omits write(), those
operations reject even when the mount is readwrite.
Local Snapshots
Provider writes do not create Crux-managed versions, artifact manifests, or
finalized deliverables. Use copy() when the agent needs a durable local
snapshot with normal workspace history:
await files.copy("/sources/draft.md", "/workspace/draft.md");