Переглянути джерело

refactor(simulation): remove filesystem layers (#35938)

James Long 1 місяць тому
батько
коміт
09553d46e0

+ 0 - 390
packages/simulation/src/backend/filesystem.ts

@@ -1,390 +0,0 @@
-import { Effect, FileSystem, Layer, Option, Stream } from "effect"
-import { systemError, type PlatformError, type SystemErrorTag } from "effect/PlatformError"
-import nodeFs from "fs"
-import path from "path"
-
-/**
- * In-memory simulated `FileSystem.FileSystem`.
- *
- * Replaces the `NodeFileSystem` platform node when the server runs in
- * simulation mode. Backed by a flat map of absolute paths to entries and
- * rooted at a single directory (the simulation anchor): paths that resolve
- * outside the root fail with `PermissionDenied` so host filesystem escapes
- * are loud. Only the operations the app actually uses are implemented;
- * everything else dies with a clear defect.
- *
- * Inspired by the V1 prototype on `jlongster/simulation-rebase`, rewritten
- * for the V2 platform node shape without the `just-bash` dependency.
- */
-
-export interface Options {
-  readonly root: string
-  readonly files?: Record<string, string | Uint8Array>
-}
-
-interface FileEntry {
-  readonly type: "File"
-  content: Uint8Array
-  mode: number
-  mtime: Date
-}
-
-interface DirectoryEntry {
-  readonly type: "Directory"
-  mode: number
-  mtime: Date
-}
-
-type Entry = FileEntry | DirectoryEntry
-
-export function make(options: Options): FileSystem.FileSystem {
-  const root = path.resolve(options.root)
-  const store = new Map<string, Entry>()
-  const temp = { value: 0 }
-  const encoder = new TextEncoder()
-  store.set(root, makeDirectoryEntry())
-
-  const within = (resolved: string) => resolved === root || resolved.startsWith(withSep(root))
-
-  const childrenOf = (resolved: string) => [...store.keys()].filter((key) => key.startsWith(withSep(resolved)))
-
-  const fail = (
-    tag: SystemErrorTag,
-    method: string,
-    file: string,
-    description?: string,
-  ): Effect.Effect<never, PlatformError> =>
-    Effect.fail(
-      systemError({ _tag: tag, module: "SimulationFileSystem", method, description, pathOrDescriptor: file }),
-    )
-
-  const locate = (method: string, file: string): Effect.Effect<string, PlatformError> => {
-    const resolved = path.resolve(root, file)
-    if (within(resolved)) return Effect.succeed(resolved)
-    return fail("PermissionDenied", method, file, "path escapes the simulated filesystem root")
-  }
-
-  const requireEntry = (method: string, file: string): Effect.Effect<readonly [string, Entry], PlatformError> =>
-    locate(method, file).pipe(
-      Effect.flatMap((resolved) => {
-        const entry = store.get(resolved)
-        if (!entry) return fail("NotFound", method, file)
-        return Effect.succeed([resolved, entry] as const)
-      }),
-    )
-
-  const requireParentDirectory = (
-    method: string,
-    resolved: string,
-    file: string,
-  ): Effect.Effect<void, PlatformError> => {
-    const parent = store.get(path.dirname(resolved))
-    if (parent?.type === "Directory") return Effect.void
-    return fail("NotFound", method, file, "parent directory does not exist")
-  }
-
-  // Creates every missing directory between root and resolved (inclusive).
-  const ensureDirectories = (method: string, file: string, resolved: string): Effect.Effect<void, PlatformError> =>
-    Effect.suspend(() => {
-      const segments = path.relative(root, resolved).split(path.sep).filter(Boolean)
-      const conflict = segments.reduce<string | Effect.Effect<never, PlatformError>>((current, segment) => {
-        if (typeof current !== "string") return current
-        const next = path.join(current, segment)
-        const entry = store.get(next)
-        if (entry && entry.type !== "Directory")
-          return fail("AlreadyExists", method, file, "path component is not a directory")
-        if (!entry) store.set(next, makeDirectoryEntry())
-        return next
-      }, root)
-      return typeof conflict === "string" ? Effect.void : conflict
-    })
-
-  // Seed initial files, creating parents as needed. Entries outside the root are ignored.
-  for (const [file, content] of Object.entries(options.files ?? {})) {
-    const resolved = path.resolve(root, file)
-    if (!within(resolved)) continue
-    Effect.runSync(ensureDirectories("seed", file, path.dirname(resolved)))
-    store.set(resolved, {
-      type: "File",
-      content: typeof content === "string" ? encoder.encode(content) : content.slice(),
-      mode: 0o644,
-      mtime: new Date(),
-    })
-  }
-
-  // Probe operations report NotFound outside the root instead of
-  // PermissionDenied: walk-up loops (project discovery, findUp, globUp)
-  // legitimately probe ancestor directories of the anchor and must observe
-  // "nothing there". Content access and mutation outside the root stay loud.
-  const probe = (method: string, file: string): Effect.Effect<Entry, PlatformError> =>
-    Effect.suspend(() => {
-      const resolved = path.resolve(root, file)
-      const entry = within(resolved) ? store.get(resolved) : undefined
-      if (!entry) return fail("NotFound", method, file)
-      return Effect.succeed(entry)
-    })
-
-  const stat: FileSystem.FileSystem["stat"] = (file) => probe("stat", file).pipe(Effect.map(toInfo))
-
-  const access: FileSystem.FileSystem["access"] = (file) => probe("access", file).pipe(Effect.asVoid)
-
-  const chmod: FileSystem.FileSystem["chmod"] = (file, mode) =>
-    requireEntry("chmod", file).pipe(
-      Effect.map(([, entry]) => {
-        entry.mode = mode
-      }),
-    )
-
-  const realPath: FileSystem.FileSystem["realPath"] = (file) =>
-    requireEntry("realPath", file).pipe(Effect.map(([resolved]) => resolved))
-
-  const readFile: FileSystem.FileSystem["readFile"] = (file) =>
-    requireEntry("readFile", file).pipe(
-      Effect.flatMap(([, entry]) => {
-        if (entry.type !== "File") return fail("BadResource", "readFile", file, "path is a directory")
-        return Effect.succeed(entry.content.slice())
-      }),
-    )
-
-  const writeFile: FileSystem.FileSystem["writeFile"] = (file, data, writeOptions) =>
-    locate("writeFile", file).pipe(
-      Effect.flatMap((resolved) => {
-        const existing = store.get(resolved)
-        if (existing?.type === "Directory") return fail("BadResource", "writeFile", file, "path is a directory")
-        return requireParentDirectory("writeFile", resolved, file).pipe(
-          Effect.map(() => {
-            store.set(resolved, {
-              type: "File",
-              content: data.slice(),
-              mode: writeOptions?.mode ?? existing?.mode ?? 0o644,
-              mtime: new Date(),
-            })
-          }),
-        )
-      }),
-    )
-
-  const makeDirectory: FileSystem.FileSystem["makeDirectory"] = (file, dirOptions) =>
-    locate("makeDirectory", file).pipe(
-      Effect.flatMap((resolved) => {
-        if (dirOptions?.recursive) return ensureDirectories("makeDirectory", file, resolved)
-        if (store.has(resolved)) return fail("AlreadyExists", "makeDirectory", file)
-        return requireParentDirectory("makeDirectory", resolved, file).pipe(
-          Effect.map(() => {
-            store.set(resolved, { type: "Directory", mode: dirOptions?.mode ?? 0o755, mtime: new Date() })
-          }),
-        )
-      }),
-    )
-
-  const readDirectory: FileSystem.FileSystem["readDirectory"] = (file, readOptions) =>
-    requireEntry("readDirectory", file).pipe(
-      Effect.flatMap(([resolved, entry]) => {
-        if (entry.type !== "Directory") return fail("BadResource", "readDirectory", file, "path is not a directory")
-        const children = childrenOf(resolved)
-        const names = readOptions?.recursive
-          ? children.map((key) => path.relative(resolved, key))
-          : children.filter((key) => path.dirname(key) === resolved).map((key) => path.basename(key))
-        return Effect.succeed(names.sort((a, b) => a.localeCompare(b)))
-      }),
-    )
-
-  const remove: FileSystem.FileSystem["remove"] = (file, removeOptions) =>
-    locate("remove", file).pipe(
-      Effect.flatMap((resolved) => {
-        const entry = store.get(resolved)
-        if (!entry) return removeOptions?.force ? Effect.void : fail("NotFound", "remove", file)
-        const children = childrenOf(resolved)
-        if (entry.type === "Directory" && children.length > 0 && !removeOptions?.recursive)
-          return fail("Unknown", "remove", file, "directory is not empty")
-        for (const key of children) store.delete(key)
-        store.delete(resolved)
-        // The root itself must always exist.
-        if (resolved === root) store.set(root, makeDirectoryEntry())
-        return Effect.void
-      }),
-    )
-
-  const rename: FileSystem.FileSystem["rename"] = (oldPath, newPath) =>
-    Effect.all([locate("rename", oldPath), locate("rename", newPath)]).pipe(
-      Effect.flatMap(([from, to]) => {
-        const entry = store.get(from)
-        if (!entry) return fail("NotFound", "rename", oldPath)
-        return requireParentDirectory("rename", to, newPath).pipe(
-          Effect.map(() => {
-            const moved = [from, ...childrenOf(from)].map((key) => [key, store.get(key)!] as const)
-            for (const [key] of moved) store.delete(key)
-            for (const key of [to, ...childrenOf(to)]) store.delete(key)
-            for (const [key, value] of moved) store.set(key === from ? to : to + key.slice(from.length), value)
-          }),
-        )
-      }),
-    )
-
-  const copy: FileSystem.FileSystem["copy"] = (fromPath, toPath) =>
-    Effect.all([locate("copy", fromPath), locate("copy", toPath)]).pipe(
-      Effect.flatMap(([from, to]) => {
-        const entry = store.get(from)
-        if (!entry) return fail("NotFound", "copy", fromPath)
-        return requireParentDirectory("copy", to, toPath).pipe(
-          Effect.map(() => {
-            for (const key of [from, ...childrenOf(from)]) {
-              const source = store.get(key)!
-              const target = key === from ? to : to + key.slice(from.length)
-              store.set(
-                target,
-                source.type === "File"
-                  ? { ...source, content: source.content.slice(), mtime: new Date() }
-                  : { ...source, mtime: new Date() },
-              )
-            }
-          }),
-        )
-      }),
-    )
-
-  const copyFile: FileSystem.FileSystem["copyFile"] = (fromPath, toPath) =>
-    readFile(fromPath).pipe(Effect.flatMap((content) => writeFile(toPath, content)))
-
-  const makeTempDirectory: FileSystem.FileSystem["makeTempDirectory"] = (tempOptions) =>
-    Effect.suspend(() => {
-      const directory = tempOptions?.directory ?? path.join(root, ".simulation-tmp")
-      const file = path.join(directory, `${tempOptions?.prefix ?? "tmp-"}${++temp.value}`)
-      return makeDirectory(file, { recursive: true }).pipe(Effect.map(() => file))
-    })
-
-  const makeTempDirectoryScoped: FileSystem.FileSystem["makeTempDirectoryScoped"] = (tempOptions) =>
-    Effect.acquireRelease(makeTempDirectory(tempOptions), (directory) =>
-      remove(directory, { recursive: true, force: true }).pipe(Effect.ignore),
-    )
-
-  // Read-only file handle: enough for the read tool's stat/seek/readAlloc use.
-  const open: FileSystem.FileSystem["open"] = (file) =>
-    requireEntry("open", file).pipe(
-      Effect.map(([resolved]) => {
-        const position = { value: 0 }
-        const contentOf = () => {
-          const current = store.get(resolved)
-          return current?.type === "File" ? current.content : new Uint8Array()
-        }
-        return {
-          [FileSystem.FileTypeId]: FileSystem.FileTypeId,
-          fd: FileSystem.FileDescriptor(0),
-          stat: Effect.suspend(() => stat(resolved)),
-          seek: (offset, from) =>
-            Effect.sync(() => {
-              position.value = from === "start" ? Number(offset) : position.value + Number(offset)
-            }),
-          sync: Effect.void,
-          read: (buffer) =>
-            Effect.sync(() => {
-              const chunk = contentOf().subarray(position.value, position.value + buffer.length)
-              buffer.set(chunk)
-              position.value += chunk.length
-              return FileSystem.Size(chunk.length)
-            }),
-          readAlloc: (size) =>
-            Effect.sync(() => {
-              const chunk = contentOf().slice(position.value, position.value + Number(size))
-              position.value += chunk.length
-              return chunk.length === 0 ? Option.none() : Option.some(chunk)
-            }),
-          truncate: () => unimplemented("File.truncate"),
-          write: () => unimplemented("File.write"),
-          writeAll: () => unimplemented("File.writeAll"),
-        } satisfies FileSystem.File
-      }),
-    )
-
-  return FileSystem.make({
-    access,
-    chmod,
-    chown: () => unimplemented("chown"),
-    copy,
-    copyFile,
-    link: () => unimplemented("link"),
-    makeDirectory,
-    makeTempDirectory,
-    makeTempDirectoryScoped,
-    makeTempFile: () => unimplemented("makeTempFile"),
-    makeTempFileScoped: () => unimplemented("makeTempFileScoped"),
-    open,
-    readDirectory,
-    readFile,
-    readLink: () => unimplemented("readLink"),
-    realPath,
-    remove,
-    rename,
-    stat,
-    symlink: () => unimplemented("symlink"),
-    truncate: () => unimplemented("truncate"),
-    utimes: () => unimplemented("utimes"),
-    watch: () => Stream.die(new Error("SimulationFileSystem.watch is not implemented in simulation")),
-    writeFile,
-  })
-}
-
-/**
- * Lazily constructed layer so the root defaults to `process.cwd()` at
- * layer-build time (the simulation anchor directory), not at import time.
- *
- * When `OPENCODE_SIMULATE_STATE` points at a snapshot directory, its
- * `files/` contents are read from the host once at build time and seeded
- * into the in-memory tree, joined onto the anchor root.
- */
-export const layer = (options?: Partial<Options>) =>
-  Layer.sync(FileSystem.FileSystem)(() =>
-    make({
-      root: options?.root ?? process.cwd(),
-      files: { ...loadSnapshotFiles(process.env.OPENCODE_SIMULATE_STATE), ...options?.files },
-    }),
-  )
-
-function loadSnapshotFiles(stateDirectory: string | undefined) {
-  if (!stateDirectory) return {}
-  const snapshot = path.join(stateDirectory, "files")
-  if (!nodeFs.existsSync(snapshot)) return {}
-  const files: Record<string, Uint8Array> = {}
-  const walk = (dir: string) => {
-    for (const entry of nodeFs.readdirSync(dir, { withFileTypes: true })) {
-      const file = path.join(dir, entry.name)
-      if (entry.isDirectory()) walk(file)
-      if (entry.isFile()) files[path.relative(snapshot, file)] = new Uint8Array(nodeFs.readFileSync(file))
-    }
-  }
-  walk(snapshot)
-  return files
-}
-
-function makeDirectoryEntry(): Entry {
-  return { type: "Directory", mode: 0o755, mtime: new Date() }
-}
-
-function withSep(dir: string) {
-  return dir.endsWith(path.sep) ? dir : dir + path.sep
-}
-
-function toInfo(entry: Entry): FileSystem.File.Info {
-  return {
-    type: entry.type,
-    mtime: Option.some(entry.mtime),
-    atime: Option.some(entry.mtime),
-    birthtime: Option.some(entry.mtime),
-    dev: 0,
-    ino: Option.none(),
-    mode: entry.mode,
-    nlink: Option.none(),
-    uid: Option.none(),
-    gid: Option.none(),
-    rdev: Option.none(),
-    size: FileSystem.Size(entry.type === "File" ? entry.content.length : 0),
-    blksize: Option.none(),
-    blocks: Option.none(),
-  }
-}
-
-function unimplemented(method: string) {
-  return Effect.die(new Error(`SimulationFileSystem.${method} is not implemented in simulation`))
-}
-
-export * as SimulationFileSystem from "./filesystem"

+ 0 - 215
packages/simulation/src/backend/fs-util.ts

@@ -1,215 +0,0 @@
-import { Effect, FileSystem, Layer } from "effect"
-import { FSUtil } from "@opencode-ai/core/fs-util"
-import { Glob } from "@opencode-ai/core/util/glob"
-import { makeGlobalNode } from "@opencode-ai/core/effect/app-node"
-import { filesystem } from "@opencode-ai/core/effect/app-node-platform"
-import path from "path"
-
-/**
- * Simulation replacement for `FSUtil`.
- *
- * This implementation is intentionally self-contained and only uses the
- * injected simulated `FileSystem.FileSystem`. The default FSUtil layer has a
- * few helpers that reach host-node APIs directly; depending on it here makes it
- * easy for mutation paths to escape or miss the in-memory project tree.
- */
-
-const layer = Layer.effect(
-  FSUtil.Service,
-  Effect.gen(function* () {
-    const fs = yield* FileSystem.FileSystem
-
-    const existsSafe = Effect.fn("SimulationFSUtil.existsSafe")(function* (file: string) {
-      return yield* fs.exists(file).pipe(Effect.orElseSucceed(() => false))
-    })
-
-    const isDir = Effect.fn("SimulationFSUtil.isDir")(function* (file: string) {
-      const info = yield* fs.stat(file).pipe(Effect.catch(() => Effect.succeed(undefined)))
-      return info?.type === "Directory"
-    })
-
-    const isFile = Effect.fn("SimulationFSUtil.isFile")(function* (file: string) {
-      const info = yield* fs.stat(file).pipe(Effect.catch(() => Effect.succeed(undefined)))
-      return info?.type === "File"
-    })
-
-    const realPath = Effect.fn("SimulationFSUtil.realPath")(function* (file: string) {
-      return yield* fs.realPath(file)
-    })
-
-    const stat = Effect.fn("SimulationFSUtil.stat")(function* (file: string) {
-      return yield* fs.stat(file)
-    })
-
-    const readFile = Effect.fn("SimulationFSUtil.readFile")(function* (file: string) {
-      return yield* fs.readFile(file)
-    })
-
-    const readFileString = Effect.fn("SimulationFSUtil.readFileString")(function* (file: string) {
-      return yield* fs.readFileString(file)
-    })
-
-    const readFileStringSafe = Effect.fn("SimulationFSUtil.readFileStringSafe")(function* (file: string) {
-      return yield* fs
-        .readFileString(file)
-        .pipe(Effect.catchReason("PlatformError", "NotFound", () => Effect.succeed(undefined)))
-    })
-
-    const readJson = Effect.fn("SimulationFSUtil.readJson")(function* (file: string) {
-      const text = yield* readFileString(file)
-      return JSON.parse(text) as unknown
-    })
-
-    const writeFile = Effect.fn("SimulationFSUtil.writeFile")(function* (
-      file: string,
-      data: Uint8Array,
-      options?: Parameters<typeof fs.writeFile>[2],
-    ) {
-      return yield* fs.writeFile(file, data, options)
-    })
-
-    const writeFileString = Effect.fn("SimulationFSUtil.writeFileString")(function* (
-      file: string,
-      data: string,
-      options?: Parameters<typeof fs.writeFileString>[2],
-    ) {
-      return yield* fs.writeFileString(file, data, options)
-    })
-
-    const makeDirectory: FileSystem.FileSystem["makeDirectory"] = (file, options) => fs.makeDirectory(file, options)
-
-    const ensureDir = Effect.fn("SimulationFSUtil.ensureDir")(function* (file: string) {
-      yield* fs.makeDirectory(file, { recursive: true })
-    })
-
-    const writeWithDirs = Effect.fn("SimulationFSUtil.writeWithDirs")(function* (
-      file: string,
-      content: string | Uint8Array,
-      mode?: number,
-    ) {
-      const write =
-        typeof content === "string"
-          ? fs.writeFileString(file, content)
-          : fs.writeFile(file, content)
-      yield* write.pipe(
-        Effect.catchReason("PlatformError", "NotFound", () =>
-          fs.makeDirectory(path.dirname(file), { recursive: true }).pipe(Effect.andThen(write)),
-        ),
-      )
-      if (mode !== undefined) yield* fs.chmod(file, mode)
-    })
-
-    const writeJson = Effect.fn("SimulationFSUtil.writeJson")(function* (file: string, data: unknown, mode?: number) {
-      yield* writeFileString(file, JSON.stringify(data, null, 2))
-      if (mode !== undefined) yield* fs.chmod(file, mode)
-    })
-
-    const readDirectoryEntries = Effect.fn("SimulationFSUtil.readDirectoryEntries")(function* (dirPath: string) {
-      const names = yield* fs.readDirectory(dirPath)
-      return yield* Effect.forEach(names, (name) =>
-        fs.stat(path.join(dirPath, name)).pipe(
-          Effect.map(
-            (info): FSUtil.DirEntry => ({
-              name,
-              type:
-                info.type === "Directory"
-                  ? "directory"
-                  : info.type === "File"
-                    ? "file"
-                    : info.type === "SymbolicLink"
-                      ? "symlink"
-                      : "other",
-            }),
-          ),
-          Effect.orElseSucceed((): FSUtil.DirEntry => ({ name, type: "other" })),
-        ),
-      )
-    })
-
-    const resolve = Effect.fn("SimulationFSUtil.resolve")(function* (input: string) {
-      return path.resolve(input)
-    })
-
-    const glob = Effect.fn("SimulationFSUtil.glob")(function* (pattern: string, options?: Glob.Options) {
-      const cwd = path.resolve(options?.cwd ?? process.cwd())
-      const entries = yield* fs
-        .readDirectory(cwd, { recursive: true })
-        .pipe(Effect.orElseSucceed(() => [] as string[]))
-      const matches = yield* Effect.forEach(entries, (entry) =>
-        fs.stat(path.join(cwd, entry)).pipe(
-          Effect.map((info) => ({ entry, type: info.type })),
-          Effect.orElseSucceed(() => undefined),
-        ),
-      )
-      return matches
-        .filter((item) => item !== undefined)
-        .filter((item) => options?.include === "all" || item.type === "File")
-        .filter((item) => Glob.match(pattern, item.entry))
-        .map((item) => (options?.absolute ? path.join(cwd, item.entry) : item.entry))
-        .sort((a, b) => a.localeCompare(b))
-    })
-
-    const globUp = Effect.fn("SimulationFSUtil.globUp")(function* (pattern: string, start: string, stop?: string) {
-      const result: string[] = []
-      let current = path.resolve(start)
-      while (true) {
-        result.push(...(yield* glob(pattern, { cwd: current, absolute: true, include: "file", dot: true })))
-        if (stop === current) break
-        const parent = path.dirname(current)
-        if (parent === current) break
-        current = parent
-      }
-      return result
-    })
-
-    const up = Effect.fn("SimulationFSUtil.up")(function* (options: { targets: string[]; start: string; stop?: string }) {
-      const result: string[] = []
-      let current = path.resolve(options.start)
-      while (true) {
-        for (const target of options.targets) {
-          const search = path.join(current, target)
-          if (yield* fs.exists(search)) result.push(search)
-        }
-        if (options.stop === current) break
-        const parent = path.dirname(current)
-        if (parent === current) break
-        current = parent
-      }
-      return result
-    })
-
-    const findUp = Effect.fn("SimulationFSUtil.findUp")(function* (target: string, start: string, stop?: string) {
-      return yield* up({ targets: [target], start, stop })
-    })
-
-    return FSUtil.Service.of({
-      ...fs,
-      realPath,
-      stat,
-      readFile,
-      readFileString,
-      writeFile,
-      writeFileString,
-      makeDirectory,
-      isDir,
-      isFile,
-      existsSafe,
-      readFileStringSafe,
-      readJson,
-      writeJson,
-      ensureDir,
-      writeWithDirs,
-      readDirectoryEntries,
-      resolve,
-      findUp,
-      up,
-      globUp,
-      glob,
-      globMatch: Glob.match,
-    })
-  }),
-)
-
-export const node = makeGlobalNode({ service: FSUtil.Service, layer, deps: [filesystem] })
-
-export * as SimulationFSUtil from "./fs-util"

+ 1 - 8
packages/simulation/src/backend/index.ts

@@ -1,10 +1,7 @@
 import { LayerNode } from "@opencode-ai/core/effect/layer-node"
-import { filesystem, httpClient } from "@opencode-ai/core/effect/app-node-platform"
-import { FSUtil } from "@opencode-ai/core/fs-util"
+import { httpClient } from "@opencode-ai/core/effect/app-node-platform"
 import { DriveManifest } from "../manifest"
 import { SimulationControl } from "./control"
-import { SimulationFileSystem } from "./filesystem"
-import { SimulationFSUtil } from "./fs-util"
 import { SimulationNetwork } from "./network"
 import { SimulationOpenAI } from "./openai"
 
@@ -14,8 +11,6 @@ import { SimulationOpenAI } from "./openai"
  * The server merges these into the app node build when `OPENCODE_SIMULATE`
  * is enabled, via a dynamic import so this module is never loaded eagerly.
  *
- * - Filesystem: in-memory tree rooted at the current working directory.
- *   Everything under the root lives in memory; paths outside it fail loudly.
  * - Network: all outbound HTTP resolves against the simulated route table;
  *   unknown destinations are denied. The driver-answered OpenAI endpoint is
  *   registered here as the first route.
@@ -32,8 +27,6 @@ export function startDriveServer() {
 }
 
 export const simulationReplacements: LayerNode.Replacements = [
-  [filesystem, SimulationFileSystem.layer()],
-  [FSUtil.node, SimulationFSUtil.node],
   [httpClient, SimulationNetwork.layer],
 ]