From 9ff32ba6171c6c66e1513376db08a94902444219 Mon Sep 17 00:00:00 2001 From: factory-ain3sh Date: Fri, 24 Jul 2026 01:06:25 -0700 Subject: [PATCH 1/8] refactor: rename workspace runtime to vfs Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .../assets/{agentfs-arch.svg => vfs-arch.svg} | 0 Cargo.lock | 208 +++++++++--------- Cargo.toml | 22 +- crates/agentfs-cli/scripts/mount.fuse.agentfs | 14 -- crates/agentfs-core/.gitignore | 4 - crates/{agentfs-cli => vfs-cli}/.gitignore | 0 .../{agentfs-cli => vfs-cli}/CONTRIBUTING.md | 2 +- crates/{agentfs-cli => vfs-cli}/Cargo.toml | 12 +- crates/{agentfs-cli => vfs-cli}/build.rs | 4 +- .../perf/simulate-npm-workload.sh | 2 +- .../perf/syscall/Makefile | 0 .../perf/syscall/hello.txt | 0 .../perf/syscall/perf-open-close.c | 0 .../perf/syscall/perf-statx.c | 0 .../perf/syscall/run.sh | 40 ++-- .../scripts/fmt-imports | 0 crates/vfs-cli/scripts/mount.fuse.vfs | 14 ++ .../{agentfs-cli => vfs-cli}/src/cmd/clone.rs | 32 +-- .../src/cmd/completions.rs | 10 +- .../{agentfs-cli => vfs-cli}/src/cmd/exec.rs | 32 +-- crates/{agentfs-cli => vfs-cli}/src/cmd/fs.rs | 185 +++++++--------- .../{agentfs-cli => vfs-cli}/src/cmd/init.rs | 43 ++-- .../src/cmd/mcp_server.rs | 192 ++++++++-------- .../src/cmd/mcp_server/tests.rs | 93 ++++---- .../src/cmd/migrate.rs | 49 ++--- .../{agentfs-cli => vfs-cli}/src/cmd/mod.rs | 0 .../{agentfs-cli => vfs-cli}/src/cmd/mount.rs | 87 ++++---- .../{agentfs-cli => vfs-cli}/src/cmd/nfs.rs | 34 +-- .../src/cmd/profiling.rs | 7 +- crates/{agentfs-cli => vfs-cli}/src/cmd/ps.rs | 22 +- .../src/cmd/run/darwin.rs | 46 ++-- .../src/cmd/run/linux.rs | 75 +++---- .../src/cmd/run/mod.rs | 0 .../src/cmd/run/not_supported.rs | 0 .../src/cmd/run/tests.rs | 22 +- .../src/cmd/safety.rs | 51 +++-- .../{agentfs-cli => vfs-cli}/src/cmd/sync.rs | 20 +- .../src/cmd/timeline.rs | 73 +++--- crates/{agentfs-cli => vfs-cli}/src/config.rs | 24 +- crates/{agentfs-cli => vfs-cli}/src/docs.rs | 18 +- crates/{agentfs-cli => vfs-cli}/src/knobs.rs | 186 ++++++++-------- crates/{agentfs-cli => vfs-cli}/src/lib.rs | 6 +- .../{agentfs-cli => vfs-cli}/src/logging.rs | 34 ++- crates/{agentfs-cli => vfs-cli}/src/main.rs | 40 ++-- crates/{agentfs-cli => vfs-cli}/src/opts.rs | 96 ++++---- .../{agentfs-cli => vfs-cli}/tests/.gitignore | 0 crates/{agentfs-cli => vfs-cli}/tests/all.sh | 40 ++-- .../tests/fixtures/migrate/v0_0.db | Bin .../tests/fixtures/migrate/v0_2.db | Bin .../tests/fixtures/migrate/v0_4-encrypted.db | Bin .../tests/fixtures/migrate/v0_4.db | Bin .../tests/migrate_fixtures.rs | 4 +- .../tests/syscall/Makefile | 0 .../tests/syscall/main.c | 0 .../tests/syscall/test-append.c | 0 .../tests/syscall/test-close.c | 0 .../tests/syscall/test-common.h | 0 .../syscall/test-copyup-inode-stability.c | 0 .../tests/syscall/test-copyup-permissions.c | 0 .../tests/syscall/test-create-existing.c | 0 .../tests/syscall/test-dup.c | 0 .../tests/syscall/test-dup2.c | 0 .../tests/syscall/test-fstat.c | 0 .../tests/syscall/test-getdents64.c | 0 .../tests/syscall/test-link.c | 0 .../tests/syscall/test-lstat.c | 0 .../tests/syscall/test-mkfifo.c | 0 .../tests/syscall/test-mknod.c | 0 .../tests/syscall/test-open-readonly.c | 0 .../tests/syscall/test-openat.c | 0 .../tests/syscall/test-pread-sparse.c | 0 .../tests/syscall/test-read.c | 0 .../tests/syscall/test-rename.c | 0 .../tests/syscall/test-stat.c | 0 .../tests/syscall/test-unlink.c | 0 .../tests/syscall/test-write.c | 0 .../tests/test-cli-smoke.sh | 126 +++++------ .../tests/test-corruption-torture.sh | 26 +-- .../tests/test-ephemeral-sidecar-cleanup.sh | 48 ++-- .../tests/test-fuse-cache-invalidation.sh | 22 +- .../tests/test-fuse-sigint-panic-census.sh | 38 ++-- .../tests/test-init.sh | 26 +-- .../tests/test-linux-syscalls.sh | 2 +- .../tests/test-mcp-server.sh | 16 +- .../tests/test-migrate-consolidation.sh | 50 ++--- .../tests/test-mount.sh | 32 +-- .../test-overlay-base-dir-rename-exdev.sh | 22 +- .../tests/test-overlay-delta-in-base-dir.sh | 22 +- .../tests/test-overlay-whiteout.sh | 24 +- .../tests/test-profile-error-summary.sh | 20 +- .../tests/test-run-bash.sh | 16 +- .../tests/test-run-git.sh | 20 +- .../tests/test-run-read-scoping.sh | 28 +-- .../tests/test-run-syscalls.sh | 28 +-- .../tests/test-second-opener.sh | 30 +-- .../tests/test-sigkill-recovery.sh | 30 +-- .../tests/test-signal-teardown.sh | 60 ++--- .../tests/test-symlinks.sh | 24 +- .../tests/test-teardown-bounded.sh | 50 ++--- .../{agentfs-cli => vfs-cli}/tests/test_fd.c | 0 crates/vfs-core/.gitignore | 4 + crates/{agentfs-core => vfs-core}/Cargo.toml | 4 +- .../benches/overlayfs.rs | 6 +- .../benches/workload.rs | 16 +- .../src/config/core.rs | 16 +- .../src/config/env.rs | 0 .../src/config/mod.rs | 28 +-- .../{agentfs-core => vfs-core}/src/error.rs | 4 +- .../src/fs/host/common.rs | 0 .../src/fs/host/darwin.rs | 0 .../src/fs/host/linux.rs | 0 .../src/fs/host/mod.rs | 0 .../{agentfs-core => vfs-core}/src/fs/mod.rs | 8 +- .../src/fs/overlay/copyup.rs | 0 .../src/fs/overlay/fs.rs | 4 +- .../src/fs/overlay/maps.rs | 0 .../src/fs/overlay/mod.rs | 18 +- .../src/fs/overlay/partial.rs | 4 +- .../src/fs/overlay/tests.rs | 192 ++++++++-------- .../src/fs/overlay/whiteouts.rs | 0 .../src/fs/vfs}/batcher.rs | 87 ++++---- .../agentfs => vfs-core/src/fs/vfs}/caches.rs | 4 +- .../agentfs => vfs-core/src/fs/vfs}/file.rs | 16 +- .../fs/agentfs => vfs-core/src/fs/vfs}/fs.rs | 20 +- .../agentfs => vfs-core/src/fs/vfs}/import.rs | 10 +- .../src/fs/vfs}/lifecycle.rs | 4 +- .../fs/agentfs => vfs-core/src/fs/vfs}/mod.rs | 36 +-- .../src/fs/vfs}/path_api.rs | 4 +- .../agentfs => vfs-core/src/fs/vfs}/store.rs | 2 +- .../agentfs => vfs-core/src/fs/vfs}/tests.rs | 88 ++++---- crates/{agentfs-core => vfs-core}/src/kv.rs | 4 +- crates/{agentfs-core => vfs-core}/src/lib.rs | 187 ++++++++-------- .../{agentfs-core => vfs-core}/src/mounts.rs | 12 +- .../{agentfs-core => vfs-core}/src/options.rs | 40 ++-- crates/{agentfs-core => vfs-core}/src/pool.rs | 2 +- .../src/schema/integrity.rs | 6 +- .../src/schema/mod.rs | 27 ++- .../src/semantics/access.rs | 0 .../src/semantics/durability.rs | 2 +- .../src/semantics/handles.rs | 12 +- .../src/semantics/mod.rs | 8 +- .../src/telemetry/mod.rs | 64 +++--- .../src/toolcalls.rs | 4 +- .../tests/concurrency_integrity.rs | 16 +- .../tests/host_layout.rs | 0 .../tests/snapshot_restore.rs | 38 ++-- crates/{agentfs-fuse => vfs-fuse}/Cargo.toml | 6 +- .../src/adapter/cache.rs | 2 +- .../src/adapter/config.rs | 163 +++++++------- .../src/adapter/ino_files.rs | 2 +- .../src/adapter/mod.rs | 62 +++--- .../src/adapter/write_buffer.rs | 20 +- crates/{agentfs-fuse => vfs-fuse}/src/lib.rs | 4 +- .../src/telemetry.rs | 10 +- .../src/transport/channel.rs | 0 .../src/transport/deferred_notify.rs | 0 .../src/transport/ll/argument.rs | 0 .../src/transport/ll/fuse_abi.rs | 0 .../src/transport/ll/mod.rs | 0 .../src/transport/ll/notify.rs | 0 .../src/transport/ll/reply.rs | 0 .../src/transport/ll/request.rs | 0 .../src/transport/mnt/fuse_pure.rs | 4 +- .../src/transport/mnt/mod.rs | 0 .../src/transport/mnt/mount_options.rs | 7 +- .../src/transport/mod.rs | 0 .../src/transport/notify.rs | 0 .../src/transport/reply.rs | 20 +- .../src/transport/request.rs | 0 .../src/transport/session.rs | 2 +- .../src/transport/uring.rs | 4 +- .../{agentfs-mount => vfs-mount}/Cargo.toml | 10 +- .../src/daemon.rs | 2 +- .../{agentfs-mount => vfs-mount}/src/fuse.rs | 79 +++---- .../{agentfs-mount => vfs-mount}/src/lib.rs | 34 ++- .../{agentfs-mount => vfs-mount}/src/nfs.rs | 4 +- .../src/supervise.rs | 6 +- crates/{agentfs-nfs => vfs-nfs}/Cargo.toml | 6 +- .../{agentfs-nfs => vfs-nfs}/src/adapter.rs | 34 +-- crates/{agentfs-nfs => vfs-nfs}/src/lib.rs | 8 +- .../src/server/context.rs | 0 .../src/server/mod.rs | 0 .../src/server/mount.rs | 0 .../src/server/mount_handlers.rs | 0 .../src/server/nfs.rs | 0 .../src/server/nfs_handlers.rs | 38 ++-- .../src/server/permissions.rs | 10 +- .../src/server/portmap.rs | 0 .../src/server/portmap_handlers.rs | 0 .../src/server/rpc.rs | 0 .../src/server/rpcwire.rs | 8 +- .../src/server/tcp.rs | 18 +- .../src/server/transaction_tracker.rs | 0 .../src/server/vfs.rs | 2 +- .../src/server/write_counter.rs | 0 .../src/server/xdr.rs | 0 ...ne-benchmark.py => vfs-clone-benchmark.py} | 0 197 files changed, 2016 insertions(+), 2149 deletions(-) rename .github/assets/{agentfs-arch.svg => vfs-arch.svg} (100%) delete mode 100755 crates/agentfs-cli/scripts/mount.fuse.agentfs delete mode 100644 crates/agentfs-core/.gitignore rename crates/{agentfs-cli => vfs-cli}/.gitignore (100%) rename crates/{agentfs-cli => vfs-cli}/CONTRIBUTING.md (63%) rename crates/{agentfs-cli => vfs-cli}/Cargo.toml (83%) rename crates/{agentfs-cli => vfs-cli}/build.rs (92%) rename crates/{agentfs-cli => vfs-cli}/perf/simulate-npm-workload.sh (99%) rename crates/{agentfs-cli => vfs-cli}/perf/syscall/Makefile (100%) rename crates/{agentfs-cli => vfs-cli}/perf/syscall/hello.txt (100%) rename crates/{agentfs-cli => vfs-cli}/perf/syscall/perf-open-close.c (100%) rename crates/{agentfs-cli => vfs-cli}/perf/syscall/perf-statx.c (100%) rename crates/{agentfs-cli => vfs-cli}/perf/syscall/run.sh (58%) rename crates/{agentfs-cli => vfs-cli}/scripts/fmt-imports (100%) create mode 100755 crates/vfs-cli/scripts/mount.fuse.vfs rename crates/{agentfs-cli => vfs-cli}/src/cmd/clone.rs (95%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/completions.rs (95%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/exec.rs (81%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/fs.rs (77%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/init.rs (88%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/mcp_server.rs (85%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/mcp_server/tests.rs (82%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/migrate.rs (98%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/mod.rs (100%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/mount.rs (91%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/nfs.rs (75%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/profiling.rs (77%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/ps.rs (93%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/run/darwin.rs (94%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/run/linux.rs (95%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/run/mod.rs (100%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/run/not_supported.rs (100%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/run/tests.rs (95%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/safety.rs (96%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/sync.rs (77%) rename crates/{agentfs-cli => vfs-cli}/src/cmd/timeline.rs (84%) rename crates/{agentfs-cli => vfs-cli}/src/config.rs (93%) rename crates/{agentfs-cli => vfs-cli}/src/docs.rs (94%) rename crates/{agentfs-cli => vfs-cli}/src/knobs.rs (87%) rename crates/{agentfs-cli => vfs-cli}/src/lib.rs (86%) rename crates/{agentfs-cli => vfs-cli}/src/logging.rs (67%) rename crates/{agentfs-cli => vfs-cli}/src/main.rs (93%) rename crates/{agentfs-cli => vfs-cli}/src/opts.rs (90%) rename crates/{agentfs-cli => vfs-cli}/tests/.gitignore (100%) rename crates/{agentfs-cli => vfs-cli}/tests/all.sh (78%) rename crates/{agentfs-cli => vfs-cli}/tests/fixtures/migrate/v0_0.db (100%) rename crates/{agentfs-cli => vfs-cli}/tests/fixtures/migrate/v0_2.db (100%) rename crates/{agentfs-cli => vfs-cli}/tests/fixtures/migrate/v0_4-encrypted.db (100%) rename crates/{agentfs-cli => vfs-cli}/tests/fixtures/migrate/v0_4.db (100%) rename crates/{agentfs-cli => vfs-cli}/tests/migrate_fixtures.rs (98%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/Makefile (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/main.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-append.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-close.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-common.h (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-copyup-inode-stability.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-copyup-permissions.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-create-existing.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-dup.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-dup2.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-fstat.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-getdents64.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-link.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-lstat.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-mkfifo.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-mknod.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-open-readonly.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-openat.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-pread-sparse.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-read.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-rename.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-stat.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-unlink.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/syscall/test-write.c (100%) rename crates/{agentfs-cli => vfs-cli}/tests/test-cli-smoke.sh (72%) rename crates/{agentfs-cli => vfs-cli}/tests/test-corruption-torture.sh (94%) rename crates/{agentfs-cli => vfs-cli}/tests/test-ephemeral-sidecar-cleanup.sh (76%) rename crates/{agentfs-cli => vfs-cli}/tests/test-fuse-cache-invalidation.sh (92%) rename crates/{agentfs-cli => vfs-cli}/tests/test-fuse-sigint-panic-census.sh (90%) rename crates/{agentfs-cli => vfs-cli}/tests/test-init.sh (53%) rename crates/{agentfs-cli => vfs-cli}/tests/test-linux-syscalls.sh (94%) rename crates/{agentfs-cli => vfs-cli}/tests/test-mcp-server.sh (96%) rename crates/{agentfs-cli => vfs-cli}/tests/test-migrate-consolidation.sh (81%) rename crates/{agentfs-cli => vfs-cli}/tests/test-mount.sh (88%) rename crates/{agentfs-cli => vfs-cli}/tests/test-overlay-base-dir-rename-exdev.sh (90%) rename crates/{agentfs-cli => vfs-cli}/tests/test-overlay-delta-in-base-dir.sh (88%) rename crates/{agentfs-cli => vfs-cli}/tests/test-overlay-whiteout.sh (81%) rename crates/{agentfs-cli => vfs-cli}/tests/test-profile-error-summary.sh (68%) rename crates/{agentfs-cli => vfs-cli}/tests/test-run-bash.sh (68%) rename crates/{agentfs-cli => vfs-cli}/tests/test-run-git.sh (72%) rename crates/{agentfs-cli => vfs-cli}/tests/test-run-read-scoping.sh (74%) rename crates/{agentfs-cli => vfs-cli}/tests/test-run-syscalls.sh (79%) rename crates/{agentfs-cli => vfs-cli}/tests/test-second-opener.sh (79%) rename crates/{agentfs-cli => vfs-cli}/tests/test-sigkill-recovery.sh (90%) rename crates/{agentfs-cli => vfs-cli}/tests/test-signal-teardown.sh (85%) rename crates/{agentfs-cli => vfs-cli}/tests/test-symlinks.sh (72%) rename crates/{agentfs-cli => vfs-cli}/tests/test-teardown-bounded.sh (90%) rename crates/{agentfs-cli => vfs-cli}/tests/test_fd.c (100%) create mode 100644 crates/vfs-core/.gitignore rename crates/{agentfs-core => vfs-core}/Cargo.toml (93%) rename crates/{agentfs-core => vfs-core}/benches/overlayfs.rs (90%) rename crates/{agentfs-core => vfs-core}/benches/workload.rs (96%) rename crates/{agentfs-core => vfs-core}/src/config/core.rs (87%) rename crates/{agentfs-core => vfs-core}/src/config/env.rs (100%) rename crates/{agentfs-core => vfs-core}/src/config/mod.rs (91%) rename crates/{agentfs-core => vfs-core}/src/error.rs (97%) rename crates/{agentfs-core => vfs-core}/src/fs/host/common.rs (100%) rename crates/{agentfs-core => vfs-core}/src/fs/host/darwin.rs (100%) rename crates/{agentfs-core => vfs-core}/src/fs/host/linux.rs (100%) rename crates/{agentfs-core => vfs-core}/src/fs/host/mod.rs (100%) rename crates/{agentfs-core => vfs-core}/src/fs/mod.rs (98%) rename crates/{agentfs-core => vfs-core}/src/fs/overlay/copyup.rs (100%) rename crates/{agentfs-core => vfs-core}/src/fs/overlay/fs.rs (99%) rename crates/{agentfs-core => vfs-core}/src/fs/overlay/maps.rs (100%) rename crates/{agentfs-core => vfs-core}/src/fs/overlay/mod.rs (97%) rename crates/{agentfs-core => vfs-core}/src/fs/overlay/partial.rs (99%) rename crates/{agentfs-core => vfs-core}/src/fs/overlay/tests.rs (94%) rename crates/{agentfs-core => vfs-core}/src/fs/overlay/whiteouts.rs (100%) rename crates/{agentfs-core/src/fs/agentfs => vfs-core/src/fs/vfs}/batcher.rs (94%) rename crates/{agentfs-core/src/fs/agentfs => vfs-core/src/fs/vfs}/caches.rs (97%) rename crates/{agentfs-core/src/fs/agentfs => vfs-core/src/fs/vfs}/file.rs (96%) rename crates/{agentfs-core/src/fs/agentfs => vfs-core/src/fs/vfs}/fs.rs (99%) rename crates/{agentfs-core/src/fs/agentfs => vfs-core/src/fs/vfs}/import.rs (97%) rename crates/{agentfs-core/src/fs/agentfs => vfs-core/src/fs/vfs}/lifecycle.rs (98%) rename crates/{agentfs-core/src/fs/agentfs => vfs-core/src/fs/vfs}/mod.rs (96%) rename crates/{agentfs-core/src/fs/agentfs => vfs-core/src/fs/vfs}/path_api.rs (98%) rename crates/{agentfs-core/src/fs/agentfs => vfs-core/src/fs/vfs}/store.rs (99%) rename crates/{agentfs-core/src/fs/agentfs => vfs-core/src/fs/vfs}/tests.rs (97%) rename crates/{agentfs-core => vfs-core}/src/kv.rs (96%) rename crates/{agentfs-core => vfs-core}/src/lib.rs (82%) rename crates/{agentfs-core => vfs-core}/src/mounts.rs (73%) rename crates/{agentfs-core => vfs-core}/src/options.rs (87%) rename crates/{agentfs-core => vfs-core}/src/pool.rs (99%) rename crates/{agentfs-core => vfs-core}/src/schema/integrity.rs (99%) rename crates/{agentfs-core => vfs-core}/src/schema/mod.rs (97%) rename crates/{agentfs-core => vfs-core}/src/semantics/access.rs (100%) rename crates/{agentfs-core => vfs-core}/src/semantics/durability.rs (98%) rename crates/{agentfs-core => vfs-core}/src/semantics/handles.rs (96%) rename crates/{agentfs-core => vfs-core}/src/semantics/mod.rs (93%) rename crates/{agentfs-core => vfs-core}/src/telemetry/mod.rs (91%) rename crates/{agentfs-core => vfs-core}/src/toolcalls.rs (99%) rename crates/{agentfs-core => vfs-core}/tests/concurrency_integrity.rs (95%) rename crates/{agentfs-core => vfs-core}/tests/host_layout.rs (100%) rename crates/{agentfs-core => vfs-core}/tests/snapshot_restore.rs (92%) rename crates/{agentfs-fuse => vfs-fuse}/Cargo.toml (83%) rename crates/{agentfs-fuse => vfs-fuse}/src/adapter/cache.rs (99%) rename crates/{agentfs-fuse => vfs-fuse}/src/adapter/config.rs (83%) rename crates/{agentfs-fuse => vfs-fuse}/src/adapter/ino_files.rs (98%) rename crates/{agentfs-fuse => vfs-fuse}/src/adapter/mod.rs (98%) rename crates/{agentfs-fuse => vfs-fuse}/src/adapter/write_buffer.rs (94%) rename crates/{agentfs-fuse => vfs-fuse}/src/lib.rs (91%) rename crates/{agentfs-fuse => vfs-fuse}/src/telemetry.rs (97%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/channel.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/deferred_notify.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/ll/argument.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/ll/fuse_abi.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/ll/mod.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/ll/notify.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/ll/reply.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/ll/request.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/mnt/fuse_pure.rs (99%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/mnt/mod.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/mnt/mount_options.rs (93%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/mod.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/notify.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/reply.rs (98%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/request.rs (100%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/session.rs (99%) rename crates/{agentfs-fuse => vfs-fuse}/src/transport/uring.rs (99%) rename crates/{agentfs-mount => vfs-mount}/Cargo.toml (68%) rename crates/{agentfs-mount => vfs-mount}/src/daemon.rs (99%) rename crates/{agentfs-mount => vfs-mount}/src/fuse.rs (81%) rename crates/{agentfs-mount => vfs-mount}/src/lib.rs (95%) rename crates/{agentfs-mount => vfs-mount}/src/nfs.rs (98%) rename crates/{agentfs-mount => vfs-mount}/src/supervise.rs (99%) rename crates/{agentfs-nfs => vfs-nfs}/Cargo.toml (85%) rename crates/{agentfs-nfs => vfs-nfs}/src/adapter.rs (97%) rename crates/{agentfs-nfs => vfs-nfs}/src/lib.rs (97%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/context.rs (100%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/mod.rs (100%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/mount.rs (100%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/mount_handlers.rs (100%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/nfs.rs (100%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/nfs_handlers.rs (99%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/permissions.rs (94%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/portmap.rs (100%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/portmap_handlers.rs (100%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/rpc.rs (100%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/rpcwire.rs (98%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/tcp.rs (96%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/transaction_tracker.rs (100%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/vfs.rs (99%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/write_counter.rs (100%) rename crates/{agentfs-nfs => vfs-nfs}/src/server/xdr.rs (100%) rename scripts/validation/{agentfs-clone-benchmark.py => vfs-clone-benchmark.py} (100%) diff --git a/.github/assets/agentfs-arch.svg b/.github/assets/vfs-arch.svg similarity index 100% rename from .github/assets/agentfs-arch.svg rename to .github/assets/vfs-arch.svg diff --git a/Cargo.lock b/Cargo.lock index ae3fcbc3..305decd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,110 +47,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "agentfs-cli" -version = "0.6.4" -dependencies = [ - "agentfs-core", - "agentfs-mount", - "anyhow", - "base64", - "chrono", - "clap", - "clap_complete", - "dirs", - "hex", - "libc", - "parking_lot", - "serde", - "serde_json", - "sha1", - "tempfile", - "tokio", - "tracing", - "tracing-subscriber", - "turso", - "uuid", -] - -[[package]] -name = "agentfs-core" -version = "0.6.4" -dependencies = [ - "aegis", - "async-trait", - "criterion", - "libc", - "lru", - "parking_lot", - "proptest", - "rand 0.8.6", - "serde", - "serde_json", - "smallvec", - "tempfile", - "thiserror 1.0.69", - "tokio", - "tracing", - "turso", -] - -[[package]] -name = "agentfs-fuse" -version = "0.6.4" -dependencies = [ - "agentfs-core", - "anyhow", - "async-trait", - "libc", - "memchr", - "nix", - "page_size", - "parking_lot", - "smallvec", - "tempfile", - "tokio", - "tracing", - "turso", - "zerocopy", -] - -[[package]] -name = "agentfs-mount" -version = "0.6.4" -dependencies = [ - "agentfs-core", - "agentfs-fuse", - "agentfs-nfs", - "anyhow", - "async-trait", - "libc", - "tempfile", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "agentfs-nfs" -version = "0.6.4" -dependencies = [ - "agentfs-core", - "anyhow", - "async-trait", - "byteorder", - "filetime", - "libc", - "num-derive", - "num-traits", - "smallvec", - "tempfile", - "tokio", - "tokio-util", - "tracing", - "uuid", -] - [[package]] name = "aho-corasick" version = "1.1.4" @@ -2942,6 +2838,110 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vfs-cli" +version = "0.6.4" +dependencies = [ + "anyhow", + "base64", + "chrono", + "clap", + "clap_complete", + "dirs", + "hex", + "libc", + "parking_lot", + "serde", + "serde_json", + "sha1", + "tempfile", + "tokio", + "tracing", + "tracing-subscriber", + "turso", + "uuid", + "vfs-core", + "vfs-mount", +] + +[[package]] +name = "vfs-core" +version = "0.6.4" +dependencies = [ + "aegis", + "async-trait", + "criterion", + "libc", + "lru", + "parking_lot", + "proptest", + "rand 0.8.6", + "serde", + "serde_json", + "smallvec", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tracing", + "turso", +] + +[[package]] +name = "vfs-fuse" +version = "0.6.4" +dependencies = [ + "anyhow", + "async-trait", + "libc", + "memchr", + "nix", + "page_size", + "parking_lot", + "smallvec", + "tempfile", + "tokio", + "tracing", + "turso", + "vfs-core", + "zerocopy", +] + +[[package]] +name = "vfs-mount" +version = "0.6.4" +dependencies = [ + "anyhow", + "async-trait", + "libc", + "tempfile", + "tokio", + "tokio-util", + "tracing", + "vfs-core", + "vfs-fuse", + "vfs-nfs", +] + +[[package]] +name = "vfs-nfs" +version = "0.6.4" +dependencies = [ + "anyhow", + "async-trait", + "byteorder", + "filetime", + "libc", + "num-derive", + "num-traits", + "smallvec", + "tempfile", + "tokio", + "tokio-util", + "tracing", + "uuid", + "vfs-core", +] + [[package]] name = "wait-timeout" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index b822a0b9..da6ec3da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [workspace] members = [ - "crates/agentfs-cli", - "crates/agentfs-core", - "crates/agentfs-fuse", - "crates/agentfs-nfs", - "crates/agentfs-mount", + "crates/vfs-cli", + "crates/vfs-core", + "crates/vfs-fuse", + "crates/vfs-nfs", + "crates/vfs-mount", ] resolver = "2" @@ -12,7 +12,7 @@ resolver = "2" version = "0.6.4" edition = "2021" license = "MIT" -repository = "https://github.com/tursodatabase/agentfs" +repository = "https://github.com/tursodatabase/vfs" # Concurrency canon (architecture.md section 7 item 7): no lock guard held # across an .await; enforced structurally, not by review. @@ -20,11 +20,11 @@ repository = "https://github.com/tursodatabase/agentfs" await_holding_lock = "deny" [workspace.dependencies] -agentfs-core = { path = "crates/agentfs-core" } -agentfs-cli = { path = "crates/agentfs-cli" } -agentfs-fuse = { path = "crates/agentfs-fuse" } -agentfs-mount = { path = "crates/agentfs-mount" } -agentfs-nfs = { path = "crates/agentfs-nfs" } +vfs-core = { path = "crates/vfs-core" } +vfs-cli = { path = "crates/vfs-cli" } +vfs-fuse = { path = "crates/vfs-fuse" } +vfs-mount = { path = "crates/vfs-mount" } +vfs-nfs = { path = "crates/vfs-nfs" } anyhow = "1.0" async-trait = "0.1" diff --git a/crates/agentfs-cli/scripts/mount.fuse.agentfs b/crates/agentfs-cli/scripts/mount.fuse.agentfs deleted file mode 100755 index d3df14df..00000000 --- a/crates/agentfs-cli/scripts/mount.fuse.agentfs +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Determine the path to agentfs -if [ -n "$AGENTFS_BIN" ]; then - AGENTFS_PATH="$AGENTFS_BIN" -else - AGENTFS_PATH="$(which agentfs 2>/dev/null)" - if [ -z "$AGENTFS_PATH" ]; then - echo "Error: agentfs binary not found. Please install agentfs or set AGENTFS_BIN." >&2 - exit 1 - fi -fi - -"$AGENTFS_PATH" mount "$1" "$2" diff --git a/crates/agentfs-core/.gitignore b/crates/agentfs-core/.gitignore deleted file mode 100644 index db7d859d..00000000 --- a/crates/agentfs-core/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -target - -# AgentFS local databases -.agentfs/ diff --git a/crates/agentfs-cli/.gitignore b/crates/vfs-cli/.gitignore similarity index 100% rename from crates/agentfs-cli/.gitignore rename to crates/vfs-cli/.gitignore diff --git a/crates/agentfs-cli/CONTRIBUTING.md b/crates/vfs-cli/CONTRIBUTING.md similarity index 63% rename from crates/agentfs-cli/CONTRIBUTING.md rename to crates/vfs-cli/CONTRIBUTING.md index d1b8a2cf..12c1f539 100644 --- a/crates/agentfs-cli/CONTRIBUTING.md +++ b/crates/vfs-cli/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# AgentFS Contributor Guide +# Vfs Contributor Guide ## Running Tests diff --git a/crates/agentfs-cli/Cargo.toml b/crates/vfs-cli/Cargo.toml similarity index 83% rename from crates/agentfs-cli/Cargo.toml rename to crates/vfs-cli/Cargo.toml index 7f909cdc..2368bc08 100644 --- a/crates/agentfs-cli/Cargo.toml +++ b/crates/vfs-cli/Cargo.toml @@ -1,17 +1,17 @@ [package] -name = "agentfs-cli" +name = "vfs-cli" version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -description = "Command-line interface for AgentFS" +description = "Command-line interface for Vfs" [lib] -name = "agentfs_cli" +name = "vfs_cli" path = "src/lib.rs" [[bin]] -name = "agentfs" +name = "vfs" path = "src/main.rs" [features] @@ -22,8 +22,8 @@ strict = [] workspace = true [dependencies] -agentfs-core = { workspace = true } -agentfs-mount = { workspace = true } +vfs-core = { workspace = true } +vfs-mount = { workspace = true } tokio = { workspace = true } clap = { workspace = true } anyhow = { workspace = true } diff --git a/crates/agentfs-cli/build.rs b/crates/vfs-cli/build.rs similarity index 92% rename from crates/agentfs-cli/build.rs rename to crates/vfs-cli/build.rs index 091b2c1c..69427f1d 100644 --- a/crates/agentfs-cli/build.rs +++ b/crates/vfs-cli/build.rs @@ -5,7 +5,7 @@ fn main() { let repo_root = Path::new(env!("CARGO_MANIFEST_DIR")) .parent() .and_then(Path::parent) - .expect("agentfs-cli should live under crates/agentfs-cli"); + .expect("vfs-cli should live under crates/vfs-cli"); // Capture git version from tags for --version flag // Rerun if git HEAD changes (new commits or tags) @@ -46,5 +46,5 @@ fn main() { .map(|v| v.trim().to_string()) .unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string()); - println!("cargo:rustc-env=AGENTFS_VERSION={}", version); + println!("cargo:rustc-env=VFS_VERSION={}", version); } diff --git a/crates/agentfs-cli/perf/simulate-npm-workload.sh b/crates/vfs-cli/perf/simulate-npm-workload.sh similarity index 99% rename from crates/agentfs-cli/perf/simulate-npm-workload.sh rename to crates/vfs-cli/perf/simulate-npm-workload.sh index 17211b9e..766f9c92 100755 --- a/crates/agentfs-cli/perf/simulate-npm-workload.sh +++ b/crates/vfs-cli/perf/simulate-npm-workload.sh @@ -1,7 +1,7 @@ #!/bin/bash # # Simulates a workload similar to `npx create-react-app` for testing -# AgentFS/FUSE performance. +# Vfs/FUSE performance. # # Based on observed FUSE operation distribution: # lookup: 18090 (~52%) - path resolution diff --git a/crates/agentfs-cli/perf/syscall/Makefile b/crates/vfs-cli/perf/syscall/Makefile similarity index 100% rename from crates/agentfs-cli/perf/syscall/Makefile rename to crates/vfs-cli/perf/syscall/Makefile diff --git a/crates/agentfs-cli/perf/syscall/hello.txt b/crates/vfs-cli/perf/syscall/hello.txt similarity index 100% rename from crates/agentfs-cli/perf/syscall/hello.txt rename to crates/vfs-cli/perf/syscall/hello.txt diff --git a/crates/agentfs-cli/perf/syscall/perf-open-close.c b/crates/vfs-cli/perf/syscall/perf-open-close.c similarity index 100% rename from crates/agentfs-cli/perf/syscall/perf-open-close.c rename to crates/vfs-cli/perf/syscall/perf-open-close.c diff --git a/crates/agentfs-cli/perf/syscall/perf-statx.c b/crates/vfs-cli/perf/syscall/perf-statx.c similarity index 100% rename from crates/agentfs-cli/perf/syscall/perf-statx.c rename to crates/vfs-cli/perf/syscall/perf-statx.c diff --git a/crates/agentfs-cli/perf/syscall/run.sh b/crates/vfs-cli/perf/syscall/run.sh similarity index 58% rename from crates/agentfs-cli/perf/syscall/run.sh rename to crates/vfs-cli/perf/syscall/run.sh index 5b208ca5..994baf1f 100755 --- a/crates/agentfs-cli/perf/syscall/run.sh +++ b/crates/vfs-cli/perf/syscall/run.sh @@ -2,8 +2,8 @@ # # Benchmark syscall performance across different scenarios: # 1. Native filesystem -# 2. AgentFS (file in base layer) -# 3. AgentFS (file copied up to delta layer) +# 2. Vfs (file in base layer) +# 3. Vfs (file copied up to delta layer) # set -e @@ -13,14 +13,14 @@ CLI_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" REPO_ROOT="$(cd "$CLI_DIR/../.." && pwd)" TEST_FILE="$SCRIPT_DIR/hello.txt" ITERATIONS="${1:-100000}" -AGENTFS="$REPO_ROOT/target/release/agentfs" +VFS="$REPO_ROOT/target/release/vfs" # Build benchmarks if needed make -C "$SCRIPT_DIR" -s -# Check agentfs binary -if [ ! -x "$AGENTFS" ]; then - echo "Error: agentfs binary not found at $AGENTFS" +# Check vfs binary +if [ ! -x "$VFS" ]; then + echo "Error: vfs binary not found at $VFS" echo "Run: cargo build --release" exit 1 fi @@ -51,24 +51,24 @@ run_benchmark() { NATIVE_LATENCY=$(echo "$NATIVE_OUTPUT" | extract_latency) NATIVE_THROUGHPUT=$(echo "$NATIVE_OUTPUT" | extract_throughput) - # Test 2: AgentFS (file in base layer) - echo "[2/3] AgentFS (base layer)..." - AGENTFS_BASE_OUTPUT=$("$AGENTFS" run "$benchmark" "$TEST_FILE" "$ITERATIONS" 2>&1) - AGENTFS_BASE_LATENCY=$(echo "$AGENTFS_BASE_OUTPUT" | extract_latency) - AGENTFS_BASE_THROUGHPUT=$(echo "$AGENTFS_BASE_OUTPUT" | extract_throughput) + # Test 2: Vfs (file in base layer) + echo "[2/3] Vfs (base layer)..." + VFS_BASE_OUTPUT=$("$VFS" run "$benchmark" "$TEST_FILE" "$ITERATIONS" 2>&1) + VFS_BASE_LATENCY=$(echo "$VFS_BASE_OUTPUT" | extract_latency) + VFS_BASE_THROUGHPUT=$(echo "$VFS_BASE_OUTPUT" | extract_throughput) - # Test 3: AgentFS (file copied up to delta) - echo "[3/3] AgentFS (delta layer)..." - AGENTFS_DELTA_OUTPUT=$("$AGENTFS" run sh -c " + # Test 3: Vfs (file copied up to delta) + echo "[3/3] Vfs (delta layer)..." + VFS_DELTA_OUTPUT=$("$VFS" run sh -c " touch '$TEST_FILE' '$benchmark' '$TEST_FILE' '$ITERATIONS' " 2>&1) - AGENTFS_DELTA_LATENCY=$(echo "$AGENTFS_DELTA_OUTPUT" | extract_latency) - AGENTFS_DELTA_THROUGHPUT=$(echo "$AGENTFS_DELTA_OUTPUT" | extract_throughput) + VFS_DELTA_LATENCY=$(echo "$VFS_DELTA_OUTPUT" | extract_latency) + VFS_DELTA_THROUGHPUT=$(echo "$VFS_DELTA_OUTPUT" | extract_throughput) # Calculate overhead percentages - AGENTFS_BASE_OVERHEAD=$(echo "scale=1; (($AGENTFS_BASE_LATENCY / $NATIVE_LATENCY) - 1) * 100" | bc) - AGENTFS_DELTA_OVERHEAD=$(echo "scale=1; (($AGENTFS_DELTA_LATENCY / $NATIVE_LATENCY) - 1) * 100" | bc) + VFS_BASE_OVERHEAD=$(echo "scale=1; (($VFS_BASE_LATENCY / $NATIVE_LATENCY) - 1) * 100" | bc) + VFS_DELTA_OVERHEAD=$(echo "scale=1; (($VFS_DELTA_LATENCY / $NATIVE_LATENCY) - 1) * 100" | bc) # Results echo "" @@ -77,8 +77,8 @@ run_benchmark() { printf "%-25s %10s %12s %10s\n" "Scenario" "Latency" "Throughput" "Overhead" printf "%-25s %10s %12s %10s\n" "--------" "-------" "----------" "--------" printf "%-25s %8s ns %10s/s %10s\n" "Native" "$NATIVE_LATENCY" "$NATIVE_THROUGHPUT" "-" - printf "%-25s %8s ns %10s/s %8s %%\n" "AgentFS (base)" "$AGENTFS_BASE_LATENCY" "$AGENTFS_BASE_THROUGHPUT" "$AGENTFS_BASE_OVERHEAD" - printf "%-25s %8s ns %10s/s %8s %%\n" "AgentFS (delta)" "$AGENTFS_DELTA_LATENCY" "$AGENTFS_DELTA_THROUGHPUT" "$AGENTFS_DELTA_OVERHEAD" + printf "%-25s %8s ns %10s/s %8s %%\n" "Vfs (base)" "$VFS_BASE_LATENCY" "$VFS_BASE_THROUGHPUT" "$VFS_BASE_OVERHEAD" + printf "%-25s %8s ns %10s/s %8s %%\n" "Vfs (delta)" "$VFS_DELTA_LATENCY" "$VFS_DELTA_THROUGHPUT" "$VFS_DELTA_OVERHEAD" echo "----------------------------------------------" echo "" } diff --git a/crates/agentfs-cli/scripts/fmt-imports b/crates/vfs-cli/scripts/fmt-imports similarity index 100% rename from crates/agentfs-cli/scripts/fmt-imports rename to crates/vfs-cli/scripts/fmt-imports diff --git a/crates/vfs-cli/scripts/mount.fuse.vfs b/crates/vfs-cli/scripts/mount.fuse.vfs new file mode 100755 index 00000000..4d7d7a67 --- /dev/null +++ b/crates/vfs-cli/scripts/mount.fuse.vfs @@ -0,0 +1,14 @@ +#!/bin/bash + +# Determine the path to vfs +if [ -n "$VFS_BIN" ]; then + VFS_PATH="$VFS_BIN" +else + VFS_PATH="$(which vfs 2>/dev/null)" + if [ -z "$VFS_PATH" ]; then + echo "Error: vfs binary not found. Please install vfs or set VFS_BIN." >&2 + exit 1 + fi +fi + +"$VFS_PATH" mount "$1" "$2" diff --git a/crates/agentfs-cli/src/cmd/clone.rs b/crates/vfs-cli/src/cmd/clone.rs similarity index 95% rename from crates/agentfs-cli/src/cmd/clone.rs rename to crates/vfs-cli/src/cmd/clone.rs index ce8a2933..e97d4aec 100644 --- a/crates/agentfs-cli/src/cmd/clone.rs +++ b/crates/vfs-cli/src/cmd/clone.rs @@ -1,4 +1,4 @@ -//! `agentfs clone`: populate an AgentFS database from a git repository +//! `vfs clone`: populate a Vfs database from a git repository //! without per-file FUSE round trips. //! //! A regular `git clone` through the mount pays ~9-11 FUSE round trips plus @@ -6,7 +6,7 @@ //! `git clone --no-checkout` through a temporary mount (pack files are a few //! large sequential writes), then streams the worktree content out of the //! object database: a producer thread parses `git ls-tree` + `git cat-file -//! --batch` output while an [`agentfs_core::ImportSession`] consumer bulk +//! --batch` output while an [`vfs_core::ImportSession`] consumer bulk //! imports each chunk (large multi-inode transactions), so blob decoding //! overlaps SQLite writes instead of buffering every blob in memory first. //! Finally it fabricates a git index whose cached stat data matches exactly @@ -25,17 +25,17 @@ use std::process::{Command, Stdio}; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; -use agentfs_core::{AgentFSOptions, FileSystem, ImportEntry, ImportOptions, ImportedEntry}; use anyhow::{bail, Context, Result}; use sha1::{Digest, Sha1}; +use vfs_core::{FileSystem, ImportEntry, ImportOptions, ImportedEntry, VfsOptions}; -use crate::cmd::init::open_agentfs; +use crate::cmd::init::open_vfs; use crate::opts::MountBackend; -use agentfs_mount::supervise::{ +use vfs_mount::supervise::{ run_supervised_task, set_parent_death_signal_std, supervise_command, ChildOutcome, SupervisedTaskOutcome, }; -use agentfs_mount::{mount_fs, MountOpts}; +use vfs_mount::{mount_fs, MountOpts}; const S_IFDIR: u32 = 0o040000; const MODE_FILE: u32 = 0o100644; @@ -65,22 +65,22 @@ pub async fn handle_clone_command( None => derive_repo_name(&source)?, }; - let options = AgentFSOptions::resolve(&id_or_path) - .unwrap_or_else(|_| AgentFSOptions::with_path(&id_or_path)); - let agentfs = open_agentfs(options) + let options = + VfsOptions::resolve(&id_or_path).unwrap_or_else(|_| VfsOptions::with_path(&id_or_path)); + let vfs = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; - let agent = agentfs.fs.clone(); - let fs: Arc = Arc::new(agentfs.fs); + let agent = vfs.fs.clone(); + let fs: Arc = Arc::new(vfs.fs); let clone_id = uuid::Uuid::new_v4().to_string(); - let mountpoint = std::env::temp_dir().join(format!("agentfs-clone-{clone_id}")); + let mountpoint = std::env::temp_dir().join(format!("vfs-clone-{clone_id}")); std::fs::create_dir_all(&mountpoint).context("failed to create mount directory")?; let mount_opts = MountOpts { mountpoint: mountpoint.clone(), backend: backend.into(), - fsname: format!("agentfs:{id_or_path}"), + fsname: format!("vfs:{id_or_path}"), uid: None, gid: None, allow_other: false, @@ -136,7 +136,7 @@ struct CloneSummary { } async fn clone_into_mount( - agent: &agentfs_core::fs::AgentFS, + agent: &vfs_core::fs::Vfs, mountpoint: &Path, source: &str, repo_name: &str, @@ -314,10 +314,10 @@ fn ls_tree(repo: &Path) -> Result> { let _objtype = fields.next().context("missing object type")?; let sha = fields.next().context("missing object id")?; if mode == MODE_GITLINK { - bail!("repository contains submodules ({path}); not supported by agentfs clone"); + bail!("repository contains submodules ({path}); not supported by vfs clone"); } if sha.len() != 40 { - bail!("non-SHA-1 repository detected; not supported by agentfs clone"); + bail!("non-SHA-1 repository detected; not supported by vfs clone"); } rows.push(TreeRow { mode, diff --git a/crates/agentfs-cli/src/cmd/completions.rs b/crates/vfs-cli/src/cmd/completions.rs similarity index 95% rename from crates/agentfs-cli/src/cmd/completions.rs rename to crates/vfs-cli/src/cmd/completions.rs index 559b6e44..7e506203 100644 --- a/crates/agentfs-cli/src/cmd/completions.rs +++ b/crates/vfs-cli/src/cmd/completions.rs @@ -62,11 +62,11 @@ impl Shell { /// Get the completion source line for this shell fn completion_line(&self) -> &'static str { match self { - Shell::Bash => "source <(COMPLETE=bash agentfs)", - Shell::Zsh => "source <(COMPLETE=zsh agentfs)", - Shell::Fish => "COMPLETE=fish agentfs | source", - Shell::Elvish => "eval (COMPLETE=elvish agentfs | slurp)", - Shell::PowerShell => "$env:COMPLETE = \"powershell\"; agentfs | Out-String | Invoke-Expression; Remove-Item Env:\\COMPLETE", + Shell::Bash => "source <(COMPLETE=bash vfs)", + Shell::Zsh => "source <(COMPLETE=zsh vfs)", + Shell::Fish => "COMPLETE=fish vfs | source", + Shell::Elvish => "eval (COMPLETE=elvish vfs | slurp)", + Shell::PowerShell => "$env:COMPLETE = \"powershell\"; vfs | Out-String | Invoke-Expression; Remove-Item Env:\\COMPLETE", } } } diff --git a/crates/agentfs-cli/src/cmd/exec.rs b/crates/vfs-cli/src/cmd/exec.rs similarity index 81% rename from crates/agentfs-cli/src/cmd/exec.rs rename to crates/vfs-cli/src/cmd/exec.rs index 5822b27a..4bb0d9d7 100644 --- a/crates/agentfs-cli/src/cmd/exec.rs +++ b/crates/vfs-cli/src/cmd/exec.rs @@ -1,23 +1,23 @@ -//! Execute a command with an AgentFS filesystem mounted. +//! Execute a command with a Vfs filesystem mounted. //! -//! This module provides the `agentfs exec` command which mounts an AgentFS +//! This module provides the `vfs exec` command which mounts a Vfs //! filesystem to a temporary directory, runs a command with that as the //! working directory, and automatically unmounts when done. -use agentfs_core::{AgentFSOptions, EncryptionConfig, FileSystem, HostFS, OverlayFS}; use anyhow::{Context, Result}; use std::path::PathBuf; use std::sync::Arc; use turso::value::Value; +use vfs_core::{EncryptionConfig, FileSystem, HostFS, OverlayFS, VfsOptions}; -use crate::cmd::init::open_agentfs; +use crate::cmd::init::open_vfs; use crate::opts::MountBackend; -use agentfs_mount::supervise::{exit_code_for_spawn_error, exit_code_for_status, run_supervised}; -use agentfs_mount::{mount_fs, MountOpts}; +use vfs_mount::supervise::{exit_code_for_spawn_error, exit_code_for_status, run_supervised}; +use vfs_mount::{mount_fs, MountOpts}; /// Handle the exec command. /// -/// Mounts the specified AgentFS, runs the command, and unmounts on completion. +/// Mounts the specified Vfs, runs the command, and unmounts on completion. pub async fn handle_exec_command( id_or_path: String, command: PathBuf, @@ -25,8 +25,8 @@ pub async fn handle_exec_command( backend: MountBackend, encryption: Option<(String, String)>, ) -> Result<()> { - // Resolve AgentFS options - let mut opts = AgentFSOptions::resolve(&id_or_path)?; + // Resolve Vfs options + let mut opts = VfsOptions::resolve(&id_or_path)?; if let Some((key, cipher)) = encryption { opts = opts.with_encryption(EncryptionConfig { hex_key: key, @@ -34,14 +34,14 @@ pub async fn handle_exec_command( }); } - // Open AgentFS - let agentfs = open_agentfs(opts) + // Open Vfs + let vfs = open_vfs(opts) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; // Check for overlay configuration let fs: Arc = { - let conn = agentfs.get_connection().await?; + let conn = vfs.get_connection().await?; // Check if fs_overlay_config table exists and has base_path let query = "SELECT value FROM fs_overlay_config WHERE key = 'base_path'"; @@ -65,21 +65,21 @@ pub async fn handle_exec_command( if let Some(base_path) = base_path { eprintln!("Using overlay filesystem with base: {}", base_path); let hostfs = HostFS::new(&base_path)?; - let overlay = OverlayFS::new(Arc::new(hostfs), agentfs.fs); + let overlay = OverlayFS::new(Arc::new(hostfs), vfs.fs); overlay.load().await?; // Load persisted whiteouts and origin mappings Arc::new(overlay) as Arc } else { - Arc::new(agentfs.fs) as Arc + Arc::new(vfs.fs) as Arc } }; // Create a temporary directory for the mount let exec_id = uuid::Uuid::new_v4().to_string(); - let mountpoint = std::env::temp_dir().join(format!("agentfs-exec-{}", exec_id)); + let mountpoint = std::env::temp_dir().join(format!("vfs-exec-{}", exec_id)); std::fs::create_dir_all(&mountpoint).context("Failed to create mount directory")?; let fsname = format!( - "agentfs:{}", + "vfs:{}", std::fs::canonicalize(&id_or_path) .map(|p| p.to_string_lossy().to_string()) .unwrap_or_else(|_| id_or_path.clone()) diff --git a/crates/agentfs-cli/src/cmd/fs.rs b/crates/vfs-cli/src/cmd/fs.rs similarity index 77% rename from crates/agentfs-cli/src/cmd/fs.rs rename to crates/vfs-cli/src/cmd/fs.rs index 1f915680..3c1af9ad 100644 --- a/crates/agentfs-cli/src/cmd/fs.rs +++ b/crates/vfs-cli/src/cmd/fs.rs @@ -1,10 +1,10 @@ use std::collections::VecDeque; -use agentfs_core::{AgentFSOptions, EncryptionConfig}; use anyhow::{Context, Result as AnyhowResult}; use turso::Value; +use vfs_core::{EncryptionConfig, VfsOptions}; -use crate::cmd::init::{finalize_readonly, open_agentfs}; +use crate::cmd::init::{finalize_readonly, open_vfs}; const ROOT_INO: i64 = 1; const S_IFMT: u32 = 0o170000; @@ -18,7 +18,7 @@ pub async fn ls_filesystem( path: &str, encryption: Option<&(String, String)>, ) -> AnyhowResult<()> { - let mut options = AgentFSOptions::resolve(&id_or_path)?; + let mut options = VfsOptions::resolve(&id_or_path)?; if let Some((key, cipher)) = encryption { options = options.with_encryption(EncryptionConfig { hex_key: key.clone(), @@ -27,20 +27,20 @@ pub async fn ls_filesystem( } eprintln!("Using agent: {}", id_or_path); - let agentfs = open_agentfs(options) + let vfs = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; - let result = ls_opened(stdout, &agentfs, path).await; - finalize_readonly(&agentfs).await; + let result = ls_opened(stdout, &vfs, path).await; + finalize_readonly(&vfs).await; result } async fn ls_opened( stdout: &mut impl std::io::Write, - agentfs: &agentfs_core::AgentFS, + vfs: &vfs_core::Vfs, path: &str, ) -> AnyhowResult<()> { - let conn = agentfs.get_connection().await?; + let conn = vfs.get_connection().await?; let start_ino = resolve_directory_ino(&conn, path).await?; @@ -154,19 +154,19 @@ pub async fn cat_filesystem( path: &str, encryption: Option<&(String, String)>, ) -> AnyhowResult<()> { - let mut options = AgentFSOptions::resolve(&id_or_path)?; + let mut options = VfsOptions::resolve(&id_or_path)?; if let Some((key, cipher)) = encryption { options = options.with_encryption(EncryptionConfig { hex_key: key.clone(), cipher: cipher.clone(), }); } - let agentfs = open_agentfs(options) + let vfs = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; let result: AnyhowResult<()> = async { - match agentfs.fs.read_file(path).await? { + match vfs.fs.read_file(path).await? { Some(file) => { stdout.write_all(&file)?; Ok(()) @@ -175,7 +175,7 @@ pub async fn cat_filesystem( } } .await; - finalize_readonly(&agentfs).await; + finalize_readonly(&vfs).await; result } @@ -185,19 +185,19 @@ pub async fn write_filesystem( content: &str, encryption: Option<&(String, String)>, ) -> AnyhowResult<()> { - let mut options = AgentFSOptions::resolve(&id_or_path)?; + let mut options = VfsOptions::resolve(&id_or_path)?; if let Some((key, cipher)) = encryption { options = options.with_encryption(EncryptionConfig { hex_key: key.clone(), cipher: cipher.clone(), }); } - let agentfs = open_agentfs(options) + let vfs = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; // Own created entries as the invoking user, matching mount-created files: - // uid/gid 0 would make a later chmod inside `agentfs exec` (running as the + // uid/gid 0 would make a later chmod inside `vfs exec` (running as the // invoking uid) fail with EPERM. // SAFETY: getuid/getgid are always safe let (uid, gid) = unsafe { (libc::getuid(), libc::getgid()) }; @@ -209,26 +209,23 @@ pub async fn write_filesystem( // we must start with /a (first TWO entries) for i in 2..components.len() { let dir_path = components[0..i].join("/"); - if agentfs.fs.stat(&dir_path).await?.is_none() { - agentfs.fs.mkdir(&dir_path, uid, gid).await?; + if vfs.fs.stat(&dir_path).await?.is_none() { + vfs.fs.mkdir(&dir_path, uid, gid).await?; } } // Remove file if it exists (overwrite behavior) - if agentfs.fs.stat(path).await?.is_some() { - agentfs.fs.remove(path).await?; + if vfs.fs.stat(path).await?.is_some() { + vfs.fs.remove(path).await?; } - let (_, file) = agentfs - .fs - .create_file(path, S_IFREG | 0o644, uid, gid) - .await?; + let (_, file) = vfs.fs.create_file(path, S_IFREG | 0o644, uid, gid).await?; file.pwrite(0, content.as_bytes()).await?; // Tier Four: writes go into the in-memory batcher first. This CLI is a // one-shot operation — finalize so the bytes are durable in SQLite before - // we drop the AgentFS (otherwise a subsequent process or `cat` against + // we drop the Vfs (otherwise a subsequent process or `cat` against // the same path would see only the pre-write state) and the database // family is single-file again (invariant I1) instead of keeping the // truncated 0-byte -wal a bare drain leaves behind. - agentfs.fs.finalize().await?; + vfs.fs.finalize().await?; Ok(()) } @@ -267,10 +264,10 @@ fn path_exists_in_base(base_path: &str, rel_path: &str) -> bool { } pub async fn diff_filesystem(id_or_path: String) -> AnyhowResult<()> { - let options = AgentFSOptions::resolve(&id_or_path)?; + let options = VfsOptions::resolve(&id_or_path)?; eprintln!("Using agent: {}", id_or_path); - let agent = open_agentfs(options) + let agent = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; let result = diff_opened(&agent).await; @@ -278,7 +275,7 @@ pub async fn diff_filesystem(id_or_path: String) -> AnyhowResult<()> { result } -async fn diff_opened(agent: &agentfs_core::AgentFS) -> AnyhowResult<()> { +async fn diff_opened(agent: &vfs_core::Vfs) -> AnyhowResult<()> { // Check if overlay is enabled let base_path = match agent.is_overlay_enabled().await? { Some(path) => path, @@ -348,27 +345,27 @@ async fn diff_opened(agent: &agentfs_core::AgentFS) -> AnyhowResult<()> { #[cfg(test)] mod tests { - use agentfs_core::{AgentFS, AgentFSOptions, EncryptionConfig}; use tempfile::NamedTempFile; + use vfs_core::{EncryptionConfig, Vfs, VfsOptions}; use crate::cmd::fs::{cat_filesystem, ls_filesystem, write_filesystem}; const TEST_KEY: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; const TEST_CIPHER: &str = "aes256gcm"; - async fn agentfs() -> (AgentFS, String, NamedTempFile) { + async fn vfs() -> (Vfs, String, NamedTempFile) { let file = NamedTempFile::new().unwrap(); let path = file.path().to_str().unwrap(); - let agentfs = AgentFS::open(AgentFSOptions::with_path(path.to_string())) + let vfs = Vfs::open(VfsOptions::with_path(path.to_string())) .await .unwrap(); - (agentfs, file.path().to_str().unwrap().to_string(), file) + (vfs, file.path().to_str().unwrap().to_string(), file) } - async fn encrypted_agentfs() -> (AgentFS, String, NamedTempFile) { + async fn encrypted_vfs() -> (Vfs, String, NamedTempFile) { let file = NamedTempFile::new().unwrap(); let path = file.path().to_str().unwrap(); - let agentfs = AgentFS::open(AgentFSOptions::with_path(path.to_string()).with_encryption( + let vfs = Vfs::open(VfsOptions::with_path(path.to_string()).with_encryption( EncryptionConfig { hex_key: TEST_KEY.to_string(), cipher: TEST_CIPHER.to_string(), @@ -376,14 +373,14 @@ mod tests { )) .await .unwrap(); - (agentfs, file.path().to_str().unwrap().to_string(), file) + (vfs, file.path().to_str().unwrap().to_string(), file) } const S_IFREG: u32 = 0o100000; #[tokio::test] async fn cat_file_not_found() { - let (_agentfs, path, _file) = agentfs().await; + let (_vfs, path, _file) = vfs().await; let mut buf = Vec::new(); let err = cat_filesystem(&mut buf, path, "test.md", None) .await @@ -393,11 +390,9 @@ mod tests { #[tokio::test] async fn cat_file_found() { - let (agentfs, path, _file) = agentfs().await; - let content = b"hello, agentfs"; - write_file(&agentfs.fs, "test.md", content, 0, 0) - .await - .unwrap(); + let (vfs, path, _file) = vfs().await; + let content = b"hello, vfs"; + write_file(&vfs.fs, "test.md", content, 0, 0).await.unwrap(); let mut buf = Vec::new(); cat_filesystem(&mut buf, path, "test.md", None) .await @@ -407,9 +402,9 @@ mod tests { #[tokio::test] async fn cat_big_file_found() { - let (agentfs, path, _file) = agentfs().await; + let (vfs, path, _file) = vfs().await; let content = vec![100u8; 4 * 1024 * 1024]; - write_file(&agentfs.fs, "test.md", &content, 0, 0) + write_file(&vfs.fs, "test.md", &content, 0, 0) .await .unwrap(); let mut buf = Vec::new(); @@ -421,7 +416,7 @@ mod tests { #[tokio::test] async fn ls_empty() { - let (_agentfs, path, _file) = agentfs().await; + let (_vfs, path, _file) = vfs().await; let mut buf = Vec::new(); ls_filesystem(&mut buf, path, "/", None).await.unwrap(); assert_eq!(buf, b""); @@ -429,11 +424,11 @@ mod tests { #[tokio::test] async fn ls_files_only() { - let (agentfs, path, _file) = agentfs().await; - write_file(&agentfs.fs, "1.md", b"1", 0, 0).await.unwrap(); - write_file(&agentfs.fs, "2.md", b"11", 0, 0).await.unwrap(); + let (vfs, path, _file) = vfs().await; + write_file(&vfs.fs, "1.md", b"1", 0, 0).await.unwrap(); + write_file(&vfs.fs, "2.md", b"11", 0, 0).await.unwrap(); let big = vec![100u8; 1024 * 1024]; - write_file(&agentfs.fs, "3.md", &big, 0, 0).await.unwrap(); + write_file(&vfs.fs, "3.md", &big, 0, 0).await.unwrap(); let mut buf = Vec::new(); ls_filesystem(&mut buf, path, "/", None).await.unwrap(); assert_eq!( @@ -447,22 +442,16 @@ f 3.md #[tokio::test] async fn ls_dirs() { - let (agentfs, path, _file) = agentfs().await; - agentfs.fs.mkdir("a", 0, 0).await.unwrap(); - agentfs.fs.mkdir("a/b", 0, 0).await.unwrap(); - agentfs.fs.mkdir("a/c", 0, 0).await.unwrap(); - agentfs.fs.mkdir("d", 0, 0).await.unwrap(); - agentfs.fs.mkdir("d/e", 0, 0).await.unwrap(); - write_file(&agentfs.fs, "a/b/1.md", b"1", 0, 0) - .await - .unwrap(); - write_file(&agentfs.fs, "a/c/2.md", b"11", 0, 0) - .await - .unwrap(); + let (vfs, path, _file) = vfs().await; + vfs.fs.mkdir("a", 0, 0).await.unwrap(); + vfs.fs.mkdir("a/b", 0, 0).await.unwrap(); + vfs.fs.mkdir("a/c", 0, 0).await.unwrap(); + vfs.fs.mkdir("d", 0, 0).await.unwrap(); + vfs.fs.mkdir("d/e", 0, 0).await.unwrap(); + write_file(&vfs.fs, "a/b/1.md", b"1", 0, 0).await.unwrap(); + write_file(&vfs.fs, "a/c/2.md", b"11", 0, 0).await.unwrap(); let big = vec![100u8; 1024 * 1024]; - write_file(&agentfs.fs, "d/e/3.md", &big, 0, 0) - .await - .unwrap(); + write_file(&vfs.fs, "d/e/3.md", &big, 0, 0).await.unwrap(); let mut buf = Vec::new(); ls_filesystem(&mut buf, path, "/", None).await.unwrap(); assert_eq!( @@ -481,20 +470,14 @@ f d/e/3.md #[tokio::test] async fn ls_subdir_lists_only_subtree() { - let (agentfs, path, _file) = agentfs().await; - agentfs.fs.mkdir("a", 0, 0).await.unwrap(); - agentfs.fs.mkdir("a/b", 0, 0).await.unwrap(); - agentfs.fs.mkdir("a/c", 0, 0).await.unwrap(); - agentfs.fs.mkdir("d", 0, 0).await.unwrap(); - write_file(&agentfs.fs, "a/b/1.md", b"1", 0, 0) - .await - .unwrap(); - write_file(&agentfs.fs, "a/c/2.md", b"11", 0, 0) - .await - .unwrap(); - write_file(&agentfs.fs, "d/3.md", b"111", 0, 0) - .await - .unwrap(); + let (vfs, path, _file) = vfs().await; + vfs.fs.mkdir("a", 0, 0).await.unwrap(); + vfs.fs.mkdir("a/b", 0, 0).await.unwrap(); + vfs.fs.mkdir("a/c", 0, 0).await.unwrap(); + vfs.fs.mkdir("d", 0, 0).await.unwrap(); + write_file(&vfs.fs, "a/b/1.md", b"1", 0, 0).await.unwrap(); + write_file(&vfs.fs, "a/c/2.md", b"11", 0, 0).await.unwrap(); + write_file(&vfs.fs, "d/3.md", b"111", 0, 0).await.unwrap(); let mut buf = Vec::new(); ls_filesystem(&mut buf, path.clone(), "/a", None) .await @@ -518,7 +501,7 @@ f c/2.md #[tokio::test] async fn ls_missing_path_errors() { - let (_agentfs, path, _file) = agentfs().await; + let (_vfs, path, _file) = vfs().await; let mut buf = Vec::new(); let err = ls_filesystem(&mut buf, path, "/missing", None) .await @@ -531,10 +514,8 @@ f c/2.md #[tokio::test] async fn ls_file_path_errors() { - let (agentfs, path, _file) = agentfs().await; - write_file(&agentfs.fs, "file.md", b"1", 0, 0) - .await - .unwrap(); + let (vfs, path, _file) = vfs().await; + write_file(&vfs.fs, "file.md", b"1", 0, 0).await.unwrap(); let mut buf = Vec::new(); let err = ls_filesystem(&mut buf, path, "/file.md", None) .await @@ -547,11 +528,9 @@ f c/2.md #[tokio::test] async fn read_commands_leave_no_wal_sidecar() { - let (agentfs, path, _file) = agentfs().await; - write_file(&agentfs.fs, "test.md", b"1", 0, 0) - .await - .unwrap(); - drop(agentfs); + let (vfs, path, _file) = vfs().await; + write_file(&vfs.fs, "test.md", b"1", 0, 0).await.unwrap(); + drop(vfs); let wal = format!("{path}-wal"); let mut buf = Vec::new(); @@ -586,12 +565,12 @@ f c/2.md #[tokio::test] async fn encrypted_write_and_cat() { - let (agentfs, path, _file) = encrypted_agentfs().await; + let (vfs, path, _file) = encrypted_vfs().await; let content = b"encrypted content"; - write_file(&agentfs.fs, "secret.txt", content, 0, 0) + write_file(&vfs.fs, "secret.txt", content, 0, 0) .await .unwrap(); - drop(agentfs); + drop(vfs); let encryption = Some((TEST_KEY.to_string(), TEST_CIPHER.to_string())); let mut buf = Vec::new(); @@ -603,14 +582,10 @@ f c/2.md #[tokio::test] async fn encrypted_ls() { - let (agentfs, path, _file) = encrypted_agentfs().await; - write_file(&agentfs.fs, "file1.txt", b"1", 0, 0) - .await - .unwrap(); - write_file(&agentfs.fs, "file2.txt", b"2", 0, 0) - .await - .unwrap(); - drop(agentfs); + let (vfs, path, _file) = encrypted_vfs().await; + write_file(&vfs.fs, "file1.txt", b"1", 0, 0).await.unwrap(); + write_file(&vfs.fs, "file2.txt", b"2", 0, 0).await.unwrap(); + drop(vfs); let encryption = Some((TEST_KEY.to_string(), TEST_CIPHER.to_string())); let mut buf = Vec::new(); @@ -622,18 +597,16 @@ f c/2.md #[tokio::test] async fn write_filesystem_owns_entries_as_invoking_user() { - let (_agentfs, path, _file) = agentfs().await; + let (_vfs, path, _file) = vfs().await; write_filesystem(path.clone(), "/subdir/owned.txt", "content", None) .await .unwrap(); - // uid/gid 0 entries make a later chmod inside `agentfs exec` (running + // uid/gid 0 entries make a later chmod inside `vfs exec` (running // as the invoking uid) fail EPERM, unlike mount-created files. // SAFETY: getuid/getgid are always safe let (uid, gid) = unsafe { (libc::getuid(), libc::getgid()) }; - let reader = AgentFS::open(AgentFSOptions::with_path(path)) - .await - .unwrap(); + let reader = Vfs::open(VfsOptions::with_path(path)).await.unwrap(); let file_stats = reader.fs.stat("/subdir/owned.txt").await.unwrap().unwrap(); assert_eq!( (file_stats.uid, file_stats.gid), @@ -650,7 +623,7 @@ f c/2.md #[tokio::test] async fn encrypted_write_filesystem() { - let (_agentfs, path, _file) = encrypted_agentfs().await; + let (_vfs, path, _file) = encrypted_vfs().await; let encryption = Some((TEST_KEY.to_string(), TEST_CIPHER.to_string())); write_filesystem( @@ -670,7 +643,7 @@ f c/2.md } async fn write_file( - fs: &agentfs_core::fs::AgentFS, + fs: &vfs_core::fs::Vfs, path: &str, data: &[u8], uid: u32, @@ -681,7 +654,7 @@ f c/2.md } let (_, file) = fs.create_file(path, S_IFREG | 0o644, uid, gid).await?; file.pwrite(0, data).await?; - // Tier Four: cat_filesystem opens a fresh AgentFS at the same path. + // Tier Four: cat_filesystem opens a fresh Vfs at the same path. // That second instance only sees what's durable in SQLite, so the // writer must flush its batcher before another opener can read. fs.drain_all().await?; diff --git a/crates/agentfs-cli/src/cmd/init.rs b/crates/vfs-cli/src/cmd/init.rs similarity index 88% rename from crates/agentfs-cli/src/cmd/init.rs rename to crates/vfs-cli/src/cmd/init.rs index ddd2384d..d4aa8dcb 100644 --- a/crates/agentfs-cli/src/cmd/init.rs +++ b/crates/vfs-cli/src/cmd/init.rs @@ -1,11 +1,11 @@ use std::path::PathBuf; use std::time::{SystemTime, UNIX_EPOCH}; -use agentfs_core::{ - agentfs_dir, AgentFS, AgentFSOptions, EncryptionConfig, OverlayFS, PartialBootstrapStrategy, - PartialSyncOpts, SyncOptions, -}; use anyhow::{Context, Result as AnyhowResult}; +use vfs_core::{ + vfs_dir, EncryptionConfig, OverlayFS, PartialBootstrapStrategy, PartialSyncOpts, SyncOptions, + Vfs, VfsOptions, +}; use crate::opts::{MountBackend, SyncCommandOptions}; @@ -16,7 +16,7 @@ pub struct EncryptionOptions { pub cipher: String, } -pub(crate) async fn open_agentfs(options: AgentFSOptions) -> Result { +pub(crate) async fn open_vfs(options: VfsOptions) -> Result { let mut options = options; if options.core_config.is_none() { options = options.with_core_config(crate::config::core_config_from_env()); @@ -25,7 +25,7 @@ pub(crate) async fn open_agentfs(options: AgentFSOptions) -> Result Result, - agent: AgentFS, + agent: Vfs, ) -> AnyhowResult<()> { - use agentfs_core::{FileSystem, HostFS}; - use agentfs_mount::supervise::{exit_code_for_status, run_supervised}; - use agentfs_mount::{mount_fs, MountOpts}; use std::sync::Arc; + use vfs_core::{FileSystem, HostFS}; + use vfs_mount::supervise::{exit_code_for_status, run_supervised}; + use vfs_mount::{mount_fs, MountOpts}; let fs: Arc = if let Some(ref base_path) = base { let canonical = base_path @@ -241,13 +240,13 @@ async fn run_init_cmd( }; let exec_id = uuid::Uuid::new_v4().to_string(); - let mountpoint = std::env::temp_dir().join(format!("agentfs-init-{}", exec_id)); + let mountpoint = std::env::temp_dir().join(format!("vfs-init-{}", exec_id)); std::fs::create_dir_all(&mountpoint).context("Failed to create mount directory")?; let mount_opts = MountOpts { mountpoint: mountpoint.clone(), backend: backend.into(), - fsname: format!("agentfs:{}", id), + fsname: format!("vfs:{}", id), uid: None, gid: None, allow_other: false, @@ -285,7 +284,7 @@ async fn run_init_cmd( _cmd_str: String, _backend: MountBackend, _base: Option, - _agent: AgentFS, + _agent: Vfs, ) -> AnyhowResult<()> { anyhow::bail!("The -c option is not supported on Windows") } diff --git a/crates/agentfs-cli/src/cmd/mcp_server.rs b/crates/vfs-cli/src/cmd/mcp_server.rs similarity index 85% rename from crates/agentfs-cli/src/cmd/mcp_server.rs rename to crates/vfs-cli/src/cmd/mcp_server.rs index 7f6b60f8..f59086fb 100644 --- a/crates/agentfs-cli/src/cmd/mcp_server.rs +++ b/crates/vfs-cli/src/cmd/mcp_server.rs @@ -1,5 +1,3 @@ -use agentfs_core::fs::{FileSystem, DEFAULT_FILE_MODE}; -use agentfs_core::{AgentFS, AgentFSOptions, Stats}; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value as JsonValue}; @@ -8,8 +6,10 @@ use std::io::{self, BufRead, Write}; use std::path::Path; use std::sync::Arc; use tokio::sync::Mutex; +use vfs_core::fs::{FileSystem, DEFAULT_FILE_MODE}; +use vfs_core::{Stats, Vfs, VfsOptions}; -use crate::cmd::init::{finalize_readonly, open_agentfs}; +use crate::cmd::init::{finalize_readonly, open_vfs}; /// The complete dispatchable tool surface. `tools/list`, `tools/call` /// dispatch, and `--tools` filter validation all key off this list; the @@ -79,8 +79,8 @@ pub async fn handle_mcp_server_command( id_or_path: String, tools_filter: Option>, ) -> Result<()> { - // Resolve and open AgentFS - let options = AgentFSOptions::resolve(&id_or_path).context(format!( + // Resolve and open Vfs + let options = VfsOptions::resolve(&id_or_path).context(format!( "Failed to resolve agent ID or path: {}", id_or_path ))?; @@ -98,17 +98,17 @@ pub async fn handle_mcp_server_command( Ok(()) } -/// Where request handlers get their AgentFS from. -enum AgentFsSource { +/// Where request handlers get their Vfs from. +enum VfsSource { /// Ephemeral (in-memory) databases live exactly as long as this handle. - Held(Arc), + Held(Arc), /// File-backed databases are reopened per request. The database file /// lock is exclusive (one process at a time), so holding it across the /// whole stdio session would block every other CLI command (including - /// `agentfs timeline` reading the tool audit) for as long as a client + /// `vfs timeline` reading the tool audit) for as long as a client /// keeps the server open. Reopening per request releases the lock while /// the server is idle. - PerRequest(Box), + PerRequest(Box), } /// MCP Server implementation @@ -117,14 +117,14 @@ struct McpServer { /// guidance on the per-request reopens (the database can be replaced /// underneath a long-lived stdio session). id_or_path: String, - source: AgentFsSource, + source: VfsSource, enabled_tools: Option>, } impl McpServer { async fn new( id_or_path: String, - options: AgentFSOptions, + options: VfsOptions, tools_filter: Option>, ) -> Result { let enabled_tools = match tools_filter { @@ -160,21 +160,21 @@ impl McpServer { }; let source = if options.db_path()? == ":memory:" { - let agentfs = open_agentfs(options) + let vfs = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; - AgentFsSource::Held(Arc::new(agentfs)) + VfsSource::Held(Arc::new(vfs)) } else { // Open once up front so startup fails cleanly on a missing or // incompatible database, then release the file lock with the // single-file family restored: even this never-writing probe // materializes a -wal sidecar (invariant I1). - let probe = open_agentfs(options.clone()) + let probe = open_vfs(options.clone()) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; finalize_readonly(&probe).await; drop(probe); - AgentFsSource::PerRequest(Box::new(options)) + VfsSource::PerRequest(Box::new(options)) }; Ok(Self { @@ -184,14 +184,14 @@ impl McpServer { }) } - async fn agentfs(&self) -> Result> { + async fn vfs(&self) -> Result> { match &self.source { - AgentFsSource::Held(agentfs) => Ok(agentfs.clone()), - AgentFsSource::PerRequest(options) => { - let agentfs = open_agentfs((**options).clone()).await.map_err(|err| { + VfsSource::Held(vfs) => Ok(vfs.clone()), + VfsSource::PerRequest(options) => { + let vfs = open_vfs((**options).clone()).await.map_err(|err| { super::migrate::open_error_with_guidance(err, &self.id_or_path) })?; - Ok(Arc::new(agentfs)) + Ok(Arc::new(vfs)) } } } @@ -210,7 +210,7 @@ impl McpServer { // Per-request opens leave a -wal next to a file-backed database // while serving; restore the single-file family at exit even when // the stdio loop errored, mirroring the NFS server's - // finalize-on-shutdown (agentfs-nfs server/tcp.rs). + // finalize-on-shutdown (vfs-nfs server/tcp.rs). server.lock().await.finalize_on_shutdown().await; result } @@ -220,14 +220,14 @@ impl McpServer { /// holder of the database must not turn a clean shutdown into an error. async fn finalize_on_shutdown(&self) { // Ephemeral databases have no on-disk family to restore. - let AgentFsSource::PerRequest(options) = &self.source else { + let VfsSource::PerRequest(options) = &self.source else { return; }; - match open_agentfs((**options).clone()) + match open_vfs((**options).clone()) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &self.id_or_path)) { - Ok(agentfs) => finalize_readonly(&agentfs).await, + Ok(vfs) => finalize_readonly(&vfs).await, Err(error) => eprintln!( "Warning: Failed to reopen the database to restore the single-file family: {error:#}" ), @@ -329,7 +329,7 @@ impl McpServer { "resources": {} }, "serverInfo": { - "name": "agentfs-mcp-server", + "name": "vfs-mcp-server", "version": env!("CARGO_PKG_VERSION") } })) @@ -348,14 +348,14 @@ impl McpServer { let arguments = params.get("arguments").cloned().unwrap_or(json!({})); - let agentfs = self - .agentfs() + let vfs = self + .vfs() .await .map_err(|err| RpcError::internal(format!("Failed to open database: {err:#}")))?; // Every tools/call attempt is audited, including rejected ones, so - // `agentfs timeline` shows the full MCP activity for the database. - let audit_id = agentfs + // `vfs timeline` shows the full MCP activity for the database. + let audit_id = vfs .tools .start(name, Some(arguments.clone())) .await @@ -363,16 +363,15 @@ impl McpServer { RpcError::internal(format!("Failed to record tool call audit row: {err}")) })?; - let outcome = self.dispatch_tool(&agentfs, name, arguments).await; + let outcome = self.dispatch_tool(&vfs, name, arguments).await; match &outcome { Ok(text) => { - agentfs - .tools + vfs.tools .success(audit_id, Some(JsonValue::String(text.clone()))) .await } - Err(err) => agentfs.tools.error(audit_id, &err.message).await, + Err(err) => vfs.tools.error(audit_id, &err.message).await, } .map_err(|err| { RpcError::internal(format!("Failed to record tool call audit row: {err}")) @@ -392,7 +391,7 @@ impl McpServer { async fn dispatch_tool( &self, - agentfs: &AgentFS, + vfs: &Vfs, name: &str, arguments: JsonValue, ) -> Result { @@ -406,18 +405,18 @@ impl McpServer { } let result = match name { - "read_file" => handle_read_file(agentfs, parse_tool_params(arguments)?).await, - "write_file" => handle_write_file(agentfs, parse_tool_params(arguments)?).await, - "readdir" => handle_readdir(agentfs, parse_tool_params(arguments)?).await, - "mkdir" => handle_mkdir(agentfs, parse_tool_params(arguments)?).await, - "rename" => handle_rename(agentfs, parse_tool_params(arguments)?).await, - "remove" => handle_remove(agentfs, parse_tool_params(arguments)?).await, - "stat" => handle_stat(agentfs, parse_tool_params(arguments)?).await, - "access" => handle_access(agentfs, parse_tool_params(arguments)?).await, - "kv_get" => handle_kv_get(agentfs, parse_tool_params(arguments)?).await, - "kv_set" => handle_kv_set(agentfs, parse_tool_params(arguments)?).await, - "kv_delete" => handle_kv_delete(agentfs, parse_tool_params(arguments)?).await, - "kv_list" => handle_kv_list(agentfs, parse_tool_params(arguments)?).await, + "read_file" => handle_read_file(vfs, parse_tool_params(arguments)?).await, + "write_file" => handle_write_file(vfs, parse_tool_params(arguments)?).await, + "readdir" => handle_readdir(vfs, parse_tool_params(arguments)?).await, + "mkdir" => handle_mkdir(vfs, parse_tool_params(arguments)?).await, + "rename" => handle_rename(vfs, parse_tool_params(arguments)?).await, + "remove" => handle_remove(vfs, parse_tool_params(arguments)?).await, + "stat" => handle_stat(vfs, parse_tool_params(arguments)?).await, + "access" => handle_access(vfs, parse_tool_params(arguments)?).await, + "kv_get" => handle_kv_get(vfs, parse_tool_params(arguments)?).await, + "kv_set" => handle_kv_set(vfs, parse_tool_params(arguments)?).await, + "kv_delete" => handle_kv_delete(vfs, parse_tool_params(arguments)?).await, + "kv_list" => handle_kv_list(vfs, parse_tool_params(arguments)?).await, _ => Err(anyhow::anyhow!( "Tool {name} is advertised but has no dispatch arm; \ ALL_TOOLS and dispatch_tool are out of sync" @@ -428,8 +427,8 @@ impl McpServer { } async fn handle_resources_list(&self) -> Result { - let agentfs = self.agentfs().await?; - let resources = list_resources(&agentfs).await?; + let vfs = self.vfs().await?; + let resources = list_resources(&vfs).await?; Ok(json!({ "resources": resources })) } @@ -439,8 +438,8 @@ impl McpServer { .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing resource uri"))?; - let agentfs = self.agentfs().await?; - let contents = read_resource(&agentfs, uri).await?; + let vfs = self.vfs().await?; + let contents = read_resource(&vfs, uri).await?; let mime_type = guess_mime_type(uri); // Try to decode as UTF-8, fall back to base64 @@ -839,10 +838,10 @@ struct KvListParams { // ============================================================================ /// Read file contents -async fn handle_read_file(agentfs: &AgentFS, params: ReadFileParams) -> Result { +async fn handle_read_file(vfs: &Vfs, params: ReadFileParams) -> Result { let path = normalize_path(¶ms.path)?; - let data = agentfs + let data = vfs .fs .read_file(&path) .await @@ -862,7 +861,7 @@ async fn handle_read_file(agentfs: &AgentFS, params: ReadFileParams) -> Result Result { +async fn handle_write_file(vfs: &Vfs, params: WriteFileParams) -> Result { let path = normalize_path(¶ms.path)?; let data = match params.encoding.as_deref() { @@ -872,12 +871,12 @@ async fn handle_write_file(agentfs: &AgentFS, params: WriteFileParams) -> Result // Create parent directories if requested if params.create_dirs.unwrap_or(false) { - ensure_parent_dirs(agentfs, &path).await?; + ensure_parent_dirs(vfs, &path).await?; } - let file = match agentfs.fs.stat(&path).await? { + let file = match vfs.fs.stat(&path).await? { Some(stats) if stats.is_file() => { - let file = FileSystem::open(&agentfs.fs, stats.ino, libc::O_WRONLY) + let file = FileSystem::open(&vfs.fs, stats.ino, libc::O_WRONLY) .await .context("Failed to open file")?; file.truncate(0).await.context("Failed to truncate file")?; @@ -885,7 +884,7 @@ async fn handle_write_file(agentfs: &AgentFS, params: WriteFileParams) -> Result } Some(_) => anyhow::bail!("Not a regular file: {}", path), None => { - let (_, file) = agentfs + let (_, file) = vfs .fs .create_file(&path, DEFAULT_FILE_MODE, 0, 0) .await @@ -905,7 +904,7 @@ async fn handle_write_file(agentfs: &AgentFS, params: WriteFileParams) -> Result /// Helper to create parent directories recursively fn ensure_parent_dirs<'a>( - agentfs: &'a AgentFS, + vfs: &'a Vfs, path: &'a str, ) -> std::pin::Pin> + Send + 'a>> { Box::pin(async move { @@ -919,16 +918,15 @@ fn ensure_parent_dirs<'a>( let parent_path = normalize_path(&parent_str)?; // Check if parent exists - if agentfs.fs.stat(&parent_path).await?.is_some() { + if vfs.fs.stat(&parent_path).await?.is_some() { return Ok(()); } // Recursively ensure grandparent exists - ensure_parent_dirs(agentfs, &parent_path).await?; + ensure_parent_dirs(vfs, &parent_path).await?; // Create parent - agentfs - .fs + vfs.fs .mkdir(&parent_path, 0, 0) .await .context(format!("Failed to create directory: {}", parent_path))?; @@ -938,17 +936,17 @@ fn ensure_parent_dirs<'a>( } /// List directory contents -async fn handle_readdir(agentfs: &AgentFS, params: ReaddirParams) -> Result { +async fn handle_readdir(vfs: &Vfs, params: ReaddirParams) -> Result { let path = normalize_path(¶ms.path)?; - let stats = agentfs + let stats = vfs .fs .stat(&path) .await .context("Failed to stat directory")? .ok_or_else(|| anyhow::anyhow!("Directory not found: {}", path))?; - let entries = agentfs + let entries = vfs .fs .readdir(stats.ino) .await @@ -959,11 +957,10 @@ async fn handle_readdir(agentfs: &AgentFS, params: ReaddirParams) -> Result Result { +async fn handle_mkdir(vfs: &Vfs, params: MkdirParams) -> Result { let path = normalize_path(¶ms.path)?; - agentfs - .fs + vfs.fs .mkdir(&path, 0, 0) .await .context("Failed to create directory")?; @@ -972,11 +969,10 @@ async fn handle_mkdir(agentfs: &AgentFS, params: MkdirParams) -> Result } /// Remove empty directory -async fn handle_remove(agentfs: &AgentFS, params: RemoveParams) -> Result { +async fn handle_remove(vfs: &Vfs, params: RemoveParams) -> Result { let path = normalize_path(¶ms.path)?; - agentfs - .fs + vfs.fs .remove(&path) .await .context("Failed to remove directory")?; @@ -985,13 +981,13 @@ async fn handle_remove(agentfs: &AgentFS, params: RemoveParams) -> Result Result { +async fn handle_rename(vfs: &Vfs, params: RenameParams) -> Result { let from = normalize_path(¶ms.from)?; let to = normalize_path(¶ms.to)?; - let (from_parent, from_name) = agentfs.fs.resolve_parent_and_name(&from).await?; - let (to_parent, to_name) = agentfs.fs.resolve_parent_and_name(&to).await?; + let (from_parent, from_name) = vfs.fs.resolve_parent_and_name(&from).await?; + let (to_parent, to_name) = vfs.fs.resolve_parent_and_name(&to).await?; - FileSystem::rename(&agentfs.fs, from_parent, &from_name, to_parent, &to_name) + FileSystem::rename(&vfs.fs, from_parent, &from_name, to_parent, &to_name) .await .context("Failed to rename")?; @@ -999,10 +995,10 @@ async fn handle_rename(agentfs: &AgentFS, params: RenameParams) -> Result Result { +async fn handle_stat(vfs: &Vfs, params: StatParams) -> Result { let path = normalize_path(¶ms.path)?; - let stats = agentfs + let stats = vfs .fs .stat(&path) .await @@ -1013,17 +1009,17 @@ async fn handle_stat(agentfs: &AgentFS, params: StatParams) -> Result { } /// Test if path exists -async fn handle_access(agentfs: &AgentFS, params: AccessParams) -> Result { +async fn handle_access(vfs: &Vfs, params: AccessParams) -> Result { let path = normalize_path(¶ms.path)?; - let exists = agentfs.fs.stat(&path).await?.is_some(); + let exists = vfs.fs.stat(&path).await?.is_some(); Ok(serde_json::to_string(&json!({ "exists": exists }))?) } /// Get KV value -async fn handle_kv_get(agentfs: &AgentFS, params: KvGetParams) -> Result { - let value: Option = agentfs +async fn handle_kv_get(vfs: &Vfs, params: KvGetParams) -> Result { + let value: Option = vfs .kv .get(¶ms.key) .await @@ -1033,9 +1029,8 @@ async fn handle_kv_get(agentfs: &AgentFS, params: KvGetParams) -> Result } /// Set KV value -async fn handle_kv_set(agentfs: &AgentFS, params: KvSetParams) -> Result { - agentfs - .kv +async fn handle_kv_set(vfs: &Vfs, params: KvSetParams) -> Result { + vfs.kv .set(¶ms.key, ¶ms.value) .await .context("Failed to set value")?; @@ -1044,9 +1039,8 @@ async fn handle_kv_set(agentfs: &AgentFS, params: KvSetParams) -> Result } /// Delete KV value -async fn handle_kv_delete(agentfs: &AgentFS, params: KvDeleteParams) -> Result { - agentfs - .kv +async fn handle_kv_delete(vfs: &Vfs, params: KvDeleteParams) -> Result { + vfs.kv .delete(¶ms.key) .await .context("Failed to delete key")?; @@ -1055,8 +1049,8 @@ async fn handle_kv_delete(agentfs: &AgentFS, params: KvDeleteParams) -> Result Result { - let mut keys = agentfs.kv.keys().await.context("Failed to list keys")?; +async fn handle_kv_list(vfs: &Vfs, params: KvListParams) -> Result { + let mut keys = vfs.kv.keys().await.context("Failed to list keys")?; if let Some(prefix) = ¶ms.prefix { keys.retain(|key| key.starts_with(prefix.as_str())); } @@ -1066,25 +1060,25 @@ async fn handle_kv_list(agentfs: &AgentFS, params: KvListParams) -> Result Result> { +async fn list_resources(vfs: &Vfs) -> Result> { let mut resources = Vec::new(); - collect_file_resources(agentfs, "/", &mut resources).await?; + collect_file_resources(vfs, "/", &mut resources).await?; Ok(resources) } /// Recursively collect file resources fn collect_file_resources<'a>( - agentfs: &'a AgentFS, + vfs: &'a Vfs, path: &'a str, resources: &'a mut Vec, ) -> std::pin::Pin> + Send + 'a>> { Box::pin(async move { - let dir_stats = match agentfs.fs.stat(path).await? { + let dir_stats = match vfs.fs.stat(path).await? { Some(s) => s, None => return Ok(()), }; - let entries = match agentfs.fs.readdir(dir_stats.ino).await? { + let entries = match vfs.fs.readdir(dir_stats.ino).await? { Some(entries) => entries, None => return Ok(()), }; @@ -1096,7 +1090,7 @@ fn collect_file_resources<'a>( format!("{}/{}", path, entry) }; - let stats = match agentfs.fs.stat(&full_path).await? { + let stats = match vfs.fs.stat(&full_path).await? { Some(s) => s, None => continue, }; @@ -1110,7 +1104,7 @@ fn collect_file_resources<'a>( })); } else if stats.is_directory() { // Recurse into subdirectory - collect_file_resources(agentfs, &full_path, resources).await?; + collect_file_resources(vfs, &full_path, resources).await?; } } @@ -1119,10 +1113,10 @@ fn collect_file_resources<'a>( } /// Read a resource by path -async fn read_resource(agentfs: &AgentFS, path: &str) -> Result> { +async fn read_resource(vfs: &Vfs, path: &str) -> Result> { let normalized = normalize_path(path)?; - let data = agentfs + let data = vfs .fs .read_file(&normalized) .await diff --git a/crates/agentfs-cli/src/cmd/mcp_server/tests.rs b/crates/vfs-cli/src/cmd/mcp_server/tests.rs similarity index 82% rename from crates/agentfs-cli/src/cmd/mcp_server/tests.rs rename to crates/vfs-cli/src/cmd/mcp_server/tests.rs index 1c1974bb..90fe2f68 100644 --- a/crates/agentfs-cli/src/cmd/mcp_server/tests.rs +++ b/crates/vfs-cli/src/cmd/mcp_server/tests.rs @@ -1,6 +1,6 @@ use super::*; -use agentfs_core::fs::S_IFREG; -use agentfs_core::{error::Error as SdkError, FsError, ToolCallStatus}; +use vfs_core::fs::S_IFREG; +use vfs_core::{error::Error as SdkError, FsError, ToolCallStatus}; const ROOT_INO: i64 = 1; @@ -8,21 +8,21 @@ const ROOT_INO: i64 = 1; /// audit rows are shared across requests within one server. The PerRequest /// reopen semantics are inherently cross-process and are covered by the /// stdio shell suite (tests/test-mcp-server.sh). -async fn create_test_server() -> Result<(McpServer, Arc)> { +async fn create_test_server() -> Result<(McpServer, Arc)> { create_filtered_test_server(None).await } async fn create_filtered_test_server( tools_filter: Option>, -) -> Result<(McpServer, Arc)> { +) -> Result<(McpServer, Arc)> { let server = McpServer::new( ":memory:".to_string(), - AgentFSOptions::ephemeral(), + VfsOptions::ephemeral(), tools_filter, ) .await?; - let agentfs = server.agentfs().await?; - Ok((server, agentfs)) + let vfs = server.vfs().await?; + Ok((server, vfs)) } async fn call_tool(server: &McpServer, id: i64, name: &str, arguments: JsonValue) -> JsonValue { @@ -52,7 +52,7 @@ fn advertised_tool_names(server: &McpServer) -> Vec { #[tokio::test] async fn tools_list_equals_dispatch_surface() -> Result<()> { - let (server, agentfs) = create_test_server().await?; + let (server, vfs) = create_test_server().await?; assert_eq!( advertised_tool_names(&server), @@ -61,7 +61,7 @@ async fn tools_list_equals_dispatch_surface() -> Result<()> { ); // Every advertised tool must dispatch with minimal valid arguments. - agentfs.fs.mkdir("/dir", 0, 0).await?; + vfs.fs.mkdir("/dir", 0, 0).await?; let minimal_args = |name: &str| -> JsonValue { match name { "read_file" => json!({"path": "/seed.txt"}), @@ -120,17 +120,17 @@ async fn tools_list_equals_dispatch_surface() -> Result<()> { #[tokio::test] async fn kv_list_dispatches_and_honors_prefix() -> Result<()> { - let (_server, agentfs) = create_test_server().await?; - agentfs.kv.set("app/one", &json!(1)).await?; - agentfs.kv.set("app/two", &json!(2)).await?; - agentfs.kv.set("other", &json!(3)).await?; + let (_server, vfs) = create_test_server().await?; + vfs.kv.set("app/one", &json!(1)).await?; + vfs.kv.set("app/two", &json!(2)).await?; + vfs.kv.set("other", &json!(3)).await?; - let all = handle_kv_list(&agentfs, KvListParams { prefix: None }).await?; + let all = handle_kv_list(&vfs, KvListParams { prefix: None }).await?; let all: Vec = serde_json::from_str(&all)?; assert_eq!(all, vec!["app/one", "app/two", "other"]); let filtered = handle_kv_list( - &agentfs, + &vfs, KvListParams { prefix: Some("app/".to_string()), }, @@ -143,7 +143,7 @@ async fn kv_list_dispatches_and_honors_prefix() -> Result<()> { #[tokio::test] async fn tool_calls_are_audited_with_status_and_timing() -> Result<()> { - let (server, agentfs) = create_test_server().await?; + let (server, vfs) = create_test_server().await?; let ok = call_tool( &server, @@ -160,7 +160,7 @@ async fn tool_calls_are_audited_with_status_and_timing() -> Result<()> { "read_file on a missing path must fail" ); - let calls = agentfs.tools.recent(None).await?; + let calls = vfs.tools.recent(None).await?; assert_eq!(calls.len(), 2, "each tools/call must write one audit row"); // recent() orders by started_at, which can tie at millisecond @@ -202,9 +202,9 @@ async fn tool_calls_are_audited_with_status_and_timing() -> Result<()> { #[tokio::test] async fn write_file_preserves_existing_mode_and_creates_with_default() -> Result<()> { - let (server, agentfs) = create_test_server().await?; + let (server, vfs) = create_test_server().await?; - let (created, file) = agentfs + let (created, file) = vfs .fs .create_file("/mode.txt", S_IFREG | 0o755, 0, 0) .await?; @@ -221,7 +221,7 @@ async fn write_file_preserves_existing_mode_and_creates_with_default() -> Result .await; assert!(response.get("error").is_none(), "write failed: {response}"); - let stats = agentfs.fs.stat("/mode.txt").await?.unwrap(); + let stats = vfs.fs.stat("/mode.txt").await?.unwrap(); assert_eq!( stats.mode, S_IFREG | 0o755, @@ -229,7 +229,7 @@ async fn write_file_preserves_existing_mode_and_creates_with_default() -> Result ); assert_eq!(stats.size, "overwritten".len() as i64); assert_eq!( - agentfs.fs.read_file("/mode.txt").await?.unwrap(), + vfs.fs.read_file("/mode.txt").await?.unwrap(), b"overwritten".to_vec() ); @@ -241,7 +241,7 @@ async fn write_file_preserves_existing_mode_and_creates_with_default() -> Result ) .await; assert!(response.get("error").is_none(), "write failed: {response}"); - let stats = agentfs.fs.stat("/fresh.txt").await?.unwrap(); + let stats = vfs.fs.stat("/fresh.txt").await?.unwrap(); assert_eq!( stats.mode, DEFAULT_FILE_MODE, "new files must be created with the documented default mode 0644" @@ -251,7 +251,7 @@ async fn write_file_preserves_existing_mode_and_creates_with_default() -> Result #[tokio::test] async fn unknown_tool_is_rejected_with_invalid_params_and_audited() -> Result<()> { - let (server, agentfs) = create_test_server().await?; + let (server, vfs) = create_test_server().await?; let response = call_tool(&server, 1, "copy_file", json!({})).await; let error = response @@ -270,7 +270,7 @@ async fn unknown_tool_is_rejected_with_invalid_params_and_audited() -> Result<() "error should name the unknown tool: {error}" ); - let calls = agentfs.tools.recent(None).await?; + let calls = vfs.tools.recent(None).await?; assert_eq!(calls.len(), 1, "rejected calls are audited too"); assert_eq!(calls[0].name, "copy_file"); assert_eq!(calls[0].status, ToolCallStatus::Error); @@ -279,7 +279,7 @@ async fn unknown_tool_is_rejected_with_invalid_params_and_audited() -> Result<() #[tokio::test] async fn notifications_are_accepted_without_response() -> Result<()> { - let (server, _agentfs) = create_test_server().await?; + let (server, _vfs) = create_test_server().await?; // The standard post-initialize notification from strict clients. assert_eq!( @@ -312,7 +312,7 @@ async fn notifications_are_accepted_without_response() -> Result<()> { #[tokio::test] async fn unknown_method_with_id_returns_method_not_found() -> Result<()> { - let (server, _agentfs) = create_test_server().await?; + let (server, _vfs) = create_test_server().await?; let response = server .handle_request(json!({"jsonrpc": "2.0", "id": 7, "method": "bogus/method"})) @@ -329,7 +329,7 @@ async fn unknown_method_with_id_returns_method_not_found() -> Result<()> { #[tokio::test] async fn tools_filter_limits_both_listing_and_dispatch() -> Result<()> { - let (server, agentfs) = + let (server, vfs) = create_filtered_test_server(Some(vec!["read_file".to_string(), "kv_list".to_string()])) .await?; @@ -354,11 +354,11 @@ async fn tools_filter_limits_both_listing_and_dispatch() -> Result<()> { Some(INVALID_PARAMS) ); assert!( - agentfs.fs.stat("/blocked.txt").await?.is_none(), + vfs.fs.stat("/blocked.txt").await?.is_none(), "disabled tool must not have side effects" ); - let calls = agentfs.tools.recent(None).await?; + let calls = vfs.tools.recent(None).await?; assert_eq!(calls.len(), 1); assert_eq!(calls[0].status, ToolCallStatus::Error); Ok(()) @@ -368,7 +368,7 @@ async fn tools_filter_limits_both_listing_and_dispatch() -> Result<()> { async fn unknown_tools_filter_is_rejected_at_startup() -> Result<()> { let error = match McpServer::new( ":memory:".to_string(), - AgentFSOptions::ephemeral(), + VfsOptions::ephemeral(), Some(vec!["copy_file".to_string()]), ) .await @@ -387,18 +387,18 @@ async fn unknown_tools_filter_is_rejected_at_startup() -> Result<()> { #[tokio::test] async fn mcp_rename_directory_into_own_subtree_returns_error_and_preserves_namespace() -> Result<()> { - let (server, agentfs) = create_test_server().await?; - agentfs.fs.mkdir("/parent", 0, 0).await?; - agentfs.fs.mkdir("/parent/child", 0, 0).await?; + let (server, vfs) = create_test_server().await?; + vfs.fs.mkdir("/parent", 0, 0).await?; + vfs.fs.mkdir("/parent/child", 0, 0).await?; - let parent_ino = agentfs.fs.stat("/parent").await?.unwrap().ino; - let child_ino = agentfs.fs.stat("/parent/child").await?.unwrap().ino; - let root_before = agentfs.fs.readdir(ROOT_INO).await?.unwrap(); - let parent_before = agentfs.fs.readdir(parent_ino).await?.unwrap(); - let child_before = agentfs.fs.readdir(child_ino).await?.unwrap(); + let parent_ino = vfs.fs.stat("/parent").await?.unwrap().ino; + let child_ino = vfs.fs.stat("/parent/child").await?.unwrap().ino; + let root_before = vfs.fs.readdir(ROOT_INO).await?.unwrap(); + let parent_before = vfs.fs.readdir(parent_ino).await?.unwrap(); + let child_before = vfs.fs.readdir(child_ino).await?.unwrap(); let direct_error = handle_rename( - &agentfs, + &vfs, RenameParams { from: "/parent".to_string(), to: "/parent/child/parent".to_string(), @@ -436,14 +436,11 @@ async fn mcp_rename_directory_into_own_subtree_returns_error_and_preserves_names "cycle rename should return a JSON-RPC error response: {response}" ); - assert_eq!(agentfs.fs.readdir(ROOT_INO).await?.unwrap(), root_before); - assert_eq!( - agentfs.fs.readdir(parent_ino).await?.unwrap(), - parent_before - ); - assert_eq!(agentfs.fs.readdir(child_ino).await?.unwrap(), child_before); - assert!(agentfs.fs.stat("/parent").await?.is_some()); - assert!(agentfs.fs.stat("/parent/child").await?.is_some()); - assert!(agentfs.fs.stat("/parent/child/parent").await?.is_none()); + assert_eq!(vfs.fs.readdir(ROOT_INO).await?.unwrap(), root_before); + assert_eq!(vfs.fs.readdir(parent_ino).await?.unwrap(), parent_before); + assert_eq!(vfs.fs.readdir(child_ino).await?.unwrap(), child_before); + assert!(vfs.fs.stat("/parent").await?.is_some()); + assert!(vfs.fs.stat("/parent/child").await?.is_some()); + assert!(vfs.fs.stat("/parent/child/parent").await?.is_none()); Ok(()) } diff --git a/crates/agentfs-cli/src/cmd/migrate.rs b/crates/vfs-cli/src/cmd/migrate.rs similarity index 98% rename from crates/agentfs-cli/src/cmd/migrate.rs rename to crates/vfs-cli/src/cmd/migrate.rs index 876d0b29..9b02ae16 100644 --- a/crates/agentfs-cli/src/cmd/migrate.rs +++ b/crates/vfs-cli/src/cmd/migrate.rs @@ -1,16 +1,11 @@ //! Database schema migration command. //! -//! One `agentfs migrate` lands any supported old schema at the current +//! One `vfs migrate` lands any supported old schema at the current //! version: in place by default (every supported migration is an additive, //! transactional ALTER), or copy-based via `--copy ` for users who //! want a rebuilt database with the current chunk layout, keeping the //! hash/verify engine from the historical `migrate-v0-5` command. -use agentfs_core::{ - config::{DEFAULT_CHUNK_SIZE, DEFAULT_INLINE_THRESHOLD}, - error::Error as SdkError, - schema, AgentFSOptions, SchemaVersion, -}; use anyhow::{Context, Result as AnyhowResult}; use std::collections::{hash_map::DefaultHasher, HashSet}; use std::fs; @@ -19,6 +14,11 @@ use std::io::{Read as IoRead, Write}; use std::path::{Path, PathBuf}; use turso::transaction::{Transaction, TransactionBehavior}; use turso::{Connection, Value}; +use vfs_core::{ + config::{DEFAULT_CHUNK_SIZE, DEFAULT_INLINE_THRESHOLD}, + error::Error as SdkError, + schema, SchemaVersion, VfsOptions, +}; use super::safety::{build_local_database, ReadOnlyOpenSidecars}; @@ -28,19 +28,19 @@ const S_IFREG: i64 = 0o100000; /// Guidance for a schema-version mismatch surfaced by an open path. /// /// Names a command that actually finishes the job: supported old versions get -/// `agentfs migrate ` (which lands at CURRENT in one invocation); -/// anything else is from a newer agentfs and no local command can help. +/// `vfs migrate ` (which lands at CURRENT in one invocation); +/// anything else is from a newer vfs and no local command can help. fn schema_upgrade_guidance(found: &str, expected: &str, id_or_path: &str) -> String { match SchemaVersion::parse(found) { Some(version) if version < schema::CURRENT => format!( "Filesystem `{id_or_path}` requires migration\n\n\ - Found schema version {found}, but this version of agentfs requires {expected}.\n\n\ - To upgrade, run:\n\n agentfs migrate {id_or_path}\n" + Found schema version {found}, but this version of vfs requires {expected}.\n\n\ + To upgrade, run:\n\n vfs migrate {id_or_path}\n" ), _ => format!( "Filesystem `{id_or_path}` has unsupported schema version {found}; \ - this version of agentfs supports up to {expected}.\n\ - The database was likely created by a newer agentfs. Upgrade agentfs to open it." + this version of vfs supports up to {expected}.\n\ + The database was likely created by a newer vfs. Upgrade vfs to open it." ), } } @@ -63,7 +63,7 @@ pub async fn handle_migrate_command( dry_run: bool, encryption: Option<&(String, String)>, ) -> AnyhowResult<()> { - let options = AgentFSOptions::resolve(&id_or_path)?; + let options = VfsOptions::resolve(&id_or_path)?; let db_path_str = options .db_path() .context("Failed to resolve database path")?; @@ -157,7 +157,7 @@ pub async fn handle_migrate_copy_command( overwrite_target: bool, encryption: Option<&(String, String)>, ) -> AnyhowResult<()> { - let options = AgentFSOptions::resolve(&id_or_path)?; + let options = VfsOptions::resolve(&id_or_path)?; let source_path_str = options .db_path() .context("Failed to resolve database path")?; @@ -239,8 +239,8 @@ async fn copy_migrate_to_current( .unwrap_or(SchemaVersion::V0_0); if source_version == schema::CURRENT { anyhow::bail!( - "Source database is already at schema {}. Run `agentfs migrate {}` for in-place \ - normalization or `agentfs backup` for a portable copy.", + "Source database is already at schema {}. Run `vfs migrate {}` for in-place \ + normalization or `vfs backup` for a portable copy.", schema::CURRENT, source_path.display() ); @@ -320,7 +320,7 @@ async fn create_current_schema(conn: &Connection) -> AnyhowResult<()> { /// The v0.0 -> v0.2 nlink backfill rule, applied on the copy target when the /// source predates the nlink column. Must stay in step with the in-place -/// migration in `agentfs_core::schema`. +/// migration in `vfs_core::schema`. const NLINK_BACKFILL_CASE: &str = "CASE WHEN fs_inode.ino = 1 THEN 2 WHEN (fs_inode.mode & 61440) = 16384 @@ -2028,7 +2028,7 @@ mod tests { ] { let guidance = schema_upgrade_guidance(found, "0.5", id_or_path); assert!( - guidance.contains(&format!("agentfs migrate {id_or_path}")), + guidance.contains(&format!("vfs migrate {id_or_path}")), "{found}: {guidance}" ); assert!(!guidance.contains("migrate-v0-5"), "{found}: {guidance}"); @@ -2036,10 +2036,10 @@ mod tests { let future = schema_upgrade_guidance("user_version 7", "0.5", "my-agent"); assert!( - !future.contains("agentfs migrate"), + !future.contains("vfs migrate"), "future versions must not promise migrate can fix them: {future}" ); - assert!(future.contains("newer agentfs"), "{future}"); + assert!(future.contains("newer vfs"), "{future}"); } #[tokio::test] @@ -2168,11 +2168,10 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let db_path = temp_dir.path().join("current.db"); { - let agent = agentfs_core::AgentFS::open(agentfs_core::AgentFSOptions::with_path( - db_path.to_string_lossy(), - )) - .await - .unwrap(); + let agent = + vfs_core::Vfs::open(vfs_core::VfsOptions::with_path(db_path.to_string_lossy())) + .await + .unwrap(); agent.fs.finalize().await.unwrap(); } let wal = PathBuf::from(format!("{}-wal", db_path.display())); diff --git a/crates/agentfs-cli/src/cmd/mod.rs b/crates/vfs-cli/src/cmd/mod.rs similarity index 100% rename from crates/agentfs-cli/src/cmd/mod.rs rename to crates/vfs-cli/src/cmd/mod.rs diff --git a/crates/agentfs-cli/src/cmd/mount.rs b/crates/vfs-cli/src/cmd/mount.rs similarity index 91% rename from crates/agentfs-cli/src/cmd/mount.rs rename to crates/vfs-cli/src/cmd/mount.rs index b16e1e38..c11a99b5 100644 --- a/crates/agentfs-cli/src/cmd/mount.rs +++ b/crates/vfs-cli/src/cmd/mount.rs @@ -1,22 +1,20 @@ -use agentfs_core::{ - AgentFSOptions, EncryptionConfig, FileSystem, HostFS, OverlayFS, PartialOriginPolicy, -}; -use agentfs_mount::{mount_fs, Backend, MountOpts}; use anyhow::Result; use std::{path::PathBuf, sync::Arc}; use turso::value::Value; +use vfs_core::{EncryptionConfig, FileSystem, HostFS, OverlayFS, PartialOriginPolicy, VfsOptions}; +use vfs_mount::{mount_fs, Backend, MountOpts}; -#[cfg(target_os = "linux")] -use agentfs_core::{error::Error as SdkError, get_mounts, Mount}; #[cfg(target_os = "linux")] use std::{ io::{self, Write}, os::unix::fs::MetadataExt, path::Path, }; +#[cfg(target_os = "linux")] +use vfs_core::{error::Error as SdkError, get_mounts, Mount}; #[cfg(target_os = "linux")] -use crate::cmd::init::open_agentfs; +use crate::cmd::init::open_vfs; use crate::opts::MountBackend; @@ -47,10 +45,7 @@ pub struct MountArgs { pub encryption: Option<(String, String)>, } -fn apply_encryption( - options: AgentFSOptions, - encryption: &Option<(String, String)>, -) -> AgentFSOptions { +fn apply_encryption(options: VfsOptions, encryption: &Option<(String, String)>) -> VfsOptions { match encryption { Some((key, cipher)) => options.with_encryption(EncryptionConfig { hex_key: key.clone(), @@ -76,7 +71,7 @@ pub fn mount(args: MountArgs) -> Result<()> { MountBackend::Fuse => { anyhow::bail!( "FUSE mounting is not supported on macOS.\n\ - Use --backend nfs (default) or `agentfs nfs` instead." + Use --backend nfs (default) or `vfs nfs` instead." ); } MountBackend::Nfs => mount_nfs(args), @@ -101,7 +96,7 @@ fn mount_nfs(args: MountArgs) -> Result<()> { rt.block_on(mount_nfs_backend(args, mountpoint)) } else { let ready_mountpoint = mountpoint.clone(); - agentfs_mount::daemon::daemonize( + vfs_mount::daemon::daemonize( move || { // The forked daemon shares the parent's spill dir, which the // parent removes when it exits; move off it before DB work. @@ -109,7 +104,7 @@ fn mount_nfs(args: MountArgs) -> Result<()> { let rt = crate::get_runtime(); rt.block_on(mount_nfs_backend(args, mountpoint)) }, - move || agentfs_mount::is_mountpoint(&ready_mountpoint), + move || vfs_mount::is_mountpoint(&ready_mountpoint), std::time::Duration::from_secs(10), ) } @@ -118,7 +113,7 @@ fn mount_nfs(args: MountArgs) -> Result<()> { /// Mount the agent filesystem using FUSE (Linux only). #[cfg(target_os = "linux")] fn mount_fuse(args: MountArgs) -> Result<()> { - let opts = apply_encryption(AgentFSOptions::resolve(&args.id_or_path)?, &args.encryption); + let opts = apply_encryption(VfsOptions::resolve(&args.id_or_path)?, &args.encryption); // Check schema version before daemonizing. This allows us to show the error // message to the user directly, rather than having it appear in daemon logs. @@ -138,7 +133,7 @@ fn mount_fuse(args: MountArgs) -> Result<()> { } let fsname = format!( - "agentfs:{}", + "vfs:{}", std::fs::canonicalize(&args.id_or_path) .map(|p| p.to_string_lossy().to_string()) .unwrap_or_else(|_| args.id_or_path.clone()) @@ -174,15 +169,15 @@ fn mount_fuse(args: MountArgs) -> Result<()> { let partial_origin_policy = args.partial_origin_policy; let mount = move || { let rt = crate::get_runtime(); - let agentfs = rt - .block_on(open_agentfs(opts)) + let vfs = rt + .block_on(open_vfs(opts)) .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; // Check for overlay configuration let fs: Arc = rt.block_on(async { // Query base_path in a separate scope so connection is released let base_path: Option = { - let conn = agentfs.get_connection().await?; + let conn = vfs.get_connection().await?; let query = "SELECT value FROM fs_overlay_config WHERE key = 'base_path'"; match conn.query(query, ()).await { Ok(mut rows) => { @@ -208,15 +203,15 @@ fn mount_fuse(args: MountArgs) -> Result<()> { let hostfs = HostFS::new(&base_path)?; let hostfs = hostfs.with_fuse_mountpoint(mountpoint_ino); let overlay = if let Some(policy) = partial_origin_policy { - OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), agentfs.fs, policy) + OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), vfs.fs, policy) } else { - OverlayFS::new(Arc::new(hostfs), agentfs.fs) + OverlayFS::new(Arc::new(hostfs), vfs.fs) }; overlay.load().await?; // Load persisted whiteouts and origin mappings Ok::, anyhow::Error>(Arc::new(overlay)) } else { - // Plain AgentFS - Ok(Arc::new(agentfs.fs) as Arc) + // Plain Vfs + Ok(Arc::new(vfs.fs) as Arc) } })?; @@ -226,14 +221,14 @@ fn mount_fuse(args: MountArgs) -> Result<()> { if foreground { mount() } else { - agentfs_mount::daemon::daemonize( + vfs_mount::daemon::daemonize( move || { // The forked daemon shares the parent's spill dir, which the // parent removes when it exits; move off it before DB work. crate::config::adopt_private_spill_dir(); mount() }, - move || agentfs_mount::is_mountpoint(&mountpoint), + move || vfs_mount::is_mountpoint(&mountpoint), std::time::Duration::from_secs(10), ) } @@ -251,33 +246,33 @@ async fn require_schema_current_for_mount_precheck( .await .map_err(|err| SdkError::Internal(format!("{err:#}")))?; let conn = db.connect()?; - agentfs_core::schema::require_current(&conn).await + vfs_core::schema::require_current(&conn).await } /// Mount the agent filesystem using NFS over localhost. /// /// `mountpoint` is pre-canonicalized by `mount_nfs` before any fork. async fn mount_nfs_backend(args: MountArgs, mountpoint: PathBuf) -> Result<()> { - use crate::cmd::init::open_agentfs; + use crate::cmd::init::open_vfs; - let opts = apply_encryption(AgentFSOptions::resolve(&args.id_or_path)?, &args.encryption); + let opts = apply_encryption(VfsOptions::resolve(&args.id_or_path)?, &args.encryption); let fsname = format!( - "agentfs:{}", + "vfs:{}", std::fs::canonicalize(&args.id_or_path) .map(|p| p.to_string_lossy().to_string()) .unwrap_or_else(|_| args.id_or_path.clone()) ); - // Open AgentFS - let agentfs = open_agentfs(opts) + // Open Vfs + let vfs = open_vfs(opts) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &args.id_or_path))?; // Check for overlay configuration // Query base_path in a separate scope so connection is released before load_whiteouts let base_path: Option = { - let conn = agentfs.get_connection().await?; + let conn = vfs.get_connection().await?; let query = "SELECT value FROM fs_overlay_config WHERE key = 'base_path'"; match conn.query(query, ()).await { Ok(mut rows) => { @@ -302,15 +297,15 @@ async fn mount_nfs_backend(args: MountArgs, mountpoint: PathBuf) -> Result<()> { eprintln!("Using overlay filesystem with base: {}", base_path); let hostfs = HostFS::new(&base_path)?; let overlay = if let Some(policy) = args.partial_origin_policy { - OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), agentfs.fs, policy) + OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), vfs.fs, policy) } else { - OverlayFS::new(Arc::new(hostfs), agentfs.fs) + OverlayFS::new(Arc::new(hostfs), vfs.fs) }; overlay.load().await?; // Load persisted whiteouts and origin mappings Arc::new(overlay) as Arc } else { - // Plain AgentFS - Arc::new(agentfs.fs) as Arc + // Plain Vfs + Arc::new(vfs.fs) as Arc }; let mount_opts = MountOpts { @@ -340,16 +335,16 @@ async fn run_mount_session( eprintln!("Press Ctrl+C to unmount and exit."); } - agentfs_mount::supervise::supervise_mount(handle).await + vfs_mount::supervise::supervise_mount(handle).await } -/// List all currently mounted agentfs filesystems (Linux) +/// List all currently mounted vfs filesystems (Linux) #[cfg(target_os = "linux")] pub fn list_mounts(out: &mut W) { let mounts = get_mounts(); if mounts.is_empty() { - let _ = writeln!(out, "No agentfs filesystems mounted."); + let _ = writeln!(out, "No vfs filesystems mounted."); return; } @@ -385,7 +380,7 @@ pub fn list_mounts(out: &mut W) { } } -/// List all currently mounted agentfs filesystems (macOS stub) +/// List all currently mounted vfs filesystems (macOS stub) #[cfg(target_os = "macos")] pub fn list_mounts(out: &mut W) { let _ = writeln!(out, "Mount listing is only available on Linux."); @@ -482,9 +477,9 @@ fn confirm(prompt: &str) -> bool { matches!(input.trim().to_lowercase().as_str(), "y" | "yes") } -/// Prune unused agentfs mount points. +/// Prune unused vfs mount points. /// -/// Finds all mounted agentfs filesystems that are not in use by any process +/// Finds all mounted vfs filesystems that are not in use by any process /// and unmounts them. #[cfg(target_os = "linux")] pub fn prune_mounts(force: bool) -> Result<()> { @@ -545,7 +540,7 @@ pub fn prune_mounts(force: bool) -> Result<()> { Ok(()) } -/// Prune unused agentfs mount points (macOS stub). +/// Prune unused vfs mount points (macOS stub). #[cfg(target_os = "macos")] pub fn prune_mounts(_force: bool) -> Result<()> { anyhow::bail!("Mount pruning is only available on Linux") @@ -602,8 +597,8 @@ mod tests { let dir = tempdir().unwrap(); let db_path = dir.path().join("encrypted.db"); - let agentfs = agentfs_core::AgentFS::open( - AgentFSOptions::with_path(db_path.to_str().unwrap().to_string()).with_encryption( + let vfs = vfs_core::Vfs::open( + VfsOptions::with_path(db_path.to_str().unwrap().to_string()).with_encryption( EncryptionConfig { hex_key: TEST_KEY.to_string(), cipher: TEST_CIPHER.to_string(), @@ -612,7 +607,7 @@ mod tests { ) .await .unwrap(); - drop(agentfs); + drop(vfs); let encryption = (TEST_KEY.to_string(), TEST_CIPHER.to_string()); require_schema_current_for_mount_precheck(db_path.to_str().unwrap(), Some(&encryption)) diff --git a/crates/agentfs-cli/src/cmd/nfs.rs b/crates/vfs-cli/src/cmd/nfs.rs similarity index 75% rename from crates/agentfs-cli/src/cmd/nfs.rs rename to crates/vfs-cli/src/cmd/nfs.rs index cfa7421e..239315f4 100644 --- a/crates/agentfs-cli/src/cmd/nfs.rs +++ b/crates/vfs-cli/src/cmd/nfs.rs @@ -1,17 +1,17 @@ //! Standalone NFS server command. //! -//! This module provides a standalone NFS server that exports an AgentFS +//! This module provides a standalone NFS server that exports a Vfs //! filesystem over the network, allowing remote systems (like VMs) to mount //! it as their root filesystem. -use agentfs_core::{agentfs_dir, AgentFSOptions, EncryptionConfig, FileSystem, HostFS, OverlayFS}; -use agentfs_mount::{serve_nfs, NfsServerOptions}; use anyhow::{Context, Result}; use std::path::PathBuf; use std::sync::Arc; use tokio::signal; +use vfs_core::{vfs_dir, EncryptionConfig, FileSystem, HostFS, OverlayFS, VfsOptions}; +use vfs_mount::{serve_nfs, NfsServerOptions}; -use crate::cmd::init::open_agentfs; +use crate::cmd::init::open_vfs; /// Handle the `nfs` command - start a standalone NFS server. pub async fn handle_nfs_command( @@ -23,39 +23,39 @@ pub async fn handle_nfs_command( // Resolve database path let db_path = resolve_db_path(&id_or_path)?; - // Open or create the AgentFS database + // Open or create the Vfs database let db_path_str = db_path .to_str() .context("Database path contains non-UTF8 characters")?; - let mut options = AgentFSOptions::with_path(db_path_str); + let mut options = VfsOptions::with_path(db_path_str); if let Some((key, cipher)) = encryption { options = options.with_encryption(EncryptionConfig { hex_key: key, cipher, }); } - let agentfs = open_agentfs(options) + let vfs = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; // Check if overlay is configured in the database - let base_path = agentfs + let base_path = vfs .is_overlay_enabled() .await .context("Failed to check overlay config")?; - // Create filesystem - either direct AgentFS or overlay with base + // Create filesystem - either direct Vfs or overlay with base let fs: Arc = if let Some(base_str) = base_path { let hostfs = HostFS::new(&base_str).context("Failed to create HostFS")?; - let overlay = OverlayFS::new(Arc::new(hostfs), agentfs.fs); + let overlay = OverlayFS::new(Arc::new(hostfs), vfs.fs); overlay.load().await?; // Load persisted whiteouts and origin mappings eprintln!("Mode: overlay (base: {})", base_str); Arc::new(overlay) } else { - eprintln!("Mode: direct AgentFS"); - Arc::new(agentfs.fs) + eprintln!("Mode: direct Vfs"); + Arc::new(vfs.fs) }; // Bind NFS server @@ -67,7 +67,7 @@ pub async fn handle_nfs_command( // Print server info eprintln!(); - eprintln!("AgentFS NFS Server"); + eprintln!("Vfs NFS Server"); eprintln!(" Database: {}", db_path.display()); eprintln!(" Listening: {}", listen_addr); eprintln!(" Export: /"); @@ -107,14 +107,14 @@ fn resolve_db_path(id_or_path: &str) -> Result { return Ok(path); } - // Otherwise, treat it as an agent ID and look in .agentfs/ - let agentfs_dir = agentfs_dir(); - let db_path = agentfs_dir.join(format!("{}.db", id_or_path)); + // Otherwise, treat it as an agent ID and look in .vfs/ + let vfs_dir = vfs_dir(); + let db_path = vfs_dir.join(format!("{}.db", id_or_path)); if db_path.exists() { Ok(db_path) } else { - // If it doesn't exist, still return the path - AgentFS will create it + // If it doesn't exist, still return the path - Vfs will create it Ok(db_path) } } diff --git a/crates/agentfs-cli/src/cmd/profiling.rs b/crates/vfs-cli/src/cmd/profiling.rs similarity index 77% rename from crates/agentfs-cli/src/cmd/profiling.rs rename to crates/vfs-cli/src/cmd/profiling.rs index 7e007758..5dec2d8b 100644 --- a/crates/agentfs-cli/src/cmd/profiling.rs +++ b/crates/vfs-cli/src/cmd/profiling.rs @@ -3,7 +3,7 @@ //! Adapter-specific counters register from their owning crates when those //! adapters are constructed; the CLI owns only the process report lifecycle. -const PROFILE_SUMMARY_EVENT: &str = "agentfs_profile_summary"; +const PROFILE_SUMMARY_EVENT: &str = "vfs_profile_summary"; /// Drop guard installed by the CLI binary for a single process summary. #[derive(Debug)] @@ -36,14 +36,13 @@ pub fn emit_cli_report() { } pub(crate) fn report_checkpoint() { - if let Some(payload) = agentfs_core::telemetry::checkpoint_payload(PROFILE_SUMMARY_EVENT) { + if let Some(payload) = vfs_core::telemetry::checkpoint_payload(PROFILE_SUMMARY_EVENT) { eprintln!("{payload}"); } } fn emit_profile_summary(source: &str) { - if let Some(payload) = - agentfs_core::telemetry::take_summary_payload(PROFILE_SUMMARY_EVENT, source) + if let Some(payload) = vfs_core::telemetry::take_summary_payload(PROFILE_SUMMARY_EVENT, source) { eprintln!("{payload}"); } diff --git a/crates/agentfs-cli/src/cmd/ps.rs b/crates/vfs-cli/src/cmd/ps.rs similarity index 93% rename from crates/agentfs-cli/src/cmd/ps.rs rename to crates/vfs-cli/src/cmd/ps.rs index 3ab6c5b7..bdb4d0ac 100644 --- a/crates/agentfs-cli/src/cmd/ps.rs +++ b/crates/vfs-cli/src/cmd/ps.rs @@ -1,4 +1,4 @@ -//! List active agentfs run sessions. +//! List active vfs run sessions. use std::io::Write; use std::path::{Path, PathBuf}; @@ -25,10 +25,7 @@ pub struct ProcInfo { /// Get the path to the procs directory for a session. pub(crate) fn procs_dir(session_id: &str) -> PathBuf { let home = dirs::home_dir().expect("home directory"); - home.join(".agentfs") - .join("run") - .join(session_id) - .join("procs") + home.join(".vfs").join("run").join(session_id).join("procs") } /// Get the path to a proc file. @@ -37,7 +34,12 @@ fn proc_file(session_id: &str, pid: u32) -> PathBuf { } /// Write a proc file for the current process. -pub(crate) fn write_proc_file(session_id: &str, owner: bool, command: &str, cwd: &Path) -> Result<()> { +pub(crate) fn write_proc_file( + session_id: &str, + owner: bool, + command: &str, + cwd: &Path, +) -> Result<()> { let pid = std::process::id(); let procs_dir = procs_dir(session_id); std::fs::create_dir_all(&procs_dir)?; @@ -74,7 +76,7 @@ fn is_process_alive(pid: u32) -> bool { result == 0 || std::io::Error::last_os_error().raw_os_error() == Some(libc::EPERM) } -/// `agentfs run` sessions only exist on Unix platforms, so nothing can be +/// `vfs run` sessions only exist on Unix platforms, so nothing can be /// alive here. #[cfg(not(unix))] fn is_process_alive(_pid: u32) -> bool { @@ -142,7 +144,7 @@ fn list_sessions() -> Vec { None => return vec![], }; - let run_dir = home.join(".agentfs").join("run"); + let run_dir = home.join(".vfs").join("run"); let entries = match std::fs::read_dir(&run_dir) { Ok(e) => e, Err(_) => return vec![], @@ -215,12 +217,12 @@ const COL_OWNER: usize = 5; const COL_COMMAND: usize = 15; const COL_STARTED: usize = 10; -/// List active agentfs run sessions. +/// List active vfs run sessions. pub fn list_ps(out: &mut W) -> Result<()> { let sessions = list_sessions(); if sessions.is_empty() { - writeln!(out, "No active agentfs run sessions.")?; + writeln!(out, "No active vfs run sessions.")?; return Ok(()); } diff --git a/crates/agentfs-cli/src/cmd/run/darwin.rs b/crates/vfs-cli/src/cmd/run/darwin.rs similarity index 94% rename from crates/agentfs-cli/src/cmd/run/darwin.rs rename to crates/vfs-cli/src/cmd/run/darwin.rs index 11c80d6d..a818c075 100644 --- a/crates/agentfs-cli/src/cmd/run/darwin.rs +++ b/crates/vfs-cli/src/cmd/run/darwin.rs @@ -2,7 +2,7 @@ //! //! This module provides a sandboxed execution environment using NFS for //! filesystem mounting. The current working directory becomes a -//! copy-on-write overlay backed by AgentFS, mounted via a localhost NFS server. +//! copy-on-write overlay backed by Vfs, mounted via a localhost NFS server. //! //! Sandboxing is enforced using macOS sandbox-exec with dynamically generated //! profiles. Writes are restricted to the NFS mountpoint and allowed paths; @@ -11,16 +11,16 @@ //! `PLATFORM_READ_ROOTS`). use crate::opts::RunOptions; -use agentfs_core::{AgentFS, AgentFSOptions, FileSystem, HostFS, OverlayFS}; use anyhow::{Context, Result}; use std::path::{Path, PathBuf}; use std::sync::Arc; +use vfs_core::{FileSystem, HostFS, OverlayFS, Vfs, VfsOptions}; -use agentfs_mount::supervise::{ +use vfs_mount::supervise::{ exit_code_for_spawn_error, exit_code_for_status, run_supervised, supervise_command, ChildOutcome, }; -use agentfs_mount::{mount_fs, Backend, MountOpts}; +use vfs_mount::{mount_fs, Backend, MountOpts}; /// Configuration for the macOS sandbox profile. #[derive(Debug, Clone)] @@ -139,7 +139,7 @@ impl SandboxParams { /// comment) where Seatbelt parameters are not available, so restrict it to a /// conservative charset instead of trusting `--session` input. A session id /// with no surviving characters falls back to a fixed placeholder so the -/// deny message never degrades to a bare `agentfs-:` tag. +/// deny message never degrades to a bare `vfs-:` tag. fn sandbox_log_tag(session_id: &str) -> String { let tag: String = session_id .chars() @@ -152,7 +152,7 @@ fn sandbox_log_tag(session_id: &str) -> String { } } -/// Generate a sandbox-exec profile for AgentFS. +/// Generate a sandbox-exec profile for Vfs. /// /// The profile allows most operations but restricts file writes to the NFS /// mountpoint, temp directories, and explicitly allowed paths, and file @@ -162,7 +162,7 @@ fn sandbox_log_tag(session_id: &str) -> String { pub(super) fn generate_sandbox_profile(config: &SandboxConfig) -> SandboxProfile { let mut profile = Vec::new(); let mut params = SandboxParams::default(); - let log_tag = format!("agentfs-{}", sandbox_log_tag(&config.session_id)); + let log_tag = format!("vfs-{}", sandbox_log_tag(&config.session_id)); profile.push("(version 1)".to_string()); profile.push(format!( @@ -319,36 +319,36 @@ pub async fn run(options: RunOptions) -> Result<()> { std::process::exit(exit_code_for_outcome(outcome)); } else { eprintln!("Cleaning up stale NFS mount..."); - if let Err(e) = agentfs_mount::unmount(&session.mountpoint, Backend::Nfs, true) { + if let Err(e) = vfs_mount::unmount(&session.mountpoint, Backend::Nfs, true) { eprintln!("Warning: Failed to unmount stale mount: {}", e); } } } - // Initialize the AgentFS database + // Initialize the Vfs database let db_path_str = session .db_path .to_str() .context("Database path contains non-UTF8 characters")?; let encrypted = encryption.is_some(); - let mut options = AgentFSOptions::with_path(db_path_str) - .with_core_config(crate::config::core_config_from_env()); + let mut options = + VfsOptions::with_path(db_path_str).with_core_config(crate::config::core_config_from_env()); if let Some(encryption) = encryption { options = options.with_encryption(encryption); } - let agentfs = AgentFS::open(options) + let vfs = Vfs::open(options) .await .map_err(|err| crate::cmd::migrate::open_error_with_guidance(err, db_path_str)) - .context("Failed to create AgentFS")?; + .context("Failed to create Vfs")?; // Create overlay filesystem with CWD as base let base_str = cwd.to_string_lossy().to_string(); let hostfs = HostFS::new(&base_str).context("Failed to create HostFS")?; let overlay = if let Some(policy) = partial_origin_policy { - OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), agentfs.fs, policy) + OverlayFS::new_with_partial_origin_policy(Arc::new(hostfs), vfs.fs, policy) } else { - OverlayFS::new(Arc::new(hostfs), agentfs.fs) + OverlayFS::new(Arc::new(hostfs), vfs.fs) }; // Initialize the overlay (copies directory structure) @@ -360,7 +360,7 @@ pub async fn run(options: RunOptions) -> Result<()> { let fs: Arc = Arc::new(overlay); let mut mount_opts = MountOpts::new(session.mountpoint.clone(), Backend::Nfs); - mount_opts.fsname = format!("agentfs:{}", session.session_id); + mount_opts.fsname = format!("vfs:{}", session.session_id); mount_opts.lazy_unmount = true; mount_opts.timeout = std::time::Duration::from_secs(10); let mount_handle = mount_fs(fs, mount_opts).await?; @@ -383,10 +383,10 @@ pub async fn run(options: RunOptions) -> Result<()> { eprintln!("Session: {}", session.session_id); eprintln!(); eprintln!("To resume this session:"); - eprintln!(" agentfs run --session {}", session.session_id); + eprintln!(" vfs run --session {}", session.session_id); eprintln!(); eprintln!("To see what changed:"); - eprintln!(" agentfs diff {}", session.session_id); + eprintln!(" vfs diff {}", session.session_id); crate::profiling::emit_cli_report(); match status { @@ -415,7 +415,7 @@ fn exit_code_for_outcome(outcome: ChildOutcome) -> i32 { fn print_welcome_banner(session: &RunSession, encrypted: bool) { use super::group_paths_by_parent; - eprintln!("Welcome to AgentFS!"); + eprintln!("Welcome to Vfs!"); eprintln!(); eprintln!("The following directories are writable:"); eprintln!(); @@ -433,7 +433,7 @@ fn print_welcome_banner(session: &RunSession, encrypted: bool) { eprintln!(); eprintln!("To join this session from another terminal:"); eprintln!(); - eprintln!(" agentfs run --session {} ", session.session_id); + eprintln!(" vfs run --session {} ", session.session_id); eprintln!(); } @@ -465,7 +465,7 @@ fn setup_run_directory( home: &Path, ) -> Result { let run_id = session_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); - let run_dir = home.join(".agentfs").join("run").join(&run_id); + let run_dir = home.join(".vfs").join("run").join(&run_id); std::fs::create_dir_all(&run_dir).context("Failed to create run directory")?; let db_path = run_dir.join("delta.db"); @@ -555,8 +555,8 @@ fn command_in_mount( cmd.arg(&command) .args(&args) .current_dir(&session.mountpoint) - .env("AGENTFS", "1") - .env("AGENTFS_SANDBOX", "macos-sandbox") + .env("VFS", "1") + .env("VFS_SANDBOX", "macos-sandbox") // Bash prompt - show full path since we're not changing HOME .env("PS1", "🤖 \\w\\$ ") // Zsh: use custom ZDOTDIR to override prompt diff --git a/crates/agentfs-cli/src/cmd/run/linux.rs b/crates/vfs-cli/src/cmd/run/linux.rs similarity index 95% rename from crates/agentfs-cli/src/cmd/run/linux.rs rename to crates/vfs-cli/src/cmd/run/linux.rs index 98533ffe..acfc316c 100644 --- a/crates/agentfs-cli/src/cmd/run/linux.rs +++ b/crates/vfs-cli/src/cmd/run/linux.rs @@ -2,7 +2,7 @@ //! //! This module provides a sandboxed execution environment where the current //! working directory becomes a copy-on-write overlay, and the rest of the -//! filesystem is read-only. All modifications are captured in an AgentFS +//! filesystem is read-only. All modifications are captured in a Vfs //! database, leaving the original files untouched. //! //! The implementation mounts a FUSE filesystem on a hidden temporary directory, @@ -17,9 +17,6 @@ use super::{default_allowed_paths, group_paths_by_parent}; use crate::opts::RunOptions; -use agentfs_core::{ - AgentFS, AgentFSOptions, EncryptionConfig, HostFS, OverlayFS, PartialOriginPolicy, -}; use anyhow::{bail, Context, Result}; use std::{ cmp::Reverse, @@ -32,8 +29,9 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; +use vfs_core::{EncryptionConfig, HostFS, OverlayFS, PartialOriginPolicy, Vfs, VfsOptions}; -use agentfs_mount::{is_mountpoint, mount_fs, Backend, MountHandle, MountOpts}; +use vfs_mount::{is_mountpoint, mount_fs, Backend, MountHandle, MountOpts}; /// Exit code returned when exec fails (standard shell convention for "command not found") const EXIT_COMMAND_NOT_FOUND: i32 = 127; @@ -219,7 +217,7 @@ pub fn run(options: RunOptions) -> Result<()> { } /// Record the completed run session in the delta database's tool-call audit -/// log so `agentfs timeline ` reports the sandboxed command and +/// log so `vfs timeline ` reports the sandboxed command and /// its exit status (VAL-CROSS-006). async fn record_run_audit( db_path: &Path, @@ -233,12 +231,12 @@ async fn record_run_audit( let db_path_str = db_path .to_str() .context("Database path contains non-UTF8 characters")?; - let mut options = AgentFSOptions::with_path(db_path_str) - .with_core_config(crate::config::core_config_from_env()); + let mut options = + VfsOptions::with_path(db_path_str).with_core_config(crate::config::core_config_from_env()); if let Some(encryption) = encryption { options = options.with_encryption(encryption); } - let agentfs = AgentFS::open(options) + let vfs = Vfs::open(options) .await .map_err(|err| crate::cmd::migrate::open_error_with_guidance(err, db_path_str)) .context("Failed to reopen delta database for the audit row")?; @@ -249,8 +247,7 @@ async fn record_run_audit( let result = serde_json::json!({ "exit_code": exit_code }); let error_text = (exit_code != 0).then(|| format!("exit code {exit_code}")); - agentfs - .tools + vfs.tools .record( "run", started_at_ms, @@ -265,8 +262,7 @@ async fn record_run_audit( // Leave the DB in single-file journal mode again: without this the audit // insert sits in a fresh WAL sidecar, undoing the clean teardown the // supervised session just performed. - agentfs - .fs + vfs.fs .finalize() .await .context("Failed to finalize the delta database after the audit row")?; @@ -303,15 +299,15 @@ async fn mount_session_fs( .db_path .to_str() .context("Database path contains non-UTF8 characters")?; - let mut options = AgentFSOptions::with_path(db_path_str) - .with_core_config(crate::config::core_config_from_env()); + let mut options = + VfsOptions::with_path(db_path_str).with_core_config(crate::config::core_config_from_env()); if let Some(encryption) = encryption { options = options.with_encryption(encryption); } - let agentfs = AgentFS::open(options) + let vfs = Vfs::open(options) .await .map_err(|err| crate::cmd::migrate::open_error_with_guidance(err, db_path_str)) - .context("Failed to create delta AgentFS")?; + .context("Failed to create delta Vfs")?; let hostfs = HostFS::new(&fd_path).context("Failed to create HostFS")?; #[cfg(target_family = "unix")] @@ -324,9 +320,9 @@ async fn mount_session_fs( let base = Arc::new(hostfs); let overlay = if let Some(policy) = partial_origin_policy { - OverlayFS::new_with_partial_origin_policy(base, agentfs.fs, policy) + OverlayFS::new_with_partial_origin_policy(base, vfs.fs, policy) } else { - OverlayFS::new(base, agentfs.fs) + OverlayFS::new(base, vfs.fs) }; let cwd_str = cwd @@ -344,7 +340,7 @@ async fn mount_session_fs( let mount_opts = MountOpts { mountpoint: session.fuse_mountpoint.clone(), backend: Backend::Fuse, - fsname: format!("agentfs:{}", session.run_id), + fsname: format!("vfs:{}", session.run_id), uid: Some(uid), gid: Some(gid), allow_other: system, @@ -436,14 +432,14 @@ fn run_in_existing_session( } let rt = crate::get_runtime(); - let status = rt.block_on(agentfs_mount::supervise::supervise_pid_with_hooks( + let status = rt.block_on(vfs_mount::supervise::supervise_pid_with_hooks( child_pid, - agentfs_mount::supervise::SuperviseOpts::default(), - agentfs_mount::supervise::SuperviseHooks::with_profile_checkpoint( + vfs_mount::supervise::SuperviseOpts::default(), + vfs_mount::supervise::SuperviseHooks::with_profile_checkpoint( crate::profiling::report_checkpoint, ), ))?; - let exit_code = agentfs_mount::supervise::exit_code_for_status(status); + let exit_code = vfs_mount::supervise::exit_code_for_status(status); // Clean up proc file crate::cmd::ps::remove_proc_file(session_id); @@ -455,7 +451,7 @@ fn run_in_existing_session( /// Print the welcome banner showing sandbox configuration. fn print_welcome_banner(cwd: &Path, allowed_paths: &[PathBuf], session_id: &str, encrypted: bool) { - eprintln!("Welcome to AgentFS!"); + eprintln!("Welcome to Vfs!"); eprintln!(); eprintln!("The following directories are writable:"); eprintln!(); @@ -472,7 +468,7 @@ fn print_welcome_banner(cwd: &Path, allowed_paths: &[PathBuf], session_id: &str, eprintln!(); eprintln!("To join this session from another terminal:"); eprintln!(); - eprintln!(" agentfs run --session {} ", session_id); + eprintln!(" vfs run --session {} ", session_id); eprintln!(); } @@ -495,7 +491,7 @@ struct RunSession { fn setup_run_directory(session_id: Option) -> Result { let run_id = session_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); let home_dir = dirs::home_dir().context("Failed to get home directory")?; - let run_dir = home_dir.join(".agentfs").join("run").join(&run_id); + let run_dir = home_dir.join(".vfs").join("run").join(&run_id); std::fs::create_dir_all(&run_dir).context("Failed to create run directory")?; let db_path = run_dir.join("delta.db"); @@ -652,7 +648,7 @@ fn run_child( pipe_from_parent: libc::c_int, pipe_to_parent: libc::c_int, ) -> ! { - if let Err(error) = agentfs_mount::supervise::prepare_forked_child(true) { + if let Err(error) = vfs_mount::supervise::prepare_forked_child(true) { child_exit(&format!("Failed to prepare supervised child: {error}")); } @@ -709,7 +705,7 @@ fn run_child( if !is_mountpoint(fuse_mountpoint) { child_exit(&format!( "FUSE overlay never appeared at {} inside the sandbox mount namespace.\n\ - agentfs run requires shared mount propagation on / (the systemd default);\n\ + vfs run requires shared mount propagation on / (the systemd default);\n\ this host appears to use private propagation (check with\n\ `findmnt -o TARGET,PROPAGATION /`). Enable it with `mount --make-rshared /`\n\ and retry.", @@ -1144,11 +1140,11 @@ async fn run_parent(child_pid: i32, mount_handle: MountHandle, session_id: &str) // Get mountpoint before dropping handle let fuse_mountpoint = mount_handle.mountpoint().to_path_buf(); - let status = agentfs_mount::supervise::run_supervised_pid_with_hooks( + let status = vfs_mount::supervise::run_supervised_pid_with_hooks( mount_handle, child_pid, - agentfs_mount::supervise::SuperviseOpts::default(), - agentfs_mount::supervise::SuperviseHooks::with_profile_checkpoint( + vfs_mount::supervise::SuperviseOpts::default(), + vfs_mount::supervise::SuperviseHooks::with_profile_checkpoint( crate::profiling::report_checkpoint, ), ) @@ -1175,14 +1171,14 @@ async fn run_parent(child_pid: i32, mount_handle: MountHandle, session_id: &str) eprintln!("Session: {}", session_id); eprintln!(); eprintln!("To resume this session:"); - eprintln!(" agentfs run --session {}", session_id); + eprintln!(" vfs run --session {}", session_id); eprintln!(); eprintln!("To see what changed:"); - eprintln!(" agentfs diff {}", session_id); + eprintln!(" vfs diff {}", session_id); crate::profiling::emit_cli_report(); - Ok(agentfs_mount::supervise::exit_code_for_status(status?)) + Ok(vfs_mount::supervise::exit_code_for_status(status?)) } /// Execute the command, replacing the current process. @@ -1225,8 +1221,7 @@ fn exec_command(command: PathBuf, args: Vec, session_id: &str) -> ! { let error = std::io::Error::last_os_error(); child_exit_with_code( &format!("Error: Failed to execute {}: {}", command.display(), error), - agentfs_mount::supervise::exit_code_for_spawn_error(&error) - .unwrap_or(EXIT_COMMAND_NOT_FOUND), + vfs_mount::supervise::exit_code_for_spawn_error(&error).unwrap_or(EXIT_COMMAND_NOT_FOUND), ); } @@ -1236,9 +1231,9 @@ fn setup_env_vars(session_id: &str) { // sandboxed command must see the user's TMPDIR instead (inside the // namespace /tmp is a fresh scoping tmpfs anyway). crate::config::restore_original_tmpdir_env(); - std::env::set_var("AGENTFS", "1"); - std::env::set_var("AGENTFS_SANDBOX", "linux-namespace"); - std::env::set_var("AGENTFS_SESSION", session_id); + std::env::set_var("VFS", "1"); + std::env::set_var("VFS_SANDBOX", "linux-namespace"); + std::env::set_var("VFS_SESSION", session_id); std::env::set_var("PS1", "🤖 \\u@\\h:\\w\\$ "); // Configure SSH to skip system config files. diff --git a/crates/agentfs-cli/src/cmd/run/mod.rs b/crates/vfs-cli/src/cmd/run/mod.rs similarity index 100% rename from crates/agentfs-cli/src/cmd/run/mod.rs rename to crates/vfs-cli/src/cmd/run/mod.rs diff --git a/crates/agentfs-cli/src/cmd/run/not_supported.rs b/crates/vfs-cli/src/cmd/run/not_supported.rs similarity index 100% rename from crates/agentfs-cli/src/cmd/run/not_supported.rs rename to crates/vfs-cli/src/cmd/run/not_supported.rs diff --git a/crates/agentfs-cli/src/cmd/run/tests.rs b/crates/vfs-cli/src/cmd/run/tests.rs similarity index 95% rename from crates/agentfs-cli/src/cmd/run/tests.rs rename to crates/vfs-cli/src/cmd/run/tests.rs index ea8c7fa0..e598522a 100644 --- a/crates/agentfs-cli/src/cmd/run/tests.rs +++ b/crates/vfs-cli/src/cmd/run/tests.rs @@ -161,7 +161,7 @@ mod darwin_read_scoping { fn config() -> SandboxConfig { SandboxConfig { - mountpoint: PathBuf::from("/Users/tester/.agentfs/run/sess-1/mnt"), + mountpoint: PathBuf::from("/Users/tester/.vfs/run/sess-1/mnt"), allow_paths: vec![ PathBuf::from("/Users/tester/.codex"), PathBuf::from("/Users/tester/.claude.json"), @@ -232,8 +232,8 @@ mod darwin_read_scoping { let profile = generate_sandbox_profile(&config()); for path in [ - "/Users/tester/.agentfs/run/sess-1/mnt", - "/Users/tester/.agentfs/run/sess-1", + "/Users/tester/.vfs/run/sess-1/mnt", + "/Users/tester/.vfs/run/sess-1", "/Users/tester/.codex", "/Users/tester/.claude.json", ] { @@ -255,9 +255,9 @@ mod darwin_read_scoping { for parent in [ "/Users", "/Users/tester", - "/Users/tester/.agentfs", - "/Users/tester/.agentfs/run", - "/Users/tester/.agentfs/run/sess-1", + "/Users/tester/.vfs", + "/Users/tester/.vfs/run", + "/Users/tester/.vfs/run/sess-1", ] { let name = param_for(&profile, parent); let rule = format!( @@ -309,8 +309,8 @@ mod darwin_read_scoping { fn write_scoping_is_unchanged() { let profile = generate_sandbox_profile(&config()); - let mountpoint = param_for(&profile, "/Users/tester/.agentfs/run/sess-1/mnt"); - let run_dir = param_for(&profile, "/Users/tester/.agentfs/run/sess-1"); + let mountpoint = param_for(&profile, "/Users/tester/.vfs/run/sess-1/mnt"); + let run_dir = param_for(&profile, "/Users/tester/.vfs/run/sess-1"); let codex = param_for(&profile, "/Users/tester/.codex"); for rule in [ format!(r#"(allow file-write* (subpath (param "{mountpoint}")))"#), @@ -375,7 +375,7 @@ mod darwin_read_scoping { assert!( profile.policy.contains( - r#"(deny default (with message "agentfs-evilallowfile-readdenysignalx: access denied"))"# + r#"(deny default (with message "vfs-evilallowfile-readdenysignalx: access denied"))"# ), "session id must be reduced to a conservative charset:\n{}", profile.policy @@ -399,12 +399,12 @@ mod darwin_read_scoping { assert!( profile .policy - .contains(r#"(deny default (with message "agentfs-session: access denied"))"#), + .contains(r#"(deny default (with message "vfs-session: access denied"))"#), "a session id sanitized to nothing must fall back to a fixed tag:\n{}", profile.policy ); assert!( - !profile.policy.contains("agentfs-:"), + !profile.policy.contains("vfs-:"), "the log tag must never be empty:\n{}", profile.policy ); diff --git a/crates/agentfs-cli/src/cmd/safety.rs b/crates/vfs-cli/src/cmd/safety.rs similarity index 96% rename from crates/agentfs-cli/src/cmd/safety.rs rename to crates/vfs-cli/src/cmd/safety.rs index 3e1fdfbb..6512ad41 100644 --- a/crates/agentfs-cli/src/cmd/safety.rs +++ b/crates/vfs-cli/src/cmd/safety.rs @@ -1,6 +1,5 @@ -//! Production safety commands for local AgentFS databases. +//! Production safety commands for local Vfs databases. -use agentfs_core::{schema::integrity, AgentFSOptions}; use anyhow::{Context, Result as AnyhowResult}; use std::collections::BTreeMap; use std::fs; @@ -8,6 +7,7 @@ use std::io::{Read, Seek, SeekFrom, Write}; use std::path::{Component, Path, PathBuf}; use turso::transaction::{Transaction, TransactionBehavior}; use turso::{Builder, Connection, EncryptionOpts, Value}; +use vfs_core::{schema::integrity, VfsOptions}; const S_IFMT: i64 = 0o170000; const S_IFREG: i64 = 0o100000; @@ -28,7 +28,7 @@ struct PartialOriginRow { base_ctime_nsec: i64, } -/// Run integrity and schema-invariant checks for a local AgentFS database. +/// Run integrity and schema-invariant checks for a local Vfs database. pub async fn handle_integrity_command( stdout: &mut impl Write, id_or_path: String, @@ -113,7 +113,7 @@ async fn check_integrity_readonly( Ok(report) } -/// Create a portable main-database backup of a local AgentFS database. +/// Create a portable main-database backup of a local Vfs database. pub async fn handle_backup_command( stdout: &mut impl Write, id_or_path: String, @@ -185,7 +185,7 @@ pub async fn handle_backup_command( Ok(()) } -/// Create a portable materialized copy of a local AgentFS database. +/// Create a portable materialized copy of a local Vfs database. pub async fn handle_materialize_command( stdout: &mut impl Write, id_or_path: String, @@ -847,7 +847,7 @@ fn write_integrity_report( } fn resolve_local_db_path(id_or_path: &str) -> AnyhowResult { - let options = AgentFSOptions::resolve(id_or_path)?; + let options = VfsOptions::resolve(id_or_path)?; let db_path = options .db_path() .context("Failed to resolve database path")?; @@ -1028,10 +1028,10 @@ fn value_i64(value: Value) -> AnyhowResult { #[cfg(test)] mod tests { use super::*; - use agentfs_core::{AgentFS, AgentFSOptions}; use serde_json::Value as JsonValue; + use vfs_core::{Vfs, VfsOptions}; - async fn write_agent_file(agent: &AgentFS, path: &str, data: &[u8]) { + async fn write_agent_file(agent: &Vfs, path: &str, data: &[u8]) { let (_, file) = agent .fs .create_file(path, (S_IFREG | 0o644) as u32, 0, 0) @@ -1058,7 +1058,7 @@ mod tests { let source = temp_dir.path().join("source.db"); fs::write(&source, b"stub").unwrap(); - let bare = format!("agentfs-backup-target-{}.db", std::process::id()); + let bare = format!("vfs-backup-target-{}.db", std::process::id()); ensure_backup_target(&source, Path::new(&bare)).expect( "a bare relative backup target must resolve its parent to the current directory", ); @@ -1083,7 +1083,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let db_path = temp_dir.path().join("valid.db"); { - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())) .await .unwrap(); write_agent_file(&agent, "/hello.txt", b"hello").await; @@ -1110,7 +1110,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let db_path = temp_dir.path().join("read-only.db"); { - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())) .await .unwrap(); write_agent_file(&agent, "/hello.txt", b"hello").await; @@ -1152,7 +1152,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let db_path = temp_dir.path().join("hard-corrupt.db"); { - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())) .await .unwrap(); write_agent_file(&agent, "/hello.txt", b"hello").await; @@ -1188,7 +1188,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let db_path = temp_dir.path().join("stale-shm.db"); { - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())) .await .unwrap(); write_agent_file(&agent, "/hello.txt", b"hello").await; @@ -1246,7 +1246,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let db_path = temp_dir.path().join("single-file.db"); { - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())) .await .unwrap(); write_agent_file(&agent, "/hello.txt", b"hello").await; @@ -1284,7 +1284,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let db_path = temp_dir.path().join("corrupt.db"); { - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())) .await .unwrap(); write_agent_file(&agent, "/bad.txt", b"bad").await; @@ -1333,7 +1333,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let db_path = temp_dir.path().join("orphan.db"); { - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())) .await .unwrap(); let conn = agent.get_connection().await.unwrap(); @@ -1375,7 +1375,7 @@ mod tests { let target = temp_dir.path().join("backup.db"); let large = vec![7_u8; 128 * 1024 + 3]; { - let agent = AgentFS::open(AgentFSOptions::with_path(source.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(source.to_string_lossy())) .await .unwrap(); write_agent_file(&agent, "/small.txt", b"small").await; @@ -1395,7 +1395,7 @@ mod tests { .unwrap(); assert!(target.is_file()); - let backup = AgentFS::open(AgentFSOptions::with_path(target.to_string_lossy())) + let backup = Vfs::open(VfsOptions::with_path(target.to_string_lossy())) .await .unwrap(); assert_eq!( @@ -1418,9 +1418,8 @@ mod tests { let key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; let cipher = "aegis256"; { - let agent = AgentFS::open( - AgentFSOptions::with_path(source.to_string_lossy()) - .with_encryption_key(key, cipher), + let agent = Vfs::open( + VfsOptions::with_path(source.to_string_lossy()).with_encryption_key(key, cipher), ) .await .unwrap(); @@ -1455,8 +1454,8 @@ mod tests { .await .unwrap(); - let backup = AgentFS::open( - AgentFSOptions::with_path(target.to_string_lossy()).with_encryption_key(key, cipher), + let backup = Vfs::open( + VfsOptions::with_path(target.to_string_lossy()).with_encryption_key(key, cipher), ) .await .unwrap(); @@ -1472,7 +1471,7 @@ mod tests { let source = temp_dir.path().join("partial.db"); let target = temp_dir.path().join("partial-backup.db"); { - let agent = AgentFS::open(AgentFSOptions::with_path(source.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(source.to_string_lossy())) .await .unwrap(); let conn = agent.get_connection().await.unwrap(); @@ -1562,7 +1561,7 @@ mod tests { let fingerprint = metadata_fingerprint(&fs::metadata(&base_file).unwrap()); let expected = b"abcdWXYZij".to_vec(); - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())) .await .unwrap(); let conn = agent.get_connection().await.unwrap(); @@ -1639,7 +1638,7 @@ mod tests { assert_eq!(partial_table_count(db_path, "fs_partial_origin").await, 0); assert_eq!(partial_table_count(db_path, "fs_chunk_override").await, 0); - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())) + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())) .await .unwrap(); assert_eq!( diff --git a/crates/agentfs-cli/src/cmd/sync.rs b/crates/vfs-cli/src/cmd/sync.rs similarity index 77% rename from crates/agentfs-cli/src/cmd/sync.rs rename to crates/vfs-cli/src/cmd/sync.rs index deb3e6fc..4ed8484c 100644 --- a/crates/agentfs-cli/src/cmd/sync.rs +++ b/crates/vfs-cli/src/cmd/sync.rs @@ -1,12 +1,12 @@ -use agentfs_core::AgentFSOptions; +use vfs_core::VfsOptions; -use crate::cmd::init::open_agentfs; +use crate::cmd::init::open_vfs; pub async fn handle_pull_command(id_or_path: String) -> anyhow::Result<()> { - let options = AgentFSOptions::resolve(&id_or_path)?; + let options = VfsOptions::resolve(&id_or_path)?; eprintln!("Using agent: {}", id_or_path); - let agent = open_agentfs(options) + let agent = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; agent.pull().await?; @@ -15,10 +15,10 @@ pub async fn handle_pull_command(id_or_path: String) -> anyhow::Result<()> { } pub async fn handle_push_command(id_or_path: String) -> anyhow::Result<()> { - let options = AgentFSOptions::resolve(&id_or_path)?; + let options = VfsOptions::resolve(&id_or_path)?; eprintln!("Using agent: {}", id_or_path); - let agent = open_agentfs(options) + let agent = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; agent.push().await?; @@ -27,10 +27,10 @@ pub async fn handle_push_command(id_or_path: String) -> anyhow::Result<()> { } pub async fn handle_checkpoint_command(id_or_path: String) -> anyhow::Result<()> { - let options = AgentFSOptions::resolve(&id_or_path)?; + let options = VfsOptions::resolve(&id_or_path)?; eprintln!("Using agent: {}", id_or_path); - let agent = open_agentfs(options) + let agent = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; agent.checkpoint().await?; @@ -42,10 +42,10 @@ pub async fn handle_stats_command( stdout: &mut impl std::io::Write, id_or_path: String, ) -> anyhow::Result<()> { - let options = AgentFSOptions::resolve(&id_or_path)?; + let options = VfsOptions::resolve(&id_or_path)?; eprintln!("Using agent: {}", id_or_path); - let agent = open_agentfs(options) + let agent = open_vfs(options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, &id_or_path))?; let result: anyhow::Result<()> = async { diff --git a/crates/agentfs-cli/src/cmd/timeline.rs b/crates/vfs-cli/src/cmd/timeline.rs similarity index 84% rename from crates/agentfs-cli/src/cmd/timeline.rs rename to crates/vfs-cli/src/cmd/timeline.rs index 64e9aa9d..c3a4bea7 100644 --- a/crates/agentfs-cli/src/cmd/timeline.rs +++ b/crates/vfs-cli/src/cmd/timeline.rs @@ -1,10 +1,10 @@ -use agentfs_core::{toolcalls::ToolCall, AgentFSOptions, ToolCalls}; use anyhow::{Context, Result as AnyhowResult}; use chrono::TimeZone; use std::io::Write; use std::str::FromStr; +use vfs_core::{toolcalls::ToolCall, ToolCalls, VfsOptions}; -use crate::cmd::init::{finalize_readonly, open_agentfs}; +use crate::cmd::init::{finalize_readonly, open_vfs}; /// Output format for timeline display #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -40,23 +40,23 @@ pub async fn show_timeline( id_or_path: &str, options: &TimelineOptions, ) -> AnyhowResult<()> { - let agent_options = AgentFSOptions::resolve(id_or_path)?; + let agent_options = VfsOptions::resolve(id_or_path)?; - let agentfs = open_agentfs(agent_options) + let vfs = open_vfs(agent_options) .await .map_err(|err| super::migrate::open_error_with_guidance(err, id_or_path))?; - let result = show_timeline_opened(stdout, &agentfs, options).await; - finalize_readonly(&agentfs).await; + let result = show_timeline_opened(stdout, &vfs, options).await; + finalize_readonly(&vfs).await; result } async fn show_timeline_opened( stdout: &mut impl Write, - agentfs: &agentfs_core::AgentFS, + vfs: &vfs_core::Vfs, options: &TimelineOptions, ) -> AnyhowResult<()> { - let toolcalls = ToolCalls::from_pool(agentfs.get_pool()) + let toolcalls = ToolCalls::from_pool(vfs.get_pool()) .await .context("Failed to create tool calls tracker")?; @@ -150,16 +150,16 @@ fn format_json(stdout: &mut impl Write, calls: &[ToolCall]) -> AnyhowResult<()> #[cfg(test)] mod tests { use super::*; - use agentfs_core::{AgentFS, AgentFSOptions}; use tempfile::NamedTempFile; + use vfs_core::{Vfs, VfsOptions}; - async fn create_test_agentfs() -> (AgentFS, String, NamedTempFile) { + async fn create_test_vfs() -> (Vfs, String, NamedTempFile) { let file = NamedTempFile::new().unwrap(); let path = file.path().to_str().unwrap(); - let agentfs = AgentFS::open(AgentFSOptions::with_path(path.to_string())) + let vfs = Vfs::open(VfsOptions::with_path(path.to_string())) .await .unwrap(); - (agentfs, file.path().to_str().unwrap().to_string(), file) + (vfs, file.path().to_str().unwrap().to_string(), file) } /// Create default TimelineOptions for testing @@ -174,7 +174,7 @@ mod tests { #[tokio::test] async fn test_timeline_empty() { - let (_agentfs, path, _file) = create_test_agentfs().await; + let (_vfs, path, _file) = create_test_vfs().await; let mut buf = Vec::new(); show_timeline(&mut buf, &path, &default_options()) @@ -187,9 +187,9 @@ mod tests { #[tokio::test] async fn test_timeline_table_format() { - let (agentfs, path, _file) = create_test_agentfs().await; + let (vfs, path, _file) = create_test_vfs().await; - agentfs.tools.start("test_tool", None).await.unwrap(); + vfs.tools.start("test_tool", None).await.unwrap(); let mut buf = Vec::new(); show_timeline(&mut buf, &path, &default_options()) @@ -205,10 +205,10 @@ mod tests { #[tokio::test] async fn test_timeline_filter_by_name() { - let (agentfs, path, _file) = create_test_agentfs().await; + let (vfs, path, _file) = create_test_vfs().await; - agentfs.tools.start("tool_a", None).await.unwrap(); - agentfs.tools.start("tool_b", None).await.unwrap(); + vfs.tools.start("tool_a", None).await.unwrap(); + vfs.tools.start("tool_b", None).await.unwrap(); let mut buf = Vec::new(); let options = TimelineOptions { @@ -235,22 +235,21 @@ mod tests { #[tokio::test] async fn test_timeline_filter_by_status() { - let (agentfs, path, _file) = create_test_agentfs().await; + let (vfs, path, _file) = create_test_vfs().await; // Success call - let success_id = agentfs.tools.start("test_tool", None).await.unwrap(); - agentfs - .tools + let success_id = vfs.tools.start("test_tool", None).await.unwrap(); + vfs.tools .success(success_id, Some(serde_json::json!({"success": true}))) .await .unwrap(); // Error call - let error_id = agentfs.tools.start("test_tool", None).await.unwrap(); - agentfs.tools.error(error_id, "test error").await.unwrap(); + let error_id = vfs.tools.start("test_tool", None).await.unwrap(); + vfs.tools.error(error_id, "test error").await.unwrap(); // Pending call - agentfs.tools.start("test_tool", None).await.unwrap(); + vfs.tools.start("test_tool", None).await.unwrap(); // Test status="success" returns only successful calls let mut buf = Vec::new(); @@ -294,15 +293,11 @@ mod tests { #[tokio::test] async fn test_timeline_limit() { - let (agentfs, path, _file) = create_test_agentfs().await; + let (vfs, path, _file) = create_test_vfs().await; // Record 5 tool calls for i in 0..5 { - agentfs - .tools - .start(&format!("tool_{}", i), None) - .await - .unwrap(); + vfs.tools.start(&format!("tool_{}", i), None).await.unwrap(); } // Test limit=2 returns exactly 2 calls @@ -323,9 +318,9 @@ mod tests { #[tokio::test] async fn test_timeline_leaves_no_wal_sidecar() { - let (agentfs, path, _file) = create_test_agentfs().await; - agentfs.tools.start("test_tool", None).await.unwrap(); - drop(agentfs); + let (vfs, path, _file) = create_test_vfs().await; + vfs.tools.start("test_tool", None).await.unwrap(); + drop(vfs); let mut buf = Vec::new(); show_timeline(&mut buf, &path, &default_options()) @@ -349,11 +344,10 @@ mod tests { #[tokio::test] async fn test_timeline_started_at_units_consistent() { - let (agentfs, path, _file) = create_test_agentfs().await; + let (vfs, path, _file) = create_test_vfs().await; // tool_calls stores milliseconds; both display forms must expose the // same second-granularity value. - agentfs - .tools + vfs.tools .record( "clock", 1_600_000_000_000, @@ -389,11 +383,10 @@ mod tests { #[tokio::test] async fn test_timeline_truncate_long_names() { - let (agentfs, path, _file) = create_test_agentfs().await; + let (vfs, path, _file) = create_test_vfs().await; // Create a tool call with a very long name (>20 chars) - agentfs - .tools + vfs.tools .start("very_long_tool_name_that_exceeds_twenty_characters", None) .await .unwrap(); diff --git a/crates/agentfs-cli/src/config.rs b/crates/vfs-cli/src/config.rs similarity index 93% rename from crates/agentfs-cli/src/config.rs rename to crates/vfs-cli/src/config.rs index f8195a0b..5c03f08f 100644 --- a/crates/agentfs-cli/src/config.rs +++ b/crates/vfs-cli/src/config.rs @@ -1,16 +1,16 @@ //! CLI-owned runtime config assembly. -use agentfs_core::{CoreConfig, EnvReader}; use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::sync::{Mutex, OnceLock}; +use vfs_core::{CoreConfig, EnvReader}; -const CLONE_TIMINGS_ENV: &str = "AGENTFS_CLONE_TIMINGS"; +const CLONE_TIMINGS_ENV: &str = "VFS_CLONE_TIMINGS"; const SHELL_ENV: &str = "SHELL"; const TURSO_AUTH_TOKEN_ENV: &str = "TURSO_DB_AUTH_TOKEN"; #[cfg(target_os = "linux")] -const FUSE_WRITEBACK_ENV: &str = "AGENTFS_FUSE_WRITEBACK"; +const FUSE_WRITEBACK_ENV: &str = "VFS_FUSE_WRITEBACK"; pub(crate) const DEFAULT_CLONE_TIMINGS_ENABLED: bool = false; @@ -49,7 +49,7 @@ pub(crate) fn turso_db_auth_token() -> Option { // of `run`/`exec` must NOT inherit that override: the snapshot below is // restored into their environment by the spawn paths. -const SPILL_DIR_PREFIX: &str = "agentfs-spill-"; +const SPILL_DIR_PREFIX: &str = "vfs-spill-"; struct SpillDir { owner_pid: u32, @@ -175,7 +175,7 @@ fn reap_stale_spill_dirs(base: &Path) { } } -/// Parse the owner PID out of a `agentfs-spill--` directory name. +/// Parse the owner PID out of a `vfs-spill--` directory name. fn stale_spill_dir_pid(name: &str) -> Option { let rest = name.strip_prefix(SPILL_DIR_PREFIX)?; let (pid, suffix) = rest.split_once('-')?; @@ -266,12 +266,12 @@ mod tests { #[test] fn stale_spill_dir_pid_parses_only_generated_names() { - assert_eq!(stale_spill_dir_pid("agentfs-spill-1234-0"), Some(1234)); - assert_eq!(stale_spill_dir_pid("agentfs-spill-1234-15"), Some(1234)); - assert_eq!(stale_spill_dir_pid("agentfs-spill-1234"), None); - assert_eq!(stale_spill_dir_pid("agentfs-spill-abc-0"), None); - assert_eq!(stale_spill_dir_pid("agentfs-spill-1234-x"), None); - assert_eq!(stale_spill_dir_pid("agentfs-spill--0"), None); + assert_eq!(stale_spill_dir_pid("vfs-spill-1234-0"), Some(1234)); + assert_eq!(stale_spill_dir_pid("vfs-spill-1234-15"), Some(1234)); + assert_eq!(stale_spill_dir_pid("vfs-spill-1234"), None); + assert_eq!(stale_spill_dir_pid("vfs-spill-abc-0"), None); + assert_eq!(stale_spill_dir_pid("vfs-spill-1234-x"), None); + assert_eq!(stale_spill_dir_pid("vfs-spill--0"), None); assert_eq!(stale_spill_dir_pid("tursodb-ephemeral-1"), None); } @@ -283,7 +283,7 @@ mod tests { let alive = base .path() .join(format!("{SPILL_DIR_PREFIX}{}-0", std::process::id())); - let unrelated = base.path().join("agentfs-spill-notes"); + let unrelated = base.path().join("vfs-spill-notes"); std::fs::create_dir(&dead).unwrap(); std::fs::create_dir(&alive).unwrap(); std::fs::create_dir(&unrelated).unwrap(); diff --git a/crates/agentfs-cli/src/docs.rs b/crates/vfs-cli/src/docs.rs similarity index 94% rename from crates/agentfs-cli/src/docs.rs rename to crates/vfs-cli/src/docs.rs index 26fe914b..b859bfe0 100644 --- a/crates/agentfs-cli/src/docs.rs +++ b/crates/vfs-cli/src/docs.rs @@ -1,18 +1,18 @@ //! Generated clap <-> MANUAL command-reference parity. //! //! The command/option sections of docs/MANUAL.md are rendered from the clap -//! command tree in `opts`, so the manual cannot drift from `agentfs --help`. +//! command tree in `opts`, so the manual cannot drift from `vfs --help`. //! The checked-in manual is generated from the Linux CLI surface (the //! first-tier platform); `docs::tests::manual_help_parity` fails when the -//! generated region is stale and rewrites it under `AGENTFS_UPDATE_MANUAL=1`. +//! generated region is stale and rewrites it under `VFS_UPDATE_MANUAL=1`. use clap::{ArgAction, CommandFactory}; use crate::opts::Args; #[cfg(all(test, target_os = "linux"))] -const MANUAL_REGEN_ENV: &str = "AGENTFS_UPDATE_MANUAL"; -const MANUAL_REGEN_COMMAND: &str = "AGENTFS_UPDATE_MANUAL=1 cargo +nightly test -p agentfs-cli --lib docs::tests::manual_help_parity -- --exact"; +const MANUAL_REGEN_ENV: &str = "VFS_UPDATE_MANUAL"; +const MANUAL_REGEN_COMMAND: &str = "VFS_UPDATE_MANUAL=1 cargo +nightly test -p vfs-cli --lib docs::tests::manual_help_parity -- --exact"; const GENERATED_BEGIN: &str = ""; const GENERATED_END: &str = ""; @@ -29,7 +29,7 @@ fn generated_manual_commands() -> String { out.push_str("## Commands\n\n"); out.push_str( "Every section below is generated from the clap definitions the binary \ - actually parses; `agentfs --help` and this reference cannot \ + actually parses; `vfs --help` and this reference cannot \ disagree.\n", ); for sub in visible_subcommands(&cmd) { @@ -56,8 +56,8 @@ struct CommandContext { impl CommandContext { fn root() -> Self { Self { - path: "agentfs".to_string(), - invocation: "agentfs".to_string(), + path: "vfs".to_string(), + invocation: "vfs".to_string(), } } @@ -305,7 +305,7 @@ mod tests { let generated = generated_manual_commands(); let cmd = Args::command(); let mut missing = Vec::new(); - collect_paths(&cmd, "agentfs", &mut |path| { + collect_paths(&cmd, "vfs", &mut |path| { let heading = format!(" {path}\n"); if !generated.contains(&heading) { missing.push(path.to_string()); @@ -330,7 +330,7 @@ mod tests { Path::new(env!("CARGO_MANIFEST_DIR")) .parent() .and_then(Path::parent) - .expect("agentfs-cli crate should live two levels below repo root") + .expect("vfs-cli crate should live two levels below repo root") .to_path_buf() } } diff --git a/crates/agentfs-cli/src/knobs.rs b/crates/vfs-cli/src/knobs.rs similarity index 87% rename from crates/agentfs-cli/src/knobs.rs rename to crates/vfs-cli/src/knobs.rs index 2bc4ba04..6bcf0cdd 100644 --- a/crates/agentfs-cli/src/knobs.rs +++ b/crates/vfs-cli/src/knobs.rs @@ -4,19 +4,19 @@ //! core config, the typed FUSE adapter config, CLI env-backed options, and the //! first-class partial-origin CLI policy flags. -use agentfs_core::{ +use vfs_core::{ CoreConfig, DEFAULT_PARTIAL_ORIGIN_THRESHOLD_BYTES, DEFAULT_WRITE_BATCH_BYTES, DEFAULT_WRITE_BATCH_GLOBAL_BYTES, DEFAULT_WRITE_BATCH_MS, DEFAULT_WRITE_BATCH_TXN_BYTES, DEFAULT_WRITE_BATCH_TXN_INODES, }; -use agentfs_core::telemetry::DEFAULT_PROFILE_ENABLED; +use vfs_core::telemetry::DEFAULT_PROFILE_ENABLED; use crate::config::DEFAULT_CLONE_TIMINGS_ENABLED; -// The runtime FUSE config lives in the sealed agentfs-fuse crate while the +// The runtime FUSE config lives in the sealed vfs-fuse crate while the // knob ledger lives at the CLI edge. Keep these defaults byte-for-byte aligned -// with agentfs_fuse::adapter::config; the scan roots below cover every +// with vfs_fuse::adapter::config; the scan roots below cover every // workspace crate so undeclared runtime env reads cannot bypass the ledger. #[cfg(target_os = "linux")] #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -249,11 +249,11 @@ fn render_bool(value: bool) -> &'static str { } } -fn partial_origin_mode_as_str(mode: agentfs_core::PartialOriginMode) -> &'static str { +fn partial_origin_mode_as_str(mode: vfs_core::PartialOriginMode) -> &'static str { match mode { - agentfs_core::PartialOriginMode::Off => "off", - agentfs_core::PartialOriginMode::On => "on", - agentfs_core::PartialOriginMode::Auto => "auto", + vfs_core::PartialOriginMode::Off => "off", + vfs_core::PartialOriginMode::On => "on", + vfs_core::PartialOriginMode::Auto => "auto", } } @@ -334,18 +334,18 @@ impl Knob { } } -const CORE_OWNER: &str = "agentfs-core config"; +const CORE_OWNER: &str = "vfs-core config"; #[cfg(target_os = "linux")] -const FUSE_OWNER: &str = "agentfs FUSE config"; -const CLI_OWNER: &str = "agentfs CLI edge"; +const FUSE_OWNER: &str = "vfs FUSE config"; +const CLI_OWNER: &str = "vfs CLI edge"; #[cfg(all(test, target_os = "linux"))] -const KNOBS_DOC_REGEN_ENV: &str = "AGENTFS_UPDATE_KNOBS"; -const KNOBS_DOC_REGEN_COMMAND: &str = "AGENTFS_UPDATE_KNOBS=1 cargo +nightly test -p agentfs-cli --lib knobs::tests::generated_knobs_doc_matches_declarations -- --exact"; +const KNOBS_DOC_REGEN_ENV: &str = "VFS_UPDATE_KNOBS"; +const KNOBS_DOC_REGEN_COMMAND: &str = "VFS_UPDATE_KNOBS=1 cargo +nightly test -p vfs-cli --lib knobs::tests::generated_knobs_doc_matches_declarations -- --exact"; #[cfg(target_os = "linux")] const LINUX_FUSE_KNOBS: &[Knob] = &[ Knob::product( - "AGENTFS_FUSE_WORKERS", + "VFS_FUSE_WORKERS", "env", DefaultValue::FuseWorkers, FUSE_OWNER, @@ -353,23 +353,23 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "fuse_config::tests::workers_config_feeds_dispatch_and_cache_policy", ), Knob::product( - "AGENTFS_FUSE_CPU_PERCENT", + "VFS_FUSE_CPU_PERCENT", "env", DefaultValue::FuseAutoPercent, FUSE_OWNER, - "CPU budget percentage used when AGENTFS_FUSE_WORKERS=auto.", + "CPU budget percentage used when VFS_FUSE_WORKERS=auto.", "knobs::tests::knob_defaults_in_docs_match_runtime_defaults", ), Knob::product( - "AGENTFS_FUSE_MEMORY_PERCENT", + "VFS_FUSE_MEMORY_PERCENT", "env", DefaultValue::FuseAutoPercent, FUSE_OWNER, - "Memory budget percentage used when AGENTFS_FUSE_WORKERS=auto.", + "Memory budget percentage used when VFS_FUSE_WORKERS=auto.", "knobs::tests::knob_defaults_in_docs_match_runtime_defaults", ), Knob::product( - "AGENTFS_FUSE_QUEUE", + "VFS_FUSE_QUEUE", "env", DefaultValue::FuseQueue, FUSE_OWNER, @@ -377,7 +377,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "fuse_config::tests::workers_config_feeds_dispatch_and_cache_policy", ), Knob::product( - "AGENTFS_FUSE_QUEUE_MEMORY_PERCENT", + "VFS_FUSE_QUEUE_MEMORY_PERCENT", "env", DefaultValue::FuseQueueMemoryPercent, FUSE_OWNER, @@ -385,7 +385,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "knobs::tests::knob_defaults_in_docs_match_runtime_defaults", ), Knob::product( - "AGENTFS_FUSE_ENTRY_TTL_MS", + "VFS_FUSE_ENTRY_TTL_MS", "env", DefaultValue::FusePositiveTtlMs, FUSE_OWNER, @@ -393,7 +393,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "knobs::tests::knob_defaults_in_docs_match_runtime_defaults", ), Knob::product( - "AGENTFS_FUSE_ATTR_TTL_MS", + "VFS_FUSE_ATTR_TTL_MS", "env", DefaultValue::FusePositiveTtlMs, FUSE_OWNER, @@ -401,7 +401,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "knobs::tests::knob_defaults_in_docs_match_runtime_defaults", ), Knob::product( - "AGENTFS_FUSE_NEG_TTL_MS", + "VFS_FUSE_NEG_TTL_MS", "env", DefaultValue::FuseNegativeTtlMs, FUSE_OWNER, @@ -409,7 +409,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "knobs::tests::knob_defaults_in_docs_match_runtime_defaults", ), Knob::product( - "AGENTFS_FUSE_WRITEBACK", + "VFS_FUSE_WRITEBACK", "env", DefaultValue::FuseWriteback, FUSE_OWNER, @@ -417,7 +417,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "fuse_config::tests::serial_dispatch_disables_kernel_cache_policy", ), Knob::product( - "AGENTFS_FUSE_KEEPCACHE", + "VFS_FUSE_KEEPCACHE", "env", DefaultValue::FuseKeepcache, FUSE_OWNER, @@ -425,7 +425,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "fuse_config::tests::serial_dispatch_disables_kernel_cache_policy", ), Knob::product( - "AGENTFS_FUSE_READDIRPLUS", + "VFS_FUSE_READDIRPLUS", "env", DefaultValue::FuseReaddirPlus, FUSE_OWNER, @@ -433,7 +433,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "fuse_config::tests::invalid_readdirplus_warns_and_defaults_on", ), Knob::product( - "AGENTFS_FUSE_SYNC_INVAL", + "VFS_FUSE_SYNC_INVAL", "env", DefaultValue::FuseSyncInval, FUSE_OWNER, @@ -441,7 +441,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "fuse_config::tests::serial_dispatch_disables_kernel_cache_policy", ), Knob::sunset( - "AGENTFS_FUSE_SELF_INVAL", + "VFS_FUSE_SELF_INVAL", "env", DefaultValue::FuseSelfInval, FUSE_OWNER, @@ -450,7 +450,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "VAL-FUSE-011 and VAL-FUSE-018", ), Knob::sunset( - "AGENTFS_DRAIN_ON_RELEASE", + "VFS_DRAIN_ON_RELEASE", "env", DefaultValue::FuseDrainOnRelease, FUSE_OWNER, @@ -459,7 +459,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "VAL-FUSE-015", ), Knob::sunset( - "AGENTFS_DRAIN_ON_FORGET", + "VFS_DRAIN_ON_FORGET", "env", DefaultValue::FuseDrainOnForget, FUSE_OWNER, @@ -468,7 +468,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "VAL-FUSE-003 and VAL-FUSE-004", ), Knob::sunset( - "AGENTFS_FUSE_FLUSH_INVAL", + "VFS_FUSE_FLUSH_INVAL", "env", DefaultValue::FuseFlushInval, FUSE_OWNER, @@ -477,21 +477,21 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "VAL-FUSE-007 and VAL-FUSE-008", ), Knob::kill_switch( - "AGENTFS_FUSE_NOFLUSH", + "VFS_FUSE_NOFLUSH", DefaultValue::FuseNoflush, FUSE_OWNER, "Disables close-time FLUSH by returning ENOSYS after the kernel has written dirty pages.", "VAL-FUSE-007 default leg and VAL-FUSE-008 off leg", ), Knob::kill_switch( - "AGENTFS_FUSE_NOOPEN", + "VFS_FUSE_NOOPEN", DefaultValue::FuseNoopen, FUSE_OWNER, "Disables per-file OPEN/RELEASE by returning ENOSYS when the kernel supports no-open.", "VAL-FUSE-003 default leg and VAL-FUSE-004 off leg", ), Knob::product( - "AGENTFS_FUSE_INO_FILES_CAP", + "VFS_FUSE_INO_FILES_CAP", "env", DefaultValue::FuseInoFilesCap, FUSE_OWNER, @@ -499,7 +499,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "scripts/validation/noopen-coherence.py", ), Knob::product( - "AGENTFS_FUSE_CACHE_DIR", + "VFS_FUSE_CACHE_DIR", "env", DefaultValue::FuseCacheDir, FUSE_OWNER, @@ -507,7 +507,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "VAL-FUSE-011 and VAL-FUSE-018", ), Knob::sunset( - "AGENTFS_FUSE_STICKY_KEEPCACHE_DROP", + "VFS_FUSE_STICKY_KEEPCACHE_DROP", "env", DefaultValue::FuseStickyKeepcacheDrop, FUSE_OWNER, @@ -516,14 +516,14 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "VAL-FUSE-012 and VAL-FUSE-014", ), Knob::kill_switch( - "AGENTFS_FUSE_URING", + "VFS_FUSE_URING", DefaultValue::FuseUringEnabled, FUSE_OWNER, "FUSE-over-io_uring transport attempt. Set false to force the legacy /dev/fuse path.", "VAL-FUSE-009 uring leg and VAL-FUSE-010 off leg", ), Knob::product( - "AGENTFS_FUSE_URING_DEPTH", + "VFS_FUSE_URING_DEPTH", "env", DefaultValue::FuseUringDepth, FUSE_OWNER, @@ -531,7 +531,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[ "VAL-FUSE-009", ), Knob::sunset( - "AGENTFS_FUSE_URING_SPIN_US", + "VFS_FUSE_URING_SPIN_US", "env", DefaultValue::FuseUringSpinUs, FUSE_OWNER, @@ -546,7 +546,7 @@ const LINUX_FUSE_KNOBS: &[Knob] = &[]; const ACTIVE_COMMON_KNOBS: &[Knob] = &[ Knob::product( - "AGENTFS_KEY", + "VFS_KEY", "env or --key", DefaultValue::Unset, CLI_OWNER, @@ -554,15 +554,15 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ "opts clap env binding", ), Knob::product( - "AGENTFS_CIPHER", + "VFS_CIPHER", "env or --cipher", DefaultValue::Unset, CLI_OWNER, - "Encryption cipher paired with AGENTFS_KEY or --key.", + "Encryption cipher paired with VFS_KEY or --key.", "opts clap env binding", ), Knob::sunset( - "AGENTFS_CLONE_TIMINGS", + "VFS_CLONE_TIMINGS", "env", DefaultValue::CloneTimingsEnabled, CLI_OWNER, @@ -571,7 +571,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ "config::clone_timings_enabled", ), Knob::product( - "AGENTFS_PROFILE", + "VFS_PROFILE", "env", DefaultValue::ProfileEnabled, CORE_OWNER, @@ -579,7 +579,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ "VAL-CONF-011 and VAL-CONF-014", ), Knob::sunset( - "AGENTFS_OVERLAY_READS", + "VFS_OVERLAY_READS", "env", DefaultValue::CoreOverlayReads, CORE_OWNER, @@ -588,7 +588,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ "VAL-CORE-006 and phase8 smoke", ), Knob::product( - "AGENTFS_KEEPCACHE_DELTA", + "VFS_KEEPCACHE_DELTA", "env", DefaultValue::CoreKeepcacheDelta, CORE_OWNER, @@ -596,7 +596,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ "VAL-FUSE-014", ), Knob::product( - "AGENTFS_BATCH_MS", + "VFS_BATCH_MS", "env", DefaultValue::WriteBatchMs, CORE_OWNER, @@ -604,7 +604,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ "sdk write-batcher tests", ), Knob::product( - "AGENTFS_BATCH_BYTES", + "VFS_BATCH_BYTES", "env", DefaultValue::WriteBatchBytes, CORE_OWNER, @@ -612,7 +612,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ "sdk write-batcher tests", ), Knob::product( - "AGENTFS_BATCH_GLOBAL_BYTES", + "VFS_BATCH_GLOBAL_BYTES", "env", DefaultValue::WriteBatchGlobalBytes, CORE_OWNER, @@ -620,7 +620,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ "sdk write-batcher tests", ), Knob::product( - "AGENTFS_BATCH_TXN_INODES", + "VFS_BATCH_TXN_INODES", "env", DefaultValue::WriteBatchTxnInodes, CORE_OWNER, @@ -628,7 +628,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ "sdk write-batcher and import tests", ), Knob::product( - "AGENTFS_BATCH_TXN_BYTES", + "VFS_BATCH_TXN_BYTES", "env", DefaultValue::WriteBatchTxnBytes, CORE_OWNER, @@ -636,7 +636,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ "sdk write-batcher and import tests", ), Knob::sunset( - "AGENTFS_DRAIN_ON_SETATTR", + "VFS_DRAIN_ON_SETATTR", "env", DefaultValue::CoreDrainOnSetattr, CORE_OWNER, @@ -663,7 +663,7 @@ const ACTIVE_COMMON_KNOBS: &[Knob] = &[ ]; const DELETED_COMPAT_KNOBS: &[Knob] = &[Knob::sunset( - concat!("AGENTFS_OVERLAY_", "PARTIAL_ORIGIN"), + concat!("VFS_OVERLAY_", "PARTIAL_ORIGIN"), "deleted env compat", DefaultValue::Removed, CORE_OWNER, @@ -689,8 +689,8 @@ fn deleted_compat_knobs() -> &'static [Knob] { fn generated_knobs_doc() -> String { let active = active_knobs(); let mut out = String::new(); - out.push_str("# AgentFS Runtime Knobs\n\n"); - out.push_str("\n\n"); out.push_str( @@ -791,19 +791,19 @@ mod tests { fn knob_defaults_in_docs_match_runtime_defaults() { let active = active_knobs(); let expected = [ - ("AGENTFS_FUSE_WORKERS", "auto"), - ("AGENTFS_FUSE_QUEUE", "derived"), - ("AGENTFS_FUSE_ENTRY_TTL_MS", "10000"), - ("AGENTFS_FUSE_ATTR_TTL_MS", "10000"), - ("AGENTFS_FUSE_CPU_PERCENT", "50"), - ("AGENTFS_FUSE_MEMORY_PERCENT", "50"), - ("AGENTFS_FUSE_QUEUE_MEMORY_PERCENT", "25"), - ("AGENTFS_BATCH_MS", "5"), - ("AGENTFS_BATCH_BYTES", "4194304"), - ("AGENTFS_BATCH_GLOBAL_BYTES", "67108864"), - ("AGENTFS_BATCH_TXN_INODES", "1024"), - ("AGENTFS_BATCH_TXN_BYTES", "33554432"), - ("AGENTFS_PROFILE", "false"), + ("VFS_FUSE_WORKERS", "auto"), + ("VFS_FUSE_QUEUE", "derived"), + ("VFS_FUSE_ENTRY_TTL_MS", "10000"), + ("VFS_FUSE_ATTR_TTL_MS", "10000"), + ("VFS_FUSE_CPU_PERCENT", "50"), + ("VFS_FUSE_MEMORY_PERCENT", "50"), + ("VFS_FUSE_QUEUE_MEMORY_PERCENT", "25"), + ("VFS_BATCH_MS", "5"), + ("VFS_BATCH_BYTES", "4194304"), + ("VFS_BATCH_GLOBAL_BYTES", "67108864"), + ("VFS_BATCH_TXN_INODES", "1024"), + ("VFS_BATCH_TXN_BYTES", "33554432"), + ("VFS_PROFILE", "false"), ]; for (name, default) in expected { @@ -839,15 +839,15 @@ mod tests { sunset_names.sort_unstable(); println!("sunset knobs: {}", sunset_names.join(", ")); for required in [ - "AGENTFS_CLONE_TIMINGS", - "AGENTFS_FUSE_SELF_INVAL", - "AGENTFS_DRAIN_ON_RELEASE", - "AGENTFS_DRAIN_ON_FORGET", - "AGENTFS_FUSE_FLUSH_INVAL", - "AGENTFS_FUSE_STICKY_KEEPCACHE_DROP", - "AGENTFS_FUSE_URING_SPIN_US", - "AGENTFS_DRAIN_ON_SETATTR", - "AGENTFS_OVERLAY_READS", + "VFS_CLONE_TIMINGS", + "VFS_FUSE_SELF_INVAL", + "VFS_DRAIN_ON_RELEASE", + "VFS_DRAIN_ON_FORGET", + "VFS_FUSE_FLUSH_INVAL", + "VFS_FUSE_STICKY_KEEPCACHE_DROP", + "VFS_FUSE_URING_SPIN_US", + "VFS_DRAIN_ON_SETATTR", + "VFS_OVERLAY_READS", ] { assert!( sunset_names.contains(&required), @@ -860,11 +860,7 @@ mod tests { #[test] fn fuse_kill_switches_are_declared_and_gated() { let active = active_knobs(); - for name in [ - "AGENTFS_FUSE_NOOPEN", - "AGENTFS_FUSE_NOFLUSH", - "AGENTFS_FUSE_URING", - ] { + for name in ["VFS_FUSE_NOOPEN", "VFS_FUSE_NOFLUSH", "VFS_FUSE_URING"] { let knob = find_knob(&active, name); assert_eq!(knob.class, KnobClass::KillSwitch, "{name} class drifted"); assert!( @@ -931,11 +927,11 @@ mod tests { let root = repo_root(); let mut tokens = BTreeSet::new(); for source_root in [ - root.join("crates/agentfs-cli/src"), - root.join("crates/agentfs-core/src"), - root.join("crates/agentfs-fuse/src"), - root.join("crates/agentfs-mount/src"), - root.join("crates/agentfs-nfs/src"), + root.join("crates/vfs-cli/src"), + root.join("crates/vfs-core/src"), + root.join("crates/vfs-fuse/src"), + root.join("crates/vfs-mount/src"), + root.join("crates/vfs-nfs/src"), ] { collect_runtime_env_mentions_from_path(&source_root, &mut tokens); } @@ -973,7 +969,7 @@ mod tests { #[cfg(target_os = "linux")] fn extract_env_tokens(contents: &str) -> BTreeSet { let mut tokens = BTreeSet::new(); - for prefix in ["AGENTFS_", "TURSO_DB_AUTH_TOKEN"] { + for prefix in ["VFS_", "TURSO_DB_AUTH_TOKEN"] { let mut offset = 0; while let Some(index) = contents[offset..].find(prefix) { let start = offset + index; @@ -996,15 +992,15 @@ mod tests { fn ignored_env_token(name: &str) -> bool { matches!( name, - "AGENTFS_VERSION" - | "AGENTFS_SCHEMA_VERSION" - | "AGENTFS_SANDBOX" - | "AGENTFS_SESSION" + "VFS_VERSION" + | "VFS_SCHEMA_VERSION" + | "VFS_SANDBOX" + | "VFS_SESSION" | "TURSO_DB_AUTH_TOKEN" // Doc-regeneration test flag (docs.rs), not a runtime knob; // its knobs.rs sibling is exempt via the knobs.rs file skip. - | "AGENTFS_UPDATE_MANUAL" - ) || name.starts_with("AGENTFS_TEST_") + | "VFS_UPDATE_MANUAL" + ) || name.starts_with("VFS_TEST_") || name.ends_with('_') } @@ -1012,7 +1008,7 @@ mod tests { Path::new(env!("CARGO_MANIFEST_DIR")) .parent() .and_then(Path::parent) - .expect("agentfs-cli crate should live two levels below repo root") + .expect("vfs-cli crate should live two levels below repo root") .to_path_buf() } } diff --git a/crates/agentfs-cli/src/lib.rs b/crates/vfs-cli/src/lib.rs similarity index 86% rename from crates/agentfs-cli/src/lib.rs rename to crates/vfs-cli/src/lib.rs index 2b874aa1..0ea99027 100644 --- a/crates/agentfs-cli/src/lib.rs +++ b/crates/vfs-cli/src/lib.rs @@ -1,9 +1,9 @@ -//! Thin CLI edge for AgentFS. +//! Thin CLI edge for Vfs. //! //! The CLI crate owns argument parsing, user-facing command output, process //! exit/reporting behavior, and runtime config assembly. Filesystem semantics, -//! transport adapters, and mount lifecycle code live in `agentfs-core`, -//! `agentfs-fuse`, `agentfs-nfs`, and `agentfs-mount` respectively. +//! transport adapters, and mount lifecycle code live in `vfs-core`, +//! `vfs-fuse`, `vfs-nfs`, and `vfs-mount` respectively. //! //! Owned invariants: //! diff --git a/crates/agentfs-cli/src/logging.rs b/crates/vfs-cli/src/logging.rs similarity index 67% rename from crates/agentfs-cli/src/logging.rs rename to crates/vfs-cli/src/logging.rs index a09f9551..d193308e 100644 --- a/crates/agentfs-cli/src/logging.rs +++ b/crates/vfs-cli/src/logging.rs @@ -1,16 +1,16 @@ //! CLI logging defaults. //! //! The binary installs the tracing subscriber, but the default filter must -//! cover every first-party AgentFS crate so warnings are visible even when +//! cover every first-party Vfs crate so warnings are visible even when //! `RUST_LOG` is unset. const DEFAULT_ENV_FILTER: &str = concat!( - "agentfs=info,", - "agentfs_cli=info,", - "agentfs_core=info,", - "agentfs_fuse=info,", - "agentfs_nfs=info,", - "agentfs_mount=info" + "vfs=info,", + "vfs_cli=info,", + "vfs_core=info,", + "vfs_fuse=info,", + "vfs_nfs=info,", + "vfs_mount=info" ); pub fn default_env_filter() -> tracing_subscriber::EnvFilter { @@ -47,28 +47,22 @@ mod tests { } #[test] - fn default_env_filter_covers_all_agentfs_crates() { + fn default_env_filter_covers_all_vfs_crates() { let previous = std::env::var_os("RUST_LOG"); std::env::remove_var("RUST_LOG"); let seen = SeenTargets::default(); - let targets = [ - "agentfs_core", - "agentfs_fuse", - "agentfs_nfs", - "agentfs_mount", - "agentfs_cli", - ]; + let targets = ["vfs_core", "vfs_fuse", "vfs_nfs", "vfs_mount", "vfs_cli"]; let subscriber = Registry::default() .with(default_env_filter()) .with(seen.clone()); tracing::dispatcher::with_default(&subscriber.into(), || { - event!(target: "agentfs_core", Level::WARN, "default filter coverage probe"); - event!(target: "agentfs_fuse", Level::WARN, "default filter coverage probe"); - event!(target: "agentfs_nfs", Level::WARN, "default filter coverage probe"); - event!(target: "agentfs_mount", Level::WARN, "default filter coverage probe"); - event!(target: "agentfs_cli", Level::WARN, "default filter coverage probe"); + event!(target: "vfs_core", Level::WARN, "default filter coverage probe"); + event!(target: "vfs_fuse", Level::WARN, "default filter coverage probe"); + event!(target: "vfs_nfs", Level::WARN, "default filter coverage probe"); + event!(target: "vfs_mount", Level::WARN, "default filter coverage probe"); + event!(target: "vfs_cli", Level::WARN, "default filter coverage probe"); }); match previous { diff --git a/crates/agentfs-cli/src/main.rs b/crates/vfs-cli/src/main.rs similarity index 93% rename from crates/agentfs-cli/src/main.rs rename to crates/vfs-cli/src/main.rs index 370c7869..e2b2b85a 100644 --- a/crates/agentfs-cli/src/main.rs +++ b/crates/vfs-cli/src/main.rs @@ -1,12 +1,12 @@ -use agentfs_cli::{ +use clap::{CommandFactory, Parser}; +use clap_complete::CompleteEnv; +use tracing_subscriber::prelude::*; +use vfs_cli::{ cmd::{self, completions::handle_completions}, get_runtime, logging::default_env_filter, opts::{Args, Command, FsCommand, PruneCommand, ServeCommand, SyncCommand}, }; -use clap::{CommandFactory, Parser}; -use clap_complete::CompleteEnv; -use tracing_subscriber::prelude::*; /// Parse and validate encryption key and cipher options. /// Both must be provided together or neither. @@ -23,20 +23,20 @@ fn parse_encryption( } fn partial_origin_policy( - mode: Option, + mode: Option, threshold_bytes: Option, -) -> Option { +) -> Option { match (mode, threshold_bytes) { (None, None) => None, (Some(mode), threshold_bytes) => { - let mut policy = agentfs_core::PartialOriginPolicy::new(mode.into()); + let mut policy = vfs_core::PartialOriginPolicy::new(mode.into()); if let Some(threshold_bytes) = threshold_bytes { policy = policy.with_threshold_bytes(threshold_bytes); } Some(policy) } (None, Some(threshold_bytes)) => Some( - agentfs_core::PartialOriginPolicy::new(agentfs_core::PartialOriginMode::Auto) + vfs_core::PartialOriginPolicy::new(vfs_core::PartialOriginMode::Auto) .with_threshold_bytes(threshold_bytes), ), } @@ -48,14 +48,14 @@ fn exit_with_error(message: impl std::fmt::Display) -> ! { } fn exit_with_code(code: i32) -> ! { - agentfs_cli::profiling::emit_cli_report(); + vfs_cli::profiling::emit_cli_report(); std::process::exit(code); } fn main() { // Before any threads exist: the TMPDIR override mutates the process // environment (see config::init_private_spill_dir). - agentfs_cli::config::init_private_spill_dir(); + vfs_cli::config::init_private_spill_dir(); let _ = tracing_subscriber::registry() .with(tracing_subscriber::fmt::layer()) @@ -65,7 +65,7 @@ fn main() { reset_sigpipe(); CompleteEnv::with_factory(Args::command).complete(); - let _profile_report = agentfs_cli::profiling::install_cli_sink(); + let _profile_report = vfs_cli::profiling::install_cli_sink(); let args = match Args::try_parse() { Ok(args) => args, Err(error) => { @@ -137,8 +137,8 @@ fn dispatch(args: Args) -> anyhow::Result<()> { args, } => { let encryption = parse_encryption(key, cipher)? - .map(|(hex_key, cipher)| agentfs_core::EncryptionConfig { hex_key, cipher }); - let options = agentfs_cli::opts::RunOptions { + .map(|(hex_key, cipher)| vfs_core::EncryptionConfig { hex_key, cipher }); + let options = vfs_cli::opts::RunOptions { allow, no_default_allows, session, @@ -281,7 +281,7 @@ fn dispatch(args: Args) -> anyhow::Result<()> { key, cipher, } => { - eprintln!("Warning: `agentfs nfs` is deprecated, use `agentfs serve nfs` instead"); + eprintln!("Warning: `vfs nfs` is deprecated, use `vfs serve nfs` instead"); let encryption = parse_encryption(key, cipher)?; let rt = get_runtime(); rt.block_on(cmd::nfs::handle_nfs_command( @@ -289,9 +289,7 @@ fn dispatch(args: Args) -> anyhow::Result<()> { )) } Command::McpServer { id_or_path, tools } => { - eprintln!( - "Warning: `agentfs mcp-server` is deprecated, use `agentfs serve mcp` instead" - ); + eprintln!("Warning: `vfs mcp-server` is deprecated, use `vfs serve mcp` instead"); let rt = get_runtime(); rt.block_on(cmd::mcp_server::handle_mcp_server_command( id_or_path, tools, @@ -440,17 +438,17 @@ fn default_shell() -> std::path::PathBuf { #[cfg(test)] mod partial_origin { use super::partial_origin_policy; - use agentfs_cli::opts::{Args, Command, PartialOriginMode}; use clap::Parser; + use vfs_cli::opts::{Args, Command, PartialOriginMode}; #[test] fn legacy_env_does_not_override_cli_off() { - let key = concat!("AGENTFS_OVERLAY_", "PARTIAL_ORIGIN"); + let key = concat!("VFS_OVERLAY_", "PARTIAL_ORIGIN"); let previous = std::env::var(key).ok(); std::env::set_var(key, "1"); let args = Args::try_parse_from([ - "agentfs", + "vfs", "run", "--partial-origin", "off", @@ -482,6 +480,6 @@ mod partial_origin { policy.mode ); assert_eq!(mode, Some(PartialOriginMode::Off)); - assert_eq!(policy.mode, agentfs_core::PartialOriginMode::Off); + assert_eq!(policy.mode, vfs_core::PartialOriginMode::Off); } } diff --git a/crates/agentfs-cli/src/opts.rs b/crates/vfs-cli/src/opts.rs similarity index 90% rename from crates/agentfs-cli/src/opts.rs rename to crates/vfs-cli/src/opts.rs index c5edf2cf..ab7a7305 100644 --- a/crates/agentfs-cli/src/opts.rs +++ b/crates/vfs-cli/src/opts.rs @@ -1,10 +1,10 @@ use crate::cmd::completions::Shell; -use agentfs_core::agentfs_dir; use clap::{Parser, Subcommand}; use clap_complete::{ engine::ValueCompleter, ArgValueCompleter, CompletionCandidate, PathCompleter, }; use std::path::{Path, PathBuf}; +use vfs_core::vfs_dir; /// Mount backend type #[derive(Debug, Clone, Copy, clap::ValueEnum)] @@ -39,11 +39,11 @@ impl std::fmt::Display for MountBackend { } } -impl From for agentfs_mount::Backend { +impl From for vfs_mount::Backend { fn from(value: MountBackend) -> Self { match value { - MountBackend::Fuse => agentfs_mount::Backend::Fuse, - MountBackend::Nfs => agentfs_mount::Backend::Nfs, + MountBackend::Fuse => vfs_mount::Backend::Fuse, + MountBackend::Nfs => vfs_mount::Backend::Nfs, } } } @@ -59,17 +59,17 @@ pub enum PartialOriginMode { Auto, } -impl From for agentfs_core::PartialOriginMode { +impl From for vfs_core::PartialOriginMode { fn from(value: PartialOriginMode) -> Self { match value { - PartialOriginMode::Off => agentfs_core::PartialOriginMode::Off, - PartialOriginMode::On => agentfs_core::PartialOriginMode::On, - PartialOriginMode::Auto => agentfs_core::PartialOriginMode::Auto, + PartialOriginMode::Off => vfs_core::PartialOriginMode::Off, + PartialOriginMode::On => vfs_core::PartialOriginMode::On, + PartialOriginMode::Auto => vfs_core::PartialOriginMode::Auto, } } } -/// Resolved knobs for `agentfs run`. +/// Resolved knobs for `vfs run`. /// /// One struct threaded through `cmd/run/*` so adding a run knob touches the /// clap arm, this struct, and the platform backend that consumes it — not a @@ -85,9 +85,9 @@ pub struct RunOptions { /// Allow other system users to access the mount (FUSE allow_other). pub system: bool, /// Delta-layer encryption, already validated at the CLI edge. - pub encryption: Option, + pub encryption: Option, /// Partial-origin copy-up policy resolved from CLI flags. - pub partial_origin_policy: Option, + pub partial_origin_policy: Option, /// Command to execute inside the sandbox. pub command: PathBuf, /// Arguments for the command. @@ -95,8 +95,8 @@ pub struct RunOptions { } #[derive(Parser, Debug)] -#[command(name = "agentfs")] -#[command(version = env!("AGENTFS_VERSION"))] +#[command(name = "vfs")] +#[command(version = env!("VFS_VERSION"))] #[command(about = "The filesystem for agents", long_about = None)] pub struct Args { #[command(subcommand)] @@ -139,12 +139,12 @@ pub enum Command { /// Hex-encoded encryption key. /// Enables local encryption when provided. - #[arg(long, env = "AGENTFS_KEY")] + #[arg(long, env = "VFS_KEY")] key: Option, /// Cipher algorithm for encryption (required with --key). /// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm - #[arg(long, env = "AGENTFS_CIPHER")] + #[arg(long, env = "VFS_CIPHER")] cipher: Option, /// Command to execute after initialization (mounts the filesystem, runs command, unmounts) @@ -174,12 +174,12 @@ pub enum Command { id_or_path: String, /// Hex-encoded encryption key for encrypted databases. - #[arg(long, env = "AGENTFS_KEY")] + #[arg(long, env = "VFS_KEY")] key: Option, /// Cipher algorithm for encryption (required with --key). /// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm - #[arg(long, env = "AGENTFS_CIPHER")] + #[arg(long, env = "VFS_CIPHER")] cipher: Option, #[command(subcommand)] @@ -189,7 +189,7 @@ pub enum Command { /// /// By default, uses FUSE+overlay with Linux user and mount namespaces for isolation. /// The overlay uses the host filesystem as a read-only base and stores - /// all changes in an AgentFS-backed delta layer. On macOS the overlay is + /// all changes in a Vfs-backed delta layer. On macOS the overlay is /// mounted over NFS and a generated Seatbelt profile scopes writes to the /// sandbox and reads to the allowed directories plus required platform /// paths (see the Sandboxing section of docs/MANUAL.md). @@ -223,12 +223,12 @@ pub enum Command { /// Hex-encoded encryption key for the delta layer. /// Enables local encryption when provided. - #[arg(long, env = "AGENTFS_KEY")] + #[arg(long, env = "VFS_KEY")] key: Option, /// Cipher algorithm for encryption (required with --key). /// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm - #[arg(long, env = "AGENTFS_CIPHER")] + #[arg(long, env = "VFS_CIPHER")] cipher: Option, /// Command to execute (defaults to bash on Linux, zsh on macOS) @@ -238,9 +238,9 @@ pub enum Command { #[arg(trailing_var_arg = true, allow_hyphen_values = true)] args: Vec, }, - /// Execute a command with an AgentFS filesystem mounted. + /// Execute a command with a Vfs filesystem mounted. /// - /// Mounts the specified AgentFS to a temporary directory, runs the command + /// Mounts the specified Vfs to a temporary directory, runs the command /// with that directory as the working directory, then automatically unmounts. /// This is useful for running tools that need filesystem access without /// a persistent mount. @@ -263,14 +263,14 @@ pub enum Command { backend: MountBackend, /// Hex-encoded encryption key for encrypted databases. - #[arg(long, env = "AGENTFS_KEY")] + #[arg(long, env = "VFS_KEY")] key: Option, /// Cipher algorithm for encryption (required with --key). - #[arg(long, env = "AGENTFS_CIPHER")] + #[arg(long, env = "VFS_CIPHER")] cipher: Option, }, - /// Clone a git repository into an AgentFS database (fast bulk ingest). + /// Clone a git repository into a Vfs database (fast bulk ingest). /// /// Runs `git clone --no-checkout` through a temporary mount (pack files /// are large sequential writes), then materializes the worktree by @@ -350,12 +350,12 @@ pub enum Command { partial_origin_threshold_bytes: Option, /// Hex-encoded encryption key for encrypted databases. - #[arg(long, env = "AGENTFS_KEY")] + #[arg(long, env = "VFS_KEY")] key: Option, /// Cipher algorithm for encryption (required with --key). /// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm - #[arg(long, env = "AGENTFS_CIPHER")] + #[arg(long, env = "VFS_CIPHER")] cipher: Option, }, /// Show differences between base filesystem and delta (overlay mode only) @@ -386,8 +386,8 @@ pub enum Command { #[arg(long, default_value = "table", value_parser = ["table", "json"])] format: String, }, - /// Start an NFS server to export an AgentFS filesystem over the network - /// (deprecated: use `agentfs serve nfs` instead) + /// Start an NFS server to export a Vfs filesystem over the network + /// (deprecated: use `vfs serve nfs` instead) #[cfg(unix)] Nfs { /// Agent ID or database path @@ -403,17 +403,17 @@ pub enum Command { port: u32, /// Hex-encoded encryption key for encrypted databases. - #[arg(long, env = "AGENTFS_KEY")] + #[arg(long, env = "VFS_KEY")] key: Option, /// Cipher algorithm for encryption (required with --key). /// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm - #[arg(long, env = "AGENTFS_CIPHER")] + #[arg(long, env = "VFS_CIPHER")] cipher: Option, }, /// Start an MCP server exposing filesystem and KV-store tools - /// (deprecated: use `agentfs serve mcp` instead) + /// (deprecated: use `vfs serve mcp` instead) McpServer { /// Agent ID or database path #[arg(value_name = "ID_OR_PATH", add = ArgValueCompleter::new(id_or_path_completer))] @@ -426,19 +426,19 @@ pub enum Command { tools: Option>, }, - /// Serve an AgentFS filesystem via different protocols + /// Serve a Vfs filesystem via different protocols Serve { #[command(subcommand)] command: ServeCommand, }, - /// List active agentfs run sessions + /// List active vfs run sessions Ps, /// Prune unused resources Prune { #[command(subcommand)] command: PruneCommand, }, - /// Check a local AgentFS database for SQLite and schema corruption + /// Check a local Vfs database for SQLite and schema corruption Integrity { /// Agent ID or database path #[arg(add = ArgValueCompleter::new(id_or_path_completer))] @@ -468,7 +468,7 @@ pub enum Command { #[arg(long)] cipher: Option, }, - /// Create a portable local AgentFS database backup + /// Create a portable local Vfs database backup Backup { /// Agent ID or database path #[arg(add = ArgValueCompleter::new(id_or_path_completer))] @@ -573,9 +573,9 @@ pub enum FsCommand { #[derive(Subcommand, Debug)] pub enum SyncCommand { - /// Pull remote changes (only of agentfs was initialized with remote sync) + /// Pull remote changes (only of vfs was initialized with remote sync) Pull, - /// Push remote changes (only of agentfs was initialized with remote sync) + /// Push remote changes (only of vfs was initialized with remote sync) Push, /// Print synced database stats Stats, @@ -585,7 +585,7 @@ pub enum SyncCommand { #[derive(Subcommand, Debug)] pub enum ServeCommand { - /// Start an NFS server to export an AgentFS filesystem over the network + /// Start an NFS server to export a Vfs filesystem over the network #[cfg(unix)] Nfs { /// Agent ID or database path @@ -601,12 +601,12 @@ pub enum ServeCommand { port: u32, /// Hex-encoded encryption key for encrypted databases. - #[arg(long, env = "AGENTFS_KEY")] + #[arg(long, env = "VFS_KEY")] key: Option, /// Cipher algorithm for encryption (required with --key). /// Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm - #[arg(long, env = "AGENTFS_CIPHER")] + #[arg(long, env = "VFS_CIPHER")] cipher: Option, }, @@ -626,7 +626,7 @@ pub enum ServeCommand { #[derive(Subcommand, Debug)] pub enum PruneCommand { - /// Unmount unused agentfs mount points + /// Unmount unused vfs mount points Mounts { /// Skip confirmation prompt and unmount immediately #[arg(long)] @@ -658,8 +658,8 @@ fn id_completer(current: &std::ffi::OsStr) -> Vec { return completions; }; - let agentfs_dir = agentfs_dir(); - let Ok(read_dir) = agentfs_dir.read_dir() else { + let vfs_dir = vfs_dir(); + let Ok(read_dir) = vfs_dir.read_dir() else { return completions; }; @@ -708,7 +708,7 @@ mod tests { #[test] fn run_partial_origin_options_parse() { let args = Args::try_parse_from([ - "agentfs", + "vfs", "run", "--partial-origin", "auto", @@ -736,14 +736,14 @@ mod tests { #[test] fn mount_partial_origin_options_parse() { let args = Args::try_parse_from([ - "agentfs", + "vfs", "mount", "--partial-origin", "on", "--partial-origin-threshold-bytes", "8192", "agent", - "/tmp/agentfs-mnt", + "/tmp/vfs-mnt", ]) .unwrap(); @@ -758,7 +758,7 @@ mod tests { assert_eq!(partial_origin, Some(PartialOriginMode::On)); assert_eq!(partial_origin_threshold_bytes, Some(8192)); assert_eq!(id_or_path.as_deref(), Some("agent")); - assert_eq!(mountpoint, Some(PathBuf::from("/tmp/agentfs-mnt"))); + assert_eq!(mountpoint, Some(PathBuf::from("/tmp/vfs-mnt"))); } other => panic!("expected mount command, got {other:?}"), } diff --git a/crates/agentfs-cli/tests/.gitignore b/crates/vfs-cli/tests/.gitignore similarity index 100% rename from crates/agentfs-cli/tests/.gitignore rename to crates/vfs-cli/tests/.gitignore diff --git a/crates/agentfs-cli/tests/all.sh b/crates/vfs-cli/tests/all.sh similarity index 78% rename from crates/agentfs-cli/tests/all.sh rename to crates/vfs-cli/tests/all.sh index 5f045417..5c91933b 100755 --- a/crates/agentfs-cli/tests/all.sh +++ b/crates/vfs-cli/tests/all.sh @@ -1,11 +1,11 @@ #!/bin/sh set -u -# Set AGENTFS_GATE_FORCE_SKIP= to force a synthetic SKIP without +# Set VFS_GATE_FORCE_SKIP= to force a synthetic SKIP without # running the selected test. This hook exists only to validate SKIP accounting. # -# Set AGENTFS_GATE_ALLOWED_SKIPS= to name tests whose SKIP -# stays green under AGENTFS_GATE_STRICT=1. This is for runner kernels that +# Set VFS_GATE_ALLOWED_SKIPS= to name tests whose SKIP +# stays green under VFS_GATE_STRICT=1. This is for runner kernels that # cannot provide a prerequisite at all (e.g. no FUSE-over-io_uring module # parameter); every other SKIP remains red on the strict runner. @@ -13,16 +13,16 @@ DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" cd "$CLI_DIR" -# Suite hygiene contract: every suite that invokes `agentfs run` with the real +# Suite hygiene contract: every suite that invokes `vfs run` with the real # HOME must pass an explicit unique `--session` id and remove exactly the -# ~/.agentfs/run/ dirs it created in its cleanup trap (the user may -# have real sessions there — NEVER sweep ~/.agentfs/run wholesale). Suites +# ~/.vfs/run/ dirs it created in its cleanup trap (the user may +# have real sessions there — NEVER sweep ~/.vfs/run wholesale). Suites # that override HOME to a temp root are covered by their temp-root removal. # Pin TMPDIR to a per-run scratch dir cleaned on exit: turso_core 0.5.3 leaks # /tmp/tursodb-ephemeral-* sort-spill files (vdbe/execute.rs:10096 never # unlinks them), so dependency litter must not accumulate on the host. -SUITE_TMPDIR="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-all.XXXXXX")" +SUITE_TMPDIR="$(mktemp -d "${TMPDIR:-/tmp}/vfs-all.XXXXXX")" trap 'rm -rf "$SUITE_TMPDIR"' EXIT INT TERM TMPDIR="$SUITE_TMPDIR" TMP="$SUITE_TMPDIR" @@ -47,7 +47,7 @@ truthy() { } skip_allowed() { - allowed_list="${AGENTFS_GATE_ALLOWED_SKIPS:-}" + allowed_list="${VFS_GATE_ALLOWED_SKIPS:-}" [ -n "$allowed_list" ] || return 1 old_ifs="${IFS-}" IFS=', ' @@ -72,7 +72,7 @@ record_result() { SKIP) SKIP_COUNT=$((SKIP_COUNT + 1)) if skip_allowed "$label"; then - result="SKIP (allowed by AGENTFS_GATE_ALLOWED_SKIPS)" + result="SKIP (allowed by VFS_GATE_ALLOWED_SKIPS)" else DISALLOWED_SKIP_COUNT=$((DISALLOWED_SKIP_COUNT + 1)) fi @@ -91,11 +91,11 @@ run_test() { shift printf '\n==> %s\n' "$label" - tmp="$(mktemp "${TMPDIR:-/tmp}/agentfs-all.${label}.XXXXXX")" + tmp="$(mktemp "${TMPDIR:-/tmp}/vfs-all.${label}.XXXXXX")" - if [ "${AGENTFS_GATE_FORCE_SKIP:-}" = "$label" ] || - [ "${AGENTFS_GATE_FORCE_SKIP:-}" = "all" ]; then - printf 'SKIP: forced by AGENTFS_GATE_FORCE_SKIP=%s\n' "${AGENTFS_GATE_FORCE_SKIP:-}" >"$tmp" + if [ "${VFS_GATE_FORCE_SKIP:-}" = "$label" ] || + [ "${VFS_GATE_FORCE_SKIP:-}" = "all" ]; then + printf 'SKIP: forced by VFS_GATE_FORCE_SKIP=%s\n' "${VFS_GATE_FORCE_SKIP:-}" >"$tmp" status=0 else "$@" >"$tmp" 2>&1 @@ -126,7 +126,7 @@ run_test "cli-smoke" "$DIR/test-cli-smoke.sh" # 1. Linux baseline, establishes expected behavior. run_test "linux-syscalls" "$DIR/test-linux-syscalls.sh" -# 2. FUSE overlay through agentfs run, tests copy-on-write. +# 2. FUSE overlay through vfs run, tests copy-on-write. run_test "run-syscalls" "$DIR/test-run-syscalls.sh" run_test "run-bash" "$DIR/test-run-bash.sh" @@ -143,9 +143,9 @@ run_test "signal-teardown" env \ # Corruption/concurrency torture runs both FUSE transport legs. The test prints # SKIP and exits 0 if Linux user namespace/FUSE prerequisites are unavailable; -# AGENTFS_GATE_STRICT=1 makes such skips fail the gate on the designated runner. +# VFS_GATE_STRICT=1 makes such skips fail the gate on the designated runner. run_test "corruption-torture-legacy" env \ - AGENTFS_FUSE_URING=0 \ + VFS_FUSE_URING=0 \ CORRUPTION_TORTURE_WORKERS="${CORRUPTION_TORTURE_WORKERS:-4}" \ CORRUPTION_TORTURE_ITERATIONS="${CORRUPTION_TORTURE_ITERATIONS:-3}" \ CORRUPTION_TORTURE_TIMEOUT="${CORRUPTION_TORTURE_TIMEOUT:-120}" \ @@ -153,7 +153,7 @@ run_test "corruption-torture-legacy" env \ CORRUPTION_TORTURE_INTEGRITY_INTERVAL="${CORRUPTION_TORTURE_INTEGRITY_INTERVAL:-1}" \ "$DIR/test-corruption-torture.sh" run_test "corruption-torture-uring" env \ - AGENTFS_FUSE_URING=1 \ + VFS_FUSE_URING=1 \ CORRUPTION_TORTURE_WORKERS="${CORRUPTION_TORTURE_WORKERS:-4}" \ CORRUPTION_TORTURE_ITERATIONS="${CORRUPTION_TORTURE_ITERATIONS:-3}" \ CORRUPTION_TORTURE_TIMEOUT="${CORRUPTION_TORTURE_TIMEOUT:-120}" \ @@ -178,9 +178,9 @@ if [ "$FAIL_COUNT" -ne 0 ]; then exit 1 fi -if [ "$DISALLOWED_SKIP_COUNT" -ne 0 ] && truthy "${AGENTFS_GATE_STRICT:-0}"; then - printf 'FAILED: AGENTFS_GATE_STRICT=1 treats SKIP as a gate failure (disallowed skips: %s, AGENTFS_GATE_ALLOWED_SKIPS=%s)\n' \ - "$DISALLOWED_SKIP_COUNT" "${AGENTFS_GATE_ALLOWED_SKIPS:-}" +if [ "$DISALLOWED_SKIP_COUNT" -ne 0 ] && truthy "${VFS_GATE_STRICT:-0}"; then + printf 'FAILED: VFS_GATE_STRICT=1 treats SKIP as a gate failure (disallowed skips: %s, VFS_GATE_ALLOWED_SKIPS=%s)\n' \ + "$DISALLOWED_SKIP_COUNT" "${VFS_GATE_ALLOWED_SKIPS:-}" exit 1 fi diff --git a/crates/agentfs-cli/tests/fixtures/migrate/v0_0.db b/crates/vfs-cli/tests/fixtures/migrate/v0_0.db similarity index 100% rename from crates/agentfs-cli/tests/fixtures/migrate/v0_0.db rename to crates/vfs-cli/tests/fixtures/migrate/v0_0.db diff --git a/crates/agentfs-cli/tests/fixtures/migrate/v0_2.db b/crates/vfs-cli/tests/fixtures/migrate/v0_2.db similarity index 100% rename from crates/agentfs-cli/tests/fixtures/migrate/v0_2.db rename to crates/vfs-cli/tests/fixtures/migrate/v0_2.db diff --git a/crates/agentfs-cli/tests/fixtures/migrate/v0_4-encrypted.db b/crates/vfs-cli/tests/fixtures/migrate/v0_4-encrypted.db similarity index 100% rename from crates/agentfs-cli/tests/fixtures/migrate/v0_4-encrypted.db rename to crates/vfs-cli/tests/fixtures/migrate/v0_4-encrypted.db diff --git a/crates/agentfs-cli/tests/fixtures/migrate/v0_4.db b/crates/vfs-cli/tests/fixtures/migrate/v0_4.db similarity index 100% rename from crates/agentfs-cli/tests/fixtures/migrate/v0_4.db rename to crates/vfs-cli/tests/fixtures/migrate/v0_4.db diff --git a/crates/agentfs-cli/tests/migrate_fixtures.rs b/crates/vfs-cli/tests/migrate_fixtures.rs similarity index 98% rename from crates/agentfs-cli/tests/migrate_fixtures.rs rename to crates/vfs-cli/tests/migrate_fixtures.rs index ace40688..931b4e5c 100644 --- a/crates/agentfs-cli/tests/migrate_fixtures.rs +++ b/crates/vfs-cli/tests/migrate_fixtures.rs @@ -7,13 +7,13 @@ //! Regenerate after a schema-authority or turso format change with: //! //! ```sh -//! cargo +nightly test -p agentfs-cli --test migrate_fixtures -- --ignored +//! cargo +nightly test -p vfs-cli --test migrate_fixtures -- --ignored //! ``` -use agentfs_core::schema::{self, SchemaVersion}; use std::fs; use std::path::{Path, PathBuf}; use turso::{Builder, Connection, EncryptionOpts, Value}; +use vfs_core::schema::{self, SchemaVersion}; /// Key/cipher for `v0_4-encrypted.db`; kept in step with /// `test-migrate-consolidation.sh`. diff --git a/crates/agentfs-cli/tests/syscall/Makefile b/crates/vfs-cli/tests/syscall/Makefile similarity index 100% rename from crates/agentfs-cli/tests/syscall/Makefile rename to crates/vfs-cli/tests/syscall/Makefile diff --git a/crates/agentfs-cli/tests/syscall/main.c b/crates/vfs-cli/tests/syscall/main.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/main.c rename to crates/vfs-cli/tests/syscall/main.c diff --git a/crates/agentfs-cli/tests/syscall/test-append.c b/crates/vfs-cli/tests/syscall/test-append.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-append.c rename to crates/vfs-cli/tests/syscall/test-append.c diff --git a/crates/agentfs-cli/tests/syscall/test-close.c b/crates/vfs-cli/tests/syscall/test-close.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-close.c rename to crates/vfs-cli/tests/syscall/test-close.c diff --git a/crates/agentfs-cli/tests/syscall/test-common.h b/crates/vfs-cli/tests/syscall/test-common.h similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-common.h rename to crates/vfs-cli/tests/syscall/test-common.h diff --git a/crates/agentfs-cli/tests/syscall/test-copyup-inode-stability.c b/crates/vfs-cli/tests/syscall/test-copyup-inode-stability.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-copyup-inode-stability.c rename to crates/vfs-cli/tests/syscall/test-copyup-inode-stability.c diff --git a/crates/agentfs-cli/tests/syscall/test-copyup-permissions.c b/crates/vfs-cli/tests/syscall/test-copyup-permissions.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-copyup-permissions.c rename to crates/vfs-cli/tests/syscall/test-copyup-permissions.c diff --git a/crates/agentfs-cli/tests/syscall/test-create-existing.c b/crates/vfs-cli/tests/syscall/test-create-existing.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-create-existing.c rename to crates/vfs-cli/tests/syscall/test-create-existing.c diff --git a/crates/agentfs-cli/tests/syscall/test-dup.c b/crates/vfs-cli/tests/syscall/test-dup.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-dup.c rename to crates/vfs-cli/tests/syscall/test-dup.c diff --git a/crates/agentfs-cli/tests/syscall/test-dup2.c b/crates/vfs-cli/tests/syscall/test-dup2.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-dup2.c rename to crates/vfs-cli/tests/syscall/test-dup2.c diff --git a/crates/agentfs-cli/tests/syscall/test-fstat.c b/crates/vfs-cli/tests/syscall/test-fstat.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-fstat.c rename to crates/vfs-cli/tests/syscall/test-fstat.c diff --git a/crates/agentfs-cli/tests/syscall/test-getdents64.c b/crates/vfs-cli/tests/syscall/test-getdents64.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-getdents64.c rename to crates/vfs-cli/tests/syscall/test-getdents64.c diff --git a/crates/agentfs-cli/tests/syscall/test-link.c b/crates/vfs-cli/tests/syscall/test-link.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-link.c rename to crates/vfs-cli/tests/syscall/test-link.c diff --git a/crates/agentfs-cli/tests/syscall/test-lstat.c b/crates/vfs-cli/tests/syscall/test-lstat.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-lstat.c rename to crates/vfs-cli/tests/syscall/test-lstat.c diff --git a/crates/agentfs-cli/tests/syscall/test-mkfifo.c b/crates/vfs-cli/tests/syscall/test-mkfifo.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-mkfifo.c rename to crates/vfs-cli/tests/syscall/test-mkfifo.c diff --git a/crates/agentfs-cli/tests/syscall/test-mknod.c b/crates/vfs-cli/tests/syscall/test-mknod.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-mknod.c rename to crates/vfs-cli/tests/syscall/test-mknod.c diff --git a/crates/agentfs-cli/tests/syscall/test-open-readonly.c b/crates/vfs-cli/tests/syscall/test-open-readonly.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-open-readonly.c rename to crates/vfs-cli/tests/syscall/test-open-readonly.c diff --git a/crates/agentfs-cli/tests/syscall/test-openat.c b/crates/vfs-cli/tests/syscall/test-openat.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-openat.c rename to crates/vfs-cli/tests/syscall/test-openat.c diff --git a/crates/agentfs-cli/tests/syscall/test-pread-sparse.c b/crates/vfs-cli/tests/syscall/test-pread-sparse.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-pread-sparse.c rename to crates/vfs-cli/tests/syscall/test-pread-sparse.c diff --git a/crates/agentfs-cli/tests/syscall/test-read.c b/crates/vfs-cli/tests/syscall/test-read.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-read.c rename to crates/vfs-cli/tests/syscall/test-read.c diff --git a/crates/agentfs-cli/tests/syscall/test-rename.c b/crates/vfs-cli/tests/syscall/test-rename.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-rename.c rename to crates/vfs-cli/tests/syscall/test-rename.c diff --git a/crates/agentfs-cli/tests/syscall/test-stat.c b/crates/vfs-cli/tests/syscall/test-stat.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-stat.c rename to crates/vfs-cli/tests/syscall/test-stat.c diff --git a/crates/agentfs-cli/tests/syscall/test-unlink.c b/crates/vfs-cli/tests/syscall/test-unlink.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-unlink.c rename to crates/vfs-cli/tests/syscall/test-unlink.c diff --git a/crates/agentfs-cli/tests/syscall/test-write.c b/crates/vfs-cli/tests/syscall/test-write.c similarity index 100% rename from crates/agentfs-cli/tests/syscall/test-write.c rename to crates/vfs-cli/tests/syscall/test-write.c diff --git a/crates/agentfs-cli/tests/test-cli-smoke.sh b/crates/vfs-cli/tests/test-cli-smoke.sh similarity index 72% rename from crates/agentfs-cli/tests/test-cli-smoke.sh rename to crates/vfs-cli/tests/test-cli-smoke.sh index d00068c5..5679d26c 100755 --- a/crates/agentfs-cli/tests/test-cli-smoke.sh +++ b/crates/vfs-cli/tests/test-cli-smoke.sh @@ -12,11 +12,11 @@ echo -n "TEST cli smoke... " DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-cli-smoke.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-cli-smoke.XXXXXX")" NFS_PID="" -# Sessions run with the real HOME land in ~/.agentfs/run/; use unique +# Sessions run with the real HOME land in ~/.vfs/run/; use unique # ids so cleanup removes exactly the session dirs this test created (never -# sweep ~/.agentfs/run). +# sweep ~/.vfs/run). SESSION_PREFIX="smoke-$$" cleanup() { @@ -25,9 +25,9 @@ cleanup() { wait "$NFS_PID" 2>/dev/null || true fi rm -rf "$ROOT" \ - "${HOME:?}/.agentfs/run/${SESSION_PREFIX}-run" \ - "${HOME:?}/.agentfs/run/${SESSION_PREFIX}-exit-missing" \ - "${HOME:?}/.agentfs/run/${SESSION_PREFIX}-exit-nonexec" + "${HOME:?}/.vfs/run/${SESSION_PREFIX}-run" \ + "${HOME:?}/.vfs/run/${SESSION_PREFIX}-exit-missing" \ + "${HOME:?}/.vfs/run/${SESSION_PREFIX}-exit-nonexec" } trap cleanup EXIT INT TERM @@ -36,17 +36,17 @@ fail() { exit 1 } -# Resolve the binary once: background legs must track the agentfs PID itself +# Resolve the binary once: background legs must track the vfs PID itself # (backgrounding a wrapper orphans the real process on cleanup kill). -if [ -n "${AGENTFS_BIN:-}" ]; then - BIN="$AGENTFS_BIN" +if [ -n "${VFS_BIN:-}" ]; then + BIN="$VFS_BIN" else - cargo build --quiet --manifest-path "$CLI_DIR/Cargo.toml" || fail "could not build agentfs" - BIN="$CLI_DIR/../../target/debug/agentfs" + cargo build --quiet --manifest-path "$CLI_DIR/Cargo.toml" || fail "could not build vfs" + BIN="$CLI_DIR/../../target/debug/vfs" fi -[ -x "$BIN" ] || fail "agentfs binary not found at $BIN" +[ -x "$BIN" ] || fail "vfs binary not found at $BIN" -run_agentfs() { +run_vfs() { "$BIN" "$@" } @@ -68,46 +68,46 @@ export GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null cd "$ROOT" # --- init: create, refuse duplicate, --force ----------------------------------- -run_agentfs init smoke >"$ROOT/init.log" 2>&1 || fail "init failed: $(cat "$ROOT/init.log")" -DB="$ROOT/.agentfs/smoke.db" +run_vfs init smoke >"$ROOT/init.log" 2>&1 || fail "init failed: $(cat "$ROOT/init.log")" +DB="$ROOT/.vfs/smoke.db" [ -f "$DB" ] || fail "init did not create $DB" # init's schema writes must be checkpointed away before it returns: a fresh # init used to leave a ~119KB -wal next to the new DB (invariant I1). [ ! -e "$DB-wal" ] || fail "init left $DB-wal behind" [ ! -e "$DB-shm" ] || fail "init left $DB-shm behind" -if run_agentfs init smoke >"$ROOT/init-dup.log" 2>&1; then +if run_vfs init smoke >"$ROOT/init-dup.log" 2>&1; then fail "repeated init succeeded without --force" fi grep -q "already exists" "$ROOT/init-dup.log" || fail "duplicate init missing refusal message" -run_agentfs init smoke --force >/dev/null 2>&1 || fail "init --force failed" +run_vfs init smoke --force >/dev/null 2>&1 || fail "init --force failed" [ ! -e "$DB-wal" ] || fail "init --force left $DB-wal behind" [ ! -e "$DB-shm" ] || fail "init --force left $DB-shm behind" # --- init variants exit single-file too: --base (overlay) and -c (mount+command) -- mkdir -p "$ROOT/initbase" echo base-payload >"$ROOT/initbase/base.txt" -run_agentfs init smoke-base --base "$ROOT/initbase" >"$ROOT/init-base.log" 2>&1 || +run_vfs init smoke-base --base "$ROOT/initbase" >"$ROOT/init-base.log" 2>&1 || fail "init --base failed: $(cat "$ROOT/init-base.log")" -BASE_DB="$ROOT/.agentfs/smoke-base.db" +BASE_DB="$ROOT/.vfs/smoke-base.db" [ -f "$BASE_DB" ] || fail "init --base did not create $BASE_DB" [ ! -e "$BASE_DB-wal" ] || fail "init --base left $BASE_DB-wal behind" [ ! -e "$BASE_DB-shm" ] || fail "init --base left $BASE_DB-shm behind" -run_agentfs init smoke-cmd -c 'echo init-cmd-ok' >"$ROOT/init-cmd.log" 2>&1 || +run_vfs init smoke-cmd -c 'echo init-cmd-ok' >"$ROOT/init-cmd.log" 2>&1 || fail "init -c failed: $(cat "$ROOT/init-cmd.log")" grep -q "init-cmd-ok" "$ROOT/init-cmd.log" || fail "init -c command output missing" -CMD_DB="$ROOT/.agentfs/smoke-cmd.db" +CMD_DB="$ROOT/.vfs/smoke-cmd.db" [ -f "$CMD_DB" ] || fail "init -c did not create $CMD_DB" [ ! -e "$CMD_DB-wal" ] || fail "init -c left $CMD_DB-wal behind" [ ! -e "$CMD_DB-shm" ] || fail "init -c left $CMD_DB-shm behind" # --- fs write/cat/ls ------------------------------------------------------------ -run_agentfs fs "$DB" write /smoke.txt smoke-payload >/dev/null 2>&1 || fail "fs write failed" -[ "$(run_agentfs fs "$DB" cat /smoke.txt 2>/dev/null)" = "smoke-payload" ] || fail "fs cat mismatch" -run_agentfs fs "$DB" ls / >"$ROOT/ls.log" 2>&1 || fail "fs ls failed" +run_vfs fs "$DB" write /smoke.txt smoke-payload >/dev/null 2>&1 || fail "fs write failed" +[ "$(run_vfs fs "$DB" cat /smoke.txt 2>/dev/null)" = "smoke-payload" ] || fail "fs cat mismatch" +run_vfs fs "$DB" ls / >"$ROOT/ls.log" 2>&1 || fail "fs ls failed" grep -q "smoke.txt" "$ROOT/ls.log" || fail "fs ls does not list smoke.txt" # --- run (session sandbox) ------------------------------------------------------- -run_agentfs run --session "$SESSION_PREFIX-run" -- sh -c 'echo run-ok' >"$ROOT/run.log" 2>&1 || +run_vfs run --session "$SESSION_PREFIX-run" -- sh -c 'echo run-ok' >"$ROOT/run.log" 2>&1 || fail "run failed: $(cat "$ROOT/run.log")" grep -q "run-ok" "$ROOT/run.log" || fail "run output missing" @@ -116,13 +116,13 @@ mkdir -p "$ROOT/home" env HOME="$ROOT/home" "$BIN" run --session smoke-audit -- sh -c 'echo audit-ok' \ >"$ROOT/run-audit.log" 2>&1 || fail "audited run failed: $(cat "$ROOT/run-audit.log")" -AUDIT_DB="$ROOT/home/.agentfs/run/smoke-audit/delta.db" +AUDIT_DB="$ROOT/home/.vfs/run/smoke-audit/delta.db" [ -f "$AUDIT_DB" ] || fail "session delta.db missing at $AUDIT_DB" # The audit reopen must finalize: a leftover WAL/SHM next to the session DB # breaks the single-file invariant the run teardown just established. [ ! -e "$AUDIT_DB-wal" ] || fail "run audit left $AUDIT_DB-wal behind" [ ! -e "$AUDIT_DB-shm" ] || fail "run audit left $AUDIT_DB-shm behind" -run_agentfs timeline "$AUDIT_DB" --format json >"$ROOT/timeline-audit.json" 2>&1 || +run_vfs timeline "$AUDIT_DB" --format json >"$ROOT/timeline-audit.json" 2>&1 || fail "timeline on session DB failed: $(cat "$ROOT/timeline-audit.json")" # Read-style reopens must restore the single-file family too. [ ! -e "$AUDIT_DB-wal" ] || fail "timeline left $AUDIT_DB-wal behind" @@ -133,13 +133,13 @@ grep -q '"status": "success"' "$ROOT/timeline-audit.json" || fail "run audit row grep -q 'exit_code' "$ROOT/timeline-audit.json" || fail "run audit row missing exit_code" # --- exec (mount-owning) ---------------------------------------------------------- -run_agentfs exec "$DB" sh -c 'echo exec-ok' >"$ROOT/exec.log" 2>&1 || +run_vfs exec "$DB" sh -c 'echo exec-ok' >"$ROOT/exec.log" 2>&1 || fail "exec failed: $(cat "$ROOT/exec.log")" grep -q "exec-ok" "$ROOT/exec.log" || fail "exec output missing" # Regression: fs-write-created files must be chmod-able inside exec (they were # owned by uid/gid 0, so chmod as the invoking uid failed EPERM). -run_agentfs exec "$DB" sh -c 'chmod 700 smoke.txt' >"$ROOT/exec-chmod.log" 2>&1 || +run_vfs exec "$DB" sh -c 'chmod 700 smoke.txt' >"$ROOT/exec-chmod.log" 2>&1 || fail "chmod of an fs-write-created file failed inside exec: $(cat "$ROOT/exec-chmod.log")" # --- exec/run command-not-found conventions (VAL-CLI-019/020) ---------------------- @@ -149,25 +149,25 @@ run_agentfs exec "$DB" sh -c 'chmod 700 smoke.txt' >"$ROOT/exec-chmod.log" 2>&1 printf '#!/bin/sh\nexit 0\n' >"$ROOT/nonexec.sh" chmod 644 "$ROOT/nonexec.sh" rc=0 -run_agentfs exec "$DB" /definitely/missing >"$ROOT/exec-missing.log" 2>&1 || rc=$? +run_vfs exec "$DB" /definitely/missing >"$ROOT/exec-missing.log" 2>&1 || rc=$? [ "$rc" -eq 127 ] || fail "exec of a missing absolute command exited $rc, want 127" [ "$(grep -c '^Error:' "$ROOT/exec-missing.log")" -eq 1 ] || fail "exec missing-command should print exactly one Error: line: $(cat "$ROOT/exec-missing.log")" rc=0 -run_agentfs exec "$DB" definitely-missing-cmd-xyz >"$ROOT/exec-missing-path.log" 2>&1 || rc=$? +run_vfs exec "$DB" definitely-missing-cmd-xyz >"$ROOT/exec-missing-path.log" 2>&1 || rc=$? [ "$rc" -eq 127 ] || fail "exec of a missing PATH command exited $rc, want 127" rc=0 -run_agentfs exec "$DB" "$ROOT/nonexec.sh" >"$ROOT/exec-nonexec.log" 2>&1 || rc=$? +run_vfs exec "$DB" "$ROOT/nonexec.sh" >"$ROOT/exec-nonexec.log" 2>&1 || rc=$? [ "$rc" -eq 126 ] || fail "exec of a non-executable command exited $rc, want 126" rc=0 -run_agentfs exec "$DB" sh -c 'exit 43' >"$ROOT/exec-43.log" 2>&1 || rc=$? +run_vfs exec "$DB" sh -c 'exit 43' >"$ROOT/exec-43.log" 2>&1 || rc=$? [ "$rc" -eq 43 ] || fail "exec child exit status not passed through: got $rc, want 43" rc=0 -run_agentfs run --session "$SESSION_PREFIX-exit-missing" -- /definitely/missing \ +run_vfs run --session "$SESSION_PREFIX-exit-missing" -- /definitely/missing \ >"$ROOT/run-missing.log" 2>&1 || rc=$? [ "$rc" -eq 127 ] || fail "run of a missing command exited $rc, want 127" rc=0 -run_agentfs run --session "$SESSION_PREFIX-exit-nonexec" -- "$ROOT/nonexec.sh" \ +run_vfs run --session "$SESSION_PREFIX-exit-nonexec" -- "$ROOT/nonexec.sh" \ >"$ROOT/run-nonexec.log" 2>&1 || rc=$? [ "$rc" -eq 126 ] || fail "run of a non-executable command exited $rc, want 126" @@ -182,26 +182,26 @@ mkdir -p "$ROOT/srcrepo" git add hello.txt git commit -q -m smoke ) || fail "could not build clone fixture repo" -run_agentfs clone "$ROOT/clone.db" "$ROOT/srcrepo" repo >"$ROOT/clone.log" 2>&1 || +run_vfs clone "$ROOT/clone.db" "$ROOT/srcrepo" repo >"$ROOT/clone.log" 2>&1 || fail "clone failed: $(cat "$ROOT/clone.log")" -run_agentfs fs "$ROOT/clone.db" ls / >"$ROOT/clone-ls.log" 2>&1 || +run_vfs fs "$ROOT/clone.db" ls / >"$ROOT/clone-ls.log" 2>&1 || fail "fs ls on cloned DB failed" grep -q "hello.txt" "$ROOT/clone-ls.log" || fail "cloned repo content missing from DB" # --- timeline ------------------------------------------------------------------------- -run_agentfs timeline "$DB" --format json >"$ROOT/timeline.log" 2>&1 || +run_vfs timeline "$DB" --format json >"$ROOT/timeline.log" 2>&1 || fail "timeline failed: $(cat "$ROOT/timeline.log")" # --- backup --materialize and materialize -------------------------------------------- -run_agentfs backup "$DB" "$ROOT/backup.db" --verify --materialize >/dev/null 2>&1 || +run_vfs backup "$DB" "$ROOT/backup.db" --verify --materialize >/dev/null 2>&1 || fail "backup --verify --materialize failed" [ -f "$ROOT/backup.db" ] || fail "backup did not create target" -run_agentfs materialize "$DB" --output "$ROOT/materialized.db" --verify >/dev/null 2>&1 || +run_vfs materialize "$DB" --output "$ROOT/materialized.db" --verify >/dev/null 2>&1 || fail "materialize --verify failed" [ -f "$ROOT/materialized.db" ] || fail "materialize did not create target" # --- integrity ------------------------------------------------------------------------- -run_agentfs integrity --json "$DB" >"$ROOT/integrity.json" 2>&1 || +run_vfs integrity --json "$DB" >"$ROOT/integrity.json" 2>&1 || fail "integrity failed: $(cat "$ROOT/integrity.json")" # --- one-shot commands leave a single-file database family (invariant I1) --------------- @@ -215,45 +215,45 @@ no_sidecars() { [ ! -e "$RDB-shm" ] || fail "$1 left $RDB-shm behind" } no_sidecars "cp of backup.db" -run_agentfs timeline "$RDB" >/dev/null 2>&1 || fail "timeline (read census) failed" +run_vfs timeline "$RDB" >/dev/null 2>&1 || fail "timeline (read census) failed" no_sidecars "timeline" -run_agentfs timeline "$RDB" --format json >/dev/null 2>&1 || +run_vfs timeline "$RDB" --format json >/dev/null 2>&1 || fail "timeline --format json (read census) failed" no_sidecars "timeline --format json" -run_agentfs fs "$RDB" ls / >/dev/null 2>&1 || fail "fs ls (read census) failed" +run_vfs fs "$RDB" ls / >/dev/null 2>&1 || fail "fs ls (read census) failed" no_sidecars "fs ls" -run_agentfs fs "$RDB" cat /smoke.txt >/dev/null 2>&1 || fail "fs cat (read census) failed" +run_vfs fs "$RDB" cat /smoke.txt >/dev/null 2>&1 || fail "fs cat (read census) failed" no_sidecars "fs cat" -if run_agentfs fs "$RDB" cat /missing.txt >/dev/null 2>&1; then +if run_vfs fs "$RDB" cat /missing.txt >/dev/null 2>&1; then fail "fs cat of a missing file succeeded" fi no_sidecars "fs cat (error path)" -run_agentfs fs "$RDB" write /census-write.txt census-payload >/dev/null 2>&1 || +run_vfs fs "$RDB" write /census-write.txt census-payload >/dev/null 2>&1 || fail "fs write (census) failed" no_sidecars "fs write" -[ "$(run_agentfs fs "$RDB" cat /census-write.txt 2>/dev/null)" = "census-payload" ] || +[ "$(run_vfs fs "$RDB" cat /census-write.txt 2>/dev/null)" = "census-payload" ] || fail "fs write payload not durable after finalize" -run_agentfs diff "$RDB" >/dev/null 2>&1 || fail "diff (read census) failed" +run_vfs diff "$RDB" >/dev/null 2>&1 || fail "diff (read census) failed" no_sidecars "diff" -run_agentfs integrity --json "$RDB" >/dev/null 2>&1 || fail "integrity --json (read census) failed" +run_vfs integrity --json "$RDB" >/dev/null 2>&1 || fail "integrity --json (read census) failed" no_sidecars "integrity --json" -run_agentfs migrate "$RDB" >/dev/null 2>&1 || fail "migrate already-current (read census) failed" +run_vfs migrate "$RDB" >/dev/null 2>&1 || fail "migrate already-current (read census) failed" no_sidecars "migrate (already current)" cp "$DIR/fixtures/migrate/v0_4.db" "$ROOT/dry-run.db" -run_agentfs migrate "$ROOT/dry-run.db" --dry-run >/dev/null 2>&1 || fail "migrate --dry-run failed" +run_vfs migrate "$ROOT/dry-run.db" --dry-run >/dev/null 2>&1 || fail "migrate --dry-run failed" [ ! -e "$ROOT/dry-run.db-wal" ] || fail "migrate --dry-run left $ROOT/dry-run.db-wal behind" [ ! -e "$ROOT/dry-run.db-shm" ] || fail "migrate --dry-run left $ROOT/dry-run.db-shm behind" # sync stats errors on a non-synced local DB but must still exit single-file -run_agentfs sync "$RDB" stats >/dev/null 2>&1 || true +run_vfs sync "$RDB" stats >/dev/null 2>&1 || true no_sidecars "sync stats" -run_agentfs ps >/dev/null 2>&1 || fail "ps (read census) failed" +run_vfs ps >/dev/null 2>&1 || fail "ps (read census) failed" no_sidecars "ps" # exec is mount-owning: teardown chdirs to / before the sidecar sweep runs, and # agent-id resolution hands core a cwd-relative db path, which used to leave a # truncated 0-byte -wal next to the DB after exit. -EXEC_DB="$ROOT/.agentfs/exec-census.db" +EXEC_DB="$ROOT/.vfs/exec-census.db" cp "$RDB" "$EXEC_DB" -run_agentfs exec exec-census sh -c 'echo exec-census-ok' >"$ROOT/exec-census.log" 2>&1 || +run_vfs exec exec-census sh -c 'echo exec-census-ok' >"$ROOT/exec-census.log" 2>&1 || fail "exec (census) failed: $(cat "$ROOT/exec-census.log")" grep -q "exec-census-ok" "$ROOT/exec-census.log" || fail "exec (census) output missing" [ ! -e "$EXEC_DB-wal" ] || fail "exec left $EXEC_DB-wal behind" @@ -261,24 +261,24 @@ grep -q "exec-census-ok" "$ROOT/exec-census.log" || fail "exec (census) output m # --- migrate a committed old-schema fixture ---------------------------------------------- cp "$DIR/fixtures/migrate/v0_4.db" "$ROOT/old.db" -run_agentfs migrate "$ROOT/old.db" >"$ROOT/migrate.log" 2>&1 || +run_vfs migrate "$ROOT/old.db" >"$ROOT/migrate.log" 2>&1 || fail "migrate failed: $(cat "$ROOT/migrate.log")" -run_agentfs integrity --json "$ROOT/old.db" >/dev/null 2>&1 || +run_vfs integrity --json "$ROOT/old.db" >/dev/null 2>&1 || fail "integrity on migrated fixture failed" # Regression: a nonexistent path-shaped argument must report a missing # database, not "invalid agent ID". -if run_agentfs migrate "$ROOT/definitely/missing.db" >"$ROOT/migrate-missing.log" 2>&1; then +if run_vfs migrate "$ROOT/definitely/missing.db" >"$ROOT/migrate-missing.log" 2>&1; then fail "migrate of a missing path succeeded" fi grep -qi "database not found" "$ROOT/migrate-missing.log" || fail "migrate missing-path error is not a not-found report: $(cat "$ROOT/migrate-missing.log")" # --- ps ------------------------------------------------------------------------------------ -run_agentfs ps >/dev/null 2>&1 || fail "ps failed" +run_vfs ps >/dev/null 2>&1 || fail "ps failed" # --- completions ----------------------------------------------------------------------------- -run_agentfs completions show >"$ROOT/completions.log" 2>&1 || fail "completions show failed" +run_vfs completions show >"$ROOT/completions.log" 2>&1 || fail "completions show failed" # --- MCP over stdio via the deprecated `mcp-server` alias ------------------------------------- python3 - "$BIN" "$DB" <<'PY' || fail "mcp-server alias initialize round trip failed" @@ -320,14 +320,14 @@ PY NFS_PID=$! WAITED=0 while [ "$WAITED" -lt 40 ]; do - if grep -q "AgentFS NFS Server" "$ROOT/nfs.log" 2>/dev/null; then + if grep -q "Vfs NFS Server" "$ROOT/nfs.log" 2>/dev/null; then break fi kill -0 "$NFS_PID" 2>/dev/null || fail "nfs alias exited early: $(cat "$ROOT/nfs.log")" sleep 0.25 WAITED=$((WAITED + 1)) done -grep -q "AgentFS NFS Server" "$ROOT/nfs.log" || fail "nfs alias never reported startup" +grep -q "Vfs NFS Server" "$ROOT/nfs.log" || fail "nfs alias never reported startup" kill "$NFS_PID" 2>/dev/null || true wait "$NFS_PID" 2>/dev/null || true NFS_PID="" diff --git a/crates/agentfs-cli/tests/test-corruption-torture.sh b/crates/vfs-cli/tests/test-corruption-torture.sh similarity index 94% rename from crates/agentfs-cli/tests/test-corruption-torture.sh rename to crates/vfs-cli/tests/test-corruption-torture.sh index b36cf945..f0d0136b 100755 --- a/crates/agentfs-cli/tests/test-corruption-torture.sh +++ b/crates/vfs-cli/tests/test-corruption-torture.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Concurrency/corruption torture smoke for `agentfs run --session`. +# Concurrency/corruption torture smoke for `vfs run --session`. # # The workload keeps one owner session alive while concurrent joiners mutate # isolated worker directories in the same delta database. A background monitor @@ -9,7 +9,7 @@ # set -eu -echo -n "TEST corruption torture (agentfs run session concurrency)... " +echo -n "TEST corruption torture (vfs run session concurrency)... " DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" @@ -163,7 +163,7 @@ cleanup() { if [ -n "$TEST_ROOT" ] && [ -d "$TEST_ROOT" ]; then case "$TEST_ROOT" in - "${TMPDIR:-/tmp}"/agentfs-corruption-torture.*) + "${TMPDIR:-/tmp}"/vfs-corruption-torture.*) rm -rf "$TEST_ROOT" ;; *) @@ -178,7 +178,7 @@ cleanup() { trap cleanup EXIT trap 'echo "FAILED: interrupted"; exit 130' INT TERM -TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-corruption-torture.XXXXXX")" +TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-corruption-torture.XXXXXX")" TEST_HOME="$TEST_ROOT/home" WORKDIR="$TEST_ROOT/work" LOGDIR="$TEST_ROOT/logs" @@ -199,12 +199,12 @@ done ln -sf "$GIT_REAL" "$TEST_HOME/bin/git" printf '[core]\n\thooksPath = %s\n' "$TEST_HOME/git-hooks-none" >"$TEST_HOME/.gitconfig" PATH="$TEST_HOME/bin:$PATH" -# The PATH shim only holds host-side: inside `agentfs run` temp dirs are +# The PATH shim only holds host-side: inside `vfs run` temp dirs are # hidden, so sandboxed workloads must call "$GIT_REAL" by absolute path. export PATH GIT_REAL SESSION_ID="corruption-torture-$$" -RUN_DIR="$TEST_HOME/.agentfs/run/$SESSION_ID" +RUN_DIR="$TEST_HOME/.vfs/run/$SESSION_ID" DELTA_DB="$RUN_DIR/delta.db" FUSE_MNT="$RUN_DIR/mnt" STOP_MONITOR="$TEST_ROOT/stop-monitor" @@ -245,11 +245,11 @@ def run_integrity(path, *, uri=False): conn.close() def run_snapshot_integrity(): - # The live AgentFS owner keeps SQLite locks for the active FUSE session. + # The live Vfs owner keeps SQLite locks for the active FUSE session. # If direct read-only access is locked, check a same-basename copy of the # delta database and any WAL/SHM sidecars. A copy racing a writer may be # transiently inconsistent, so callers retry until their deadline. - with tempfile.TemporaryDirectory(prefix="agentfs-integrity-") as tmpdir: + with tempfile.TemporaryDirectory(prefix="vfs-integrity-") as tmpdir: snapshot = os.path.join(tmpdir, os.path.basename(db_path)) shutil.copy2(db_path, snapshot) for suffix in ("-wal", "-shm"): @@ -333,7 +333,7 @@ wait_for_owner_ready() { wait "$OWNER_PID" 2>/dev/null || true OWNER_PID="" if owner_failed_for_host_prereq; then - echo "SKIP: agentfs run prerequisites unavailable during owner startup" + echo "SKIP: vfs run prerequisites unavailable during owner startup" sed 's/^/ /' "$OWNER_LOG" | tail -n 40 exit 0 fi @@ -343,7 +343,7 @@ wait_for_owner_ready() { done if owner_failed_for_host_prereq; then - echo "SKIP: agentfs run prerequisites unavailable during owner startup" + echo "SKIP: vfs run prerequisites unavailable during owner startup" sed 's/^/ /' "$OWNER_LOG" | tail -n 40 exit 0 fi @@ -576,7 +576,7 @@ CARGO_HOME="$CARGO_HOME_FOR_TEST" \ RUSTUP_HOME="$RUSTUP_HOME_FOR_TEST" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN_FOR_TEST" \ cargo build --quiet --manifest-path "$CARGO_MANIFEST" >/dev/null 2>&1 || - fail "failed to build agentfs CLI before torture test" + fail "failed to build vfs CLI before torture test" start_owner wait_for_owner_ready @@ -602,13 +602,13 @@ integrity_check "$DELTA_DB" "$INTEGRITY_TIMEOUT" >>"$MONITOR_LOG" 2>&1 || run_final_session_checks terminate_owner -if [ "${AGENTFS_FUSE_URING:-}" = "1" ] && [ -r /sys/module/fuse/parameters/enable_uring ] && +if [ "${VFS_FUSE_URING:-}" = "1" ] && [ -r /sys/module/fuse/parameters/enable_uring ] && [ "$(cat /sys/module/fuse/parameters/enable_uring)" = "Y" ] && ! grep -q "advertising FUSE_OVER_IO_URING" "$OWNER_LOG"; then fail "uring leg did not advertise FUSE_OVER_IO_URING" fi -if [ "${AGENTFS_FUSE_URING:-}" = "0" ] && grep -q "advertising FUSE_OVER_IO_URING" "$OWNER_LOG"; then +if [ "${VFS_FUSE_URING:-}" = "0" ] && grep -q "advertising FUSE_OVER_IO_URING" "$OWNER_LOG"; then fail "legacy leg unexpectedly used FUSE_OVER_IO_URING" fi diff --git a/crates/agentfs-cli/tests/test-ephemeral-sidecar-cleanup.sh b/crates/vfs-cli/tests/test-ephemeral-sidecar-cleanup.sh similarity index 76% rename from crates/agentfs-cli/tests/test-ephemeral-sidecar-cleanup.sh rename to crates/vfs-cli/tests/test-ephemeral-sidecar-cleanup.sh index e8c1eeb6..af953648 100755 --- a/crates/agentfs-cli/tests/test-ephemeral-sidecar-cleanup.sh +++ b/crates/vfs-cli/tests/test-ephemeral-sidecar-cleanup.sh @@ -6,7 +6,7 @@ CLI_DIR="$(cd "$DIR/.." && pwd)" echo -n "TEST ephemeral sidecar cleanup... " -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-sidecar-cleanup.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-sidecar-cleanup.XXXXXX")" PINNED_TMP="$ROOT/tmp" WORK="$ROOT/work" MNT="$ROOT/mnt" @@ -26,24 +26,24 @@ cleanup() { } trap cleanup EXIT INT TERM -# Resolve the binary once: the mount leg must track the agentfs PID itself +# Resolve the binary once: the mount leg must track the vfs PID itself # and reap it before the test exits (a lingering FUSE connection draining in # the kernel wedges the next fuse-over-io_uring mount attempt forever). -if [ -n "${AGENTFS_BIN:-}" ]; then - BIN="$AGENTFS_BIN" +if [ -n "${VFS_BIN:-}" ]; then + BIN="$VFS_BIN" else cargo +nightly build --quiet --manifest-path "$CLI_DIR/Cargo.toml" || { - echo "FAILED: could not build agentfs" + echo "FAILED: could not build vfs" exit 1 } - BIN="$CLI_DIR/../../target/debug/agentfs" + BIN="$CLI_DIR/../../target/debug/vfs" fi if [ ! -x "$BIN" ]; then - echo "FAILED: agentfs binary not found at $BIN" + echo "FAILED: vfs binary not found at $BIN" exit 1 fi -run_agentfs() { +run_vfs() { "$BIN" "$@" } @@ -52,7 +52,7 @@ sidecar_count() { } spill_dir_count() { - find "$PINNED_TMP" -maxdepth 1 -type d -name 'agentfs-spill-*' -print | wc -l | tr -d ' ' + find "$PINNED_TMP" -maxdepth 1 -type d -name 'vfs-spill-*' -print | wc -l | tr -d ' ' } assert_no_sidecars() { @@ -68,8 +68,8 @@ assert_no_sidecars() { # The CLI's private TMPDIR spill dir must be cleaned up when the process # exits (no process of ours is running between phases). if [ "$spills" -ne 0 ]; then - echo "FAILED: residual agentfs-spill dirs after $phase" - find "$PINNED_TMP" -maxdepth 1 -type d -name 'agentfs-spill-*' -print | sort + echo "FAILED: residual vfs-spill dirs after $phase" + find "$PINNED_TMP" -maxdepth 1 -type d -name 'vfs-spill-*' -print | sort exit 1 fi } @@ -99,45 +99,45 @@ export PATH cd "$WORK" assert_no_sidecars "before" -run_agentfs init sidecar +run_vfs init sidecar assert_no_sidecars "init" -run_agentfs fs sidecar write /hello.txt "hello sidecar" +run_vfs fs sidecar write /hello.txt "hello sidecar" assert_no_sidecars "fs-write" -run_agentfs fs sidecar cat /hello.txt >/dev/null +run_vfs fs sidecar cat /hello.txt >/dev/null assert_no_sidecars "fs-cat" -run_agentfs run --session sidecar-run -- sh -c 'printf run-data > run.txt && cat run.txt' >/dev/null +run_vfs run --session sidecar-run -- sh -c 'printf run-data > run.txt && cat run.txt' >/dev/null assert_no_sidecars "run" # The private spill-dir TMPDIR override is process-internal: children of -# run/exec must see the user's TMPDIR, not the agentfs-spill-* dir. +# run/exec must see the user's TMPDIR, not the vfs-spill-* dir. # Mount tracing emits on stdout; the child's printf (no trailing newline) is # always the last line of the captured stream. -CHILD_TMPDIR="$(run_agentfs run --session sidecar-tmpdir -- sh -c 'printf %s "${TMPDIR:-}"' 2>/dev/null | tail -n 1)" +CHILD_TMPDIR="$(run_vfs run --session sidecar-tmpdir -- sh -c 'printf %s "${TMPDIR:-}"' 2>/dev/null | tail -n 1)" if [ "$CHILD_TMPDIR" != "$PINNED_TMP" ]; then echo "FAILED: run child saw TMPDIR='$CHILD_TMPDIR', expected '$PINNED_TMP'" exit 1 fi assert_no_sidecars "run-child-tmpdir" -CHILD_TMPDIR="$(run_agentfs exec sidecar sh -c 'printf %s "${TMPDIR:-}"' 2>/dev/null | tail -n 1)" +CHILD_TMPDIR="$(run_vfs exec sidecar sh -c 'printf %s "${TMPDIR:-}"' 2>/dev/null | tail -n 1)" if [ "$CHILD_TMPDIR" != "$PINNED_TMP" ]; then echo "FAILED: exec child saw TMPDIR='$CHILD_TMPDIR', expected '$PINNED_TMP'" exit 1 fi assert_no_sidecars "exec-child-tmpdir" # Stale spill dirs from SIGKILLed processes are reaped on the next CLI start. -mkdir -p "$PINNED_TMP/agentfs-spill-99999999-0" -run_agentfs fs sidecar cat /hello.txt >/dev/null -if [ -d "$PINNED_TMP/agentfs-spill-99999999-0" ]; then +mkdir -p "$PINNED_TMP/vfs-spill-99999999-0" +run_vfs fs sidecar cat /hello.txt >/dev/null +if [ -d "$PINNED_TMP/vfs-spill-99999999-0" ]; then echo "FAILED: stale dead-owner spill dir was not garbage-collected" exit 1 fi assert_no_sidecars "stale-spill-gc" -GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null run_agentfs run --session sidecar-git -- sh -c ' +GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null run_vfs run --session sidecar-git -- sh -c ' set -eu git init repo >/dev/null cd repo - git config user.email agentfs@example.invalid - git config user.name AgentFS + git config user.email vfs@example.invalid + git config user.name Vfs mkdir -p src for i in 1 2 3 4; do printf "token %s\n" "$i" > "src/file$i.txt" diff --git a/crates/agentfs-cli/tests/test-fuse-cache-invalidation.sh b/crates/vfs-cli/tests/test-fuse-cache-invalidation.sh similarity index 92% rename from crates/agentfs-cli/tests/test-fuse-cache-invalidation.sh rename to crates/vfs-cli/tests/test-fuse-cache-invalidation.sh index 5baef55c..b8ce61a5 100755 --- a/crates/agentfs-cli/tests/test-fuse-cache-invalidation.sh +++ b/crates/vfs-cli/tests/test-fuse-cache-invalidation.sh @@ -13,7 +13,7 @@ DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" TEST_AGENT_ID="test-fuse-cache-inval-agent" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-cache-inval.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-cache-inval.XXXXXX")" MOUNTPOINT="$ROOT/mnt" MOUNT_PID="" @@ -37,37 +37,37 @@ fail() { exit 1 } -# Resolve the binary once: background mount legs must track the agentfs PID +# Resolve the binary once: background mount legs must track the vfs PID # itself (backgrounding a wrapper orphans the real process on cleanup kill). -if [ -n "${AGENTFS_BIN:-}" ]; then - BIN="$AGENTFS_BIN" +if [ -n "${VFS_BIN:-}" ]; then + BIN="$VFS_BIN" else cargo build --quiet --manifest-path "$CLI_DIR/Cargo.toml" || { - echo "FAILED: could not build agentfs" + echo "FAILED: could not build vfs" exit 1 } - BIN="$CLI_DIR/../../target/debug/agentfs" + BIN="$CLI_DIR/../../target/debug/vfs" fi if [ ! -x "$BIN" ]; then - echo "FAILED: agentfs binary not found at $BIN" + echo "FAILED: vfs binary not found at $BIN" exit 1 fi -run_agentfs() { +run_vfs() { "$BIN" "$@" } -# The session DB lands under $ROOT/.agentfs instead of the repo working tree. +# The session DB lands under $ROOT/.vfs instead of the repo working tree. cd "$ROOT" # Initialize the database -run_agentfs init "$TEST_AGENT_ID" > /dev/null 2>&1 +run_vfs init "$TEST_AGENT_ID" > /dev/null 2>&1 # Create mountpoint mkdir -p "$MOUNTPOINT" # Mount in foreground mode (background it ourselves so we can control it) -"$BIN" mount ".agentfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground & +"$BIN" mount ".vfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground & MOUNT_PID=$! # Wait for mount to be ready diff --git a/crates/agentfs-cli/tests/test-fuse-sigint-panic-census.sh b/crates/vfs-cli/tests/test-fuse-sigint-panic-census.sh similarity index 90% rename from crates/agentfs-cli/tests/test-fuse-sigint-panic-census.sh rename to crates/vfs-cli/tests/test-fuse-sigint-panic-census.sh index 700a4416..13939b85 100755 --- a/crates/agentfs-cli/tests/test-fuse-sigint-panic-census.sh +++ b/crates/vfs-cli/tests/test-fuse-sigint-panic-census.sh @@ -14,7 +14,7 @@ echo -n "TEST FUSE SIGINT panic census... " DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" CARGO_MANIFEST="$CLI_DIR/Cargo.toml" -AGENTFS_BIN="${AGENTFS_BIN:-}" +VFS_BIN="${VFS_BIN:-}" CYCLES="${PANIC_CENSUS_CYCLES:-8}" TEARDOWN_TIMEOUT="${PANIC_CENSUS_TEARDOWN_TIMEOUT:-10}" WORKLOAD_SECONDS="${PANIC_CENSUS_WORKLOAD_SECONDS:-1.5}" @@ -28,7 +28,7 @@ MOUNT_PID="" WRITER_PID="" CURRENT_MNT="" PANIC_LINES="$( - mktemp "${TMPDIR:-/tmp}/agentfs-panic-census-lines.XXXXXX" + mktemp "${TMPDIR:-/tmp}/vfs-panic-census-lines.XXXXXX" )" skip() { @@ -75,18 +75,18 @@ fi validate_positive_integer PANIC_CENSUS_CYCLES "$CYCLES" validate_positive_integer PANIC_CENSUS_TEARDOWN_TIMEOUT "$TEARDOWN_TIMEOUT" -if [ -z "$AGENTFS_BIN" ]; then +if [ -z "$VFS_BIN" ]; then cargo +nightly build --quiet --manifest-path "$CARGO_MANIFEST" >/dev/null 2>&1 || - fail "failed to build agentfs CLI" + fail "failed to build vfs CLI" TARGET_DIR="$( cargo +nightly metadata --format-version 1 --no-deps --manifest-path "$CARGO_MANIFEST" | python3 -c 'import json, sys; print(json.load(sys.stdin)["target_directory"])' )" || fail "failed to resolve cargo target directory" - AGENTFS_BIN="$TARGET_DIR/debug/agentfs" + VFS_BIN="$TARGET_DIR/debug/vfs" fi -if [ ! -x "$AGENTFS_BIN" ]; then - fail "AGENTFS_BIN is not executable: $AGENTFS_BIN" +if [ ! -x "$VFS_BIN" ]; then + fail "VFS_BIN is not executable: $VFS_BIN" fi if [ "$CYCLES" -lt 5 ]; then @@ -138,7 +138,7 @@ cleanup_all() { cleanup_current if [ -n "$TEST_ROOT" ] && [ -d "$TEST_ROOT" ]; then case "$TEST_ROOT" in - "${TMPDIR:-/tmp}"/agentfs-panic-census.*) + "${TMPDIR:-/tmp}"/vfs-panic-census.*) rm -rf "$TEST_ROOT" ;; *) @@ -222,17 +222,17 @@ assert_no_session_residue() { fail "$residue_label left mount residue at $residue_mountpoint_path" fi if ps -e -o pid= -o args= | awk -v id="$residue_session_id" ' - index($0, id) && index($0, "agentfs") && !index($0, "awk -v id=") { + index($0, id) && index($0, "vfs") && !index($0, "awk -v id=") { print found = 1 } END { exit found ? 0 : 1 } - ' >/tmp/agentfs-panic-census-ps.$$ 2>/dev/null; then - cat /tmp/agentfs-panic-census-ps.$$ - rm -f /tmp/agentfs-panic-census-ps.$$ - fail "$residue_label left agentfs process residue for $residue_session_id" + ' >/tmp/vfs-panic-census-ps.$$ 2>/dev/null; then + cat /tmp/vfs-panic-census-ps.$$ + rm -f /tmp/vfs-panic-census-ps.$$ + fail "$residue_label left vfs process residue for $residue_session_id" fi - rm -f /tmp/agentfs-panic-census-ps.$$ + rm -f /tmp/vfs-panic-census-ps.$$ } run_cycle() { @@ -252,7 +252,7 @@ run_cycle() { env HOME="$home_dir" \ XDG_CACHE_HOME="$home_dir/.cache" \ XDG_CONFIG_HOME="$home_dir/.config" \ - "$AGENTFS_BIN" init "$id" + "$VFS_BIN" init "$id" ) >/dev/null 2>&1 || fail "$cycle_label cycle $cycle_index init failed" ( @@ -263,9 +263,9 @@ run_cycle() { CARGO_HOME="$CARGO_HOME_FOR_TEST" \ RUSTUP_HOME="$RUSTUP_HOME_FOR_TEST" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN_FOR_TEST" \ - AGENTFS_FUSE_URING="$cycle_leg" \ - RUST_LOG=agentfs=info \ - "$AGENTFS_BIN" mount "$id" "$mnt" --backend fuse --foreground + VFS_FUSE_URING="$cycle_leg" \ + RUST_LOG=vfs=info \ + "$VFS_BIN" mount "$id" "$mnt" --backend fuse --foreground ) >"$log" 2>&1 & MOUNT_PID=$! CURRENT_MNT="$mnt" @@ -316,7 +316,7 @@ run_cycle() { assert_no_session_residue "$cycle_label cycle $cycle_index" "$id" "$mnt" } -TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-panic-census.XXXXXX")" +TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-panic-census.XXXXXX")" RUN_URING_LEG=1 URING_CYCLES_RUN=0 LEGACY_CYCLES_RUN=0 diff --git a/crates/agentfs-cli/tests/test-init.sh b/crates/vfs-cli/tests/test-init.sh similarity index 53% rename from crates/agentfs-cli/tests/test-init.sh rename to crates/vfs-cli/tests/test-init.sh index 96aedc15..a7015a0e 100755 --- a/crates/agentfs-cli/tests/test-init.sh +++ b/crates/vfs-cli/tests/test-init.sh @@ -7,7 +7,7 @@ DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" TEST_AGENT_ID="test-agent" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-init.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-init.XXXXXX")" cleanup() { rm -rf "$ROOT" @@ -19,44 +19,44 @@ fail() { exit 1 } -run_agentfs() { - if [ -n "${AGENTFS_BIN:-}" ]; then - "$AGENTFS_BIN" "$@" +run_vfs() { + if [ -n "${VFS_BIN:-}" ]; then + "$VFS_BIN" "$@" else cargo run --quiet --manifest-path "$CLI_DIR/Cargo.toml" -- "$@" fi } -# agentfs init creates its DB under CWD/.agentfs, so run from the temp root. +# vfs init creates its DB under CWD/.vfs, so run from the temp root. cd "$ROOT" -output=$(run_agentfs init "$TEST_AGENT_ID" 2>&1) || +output=$(run_vfs init "$TEST_AGENT_ID" 2>&1) || fail "init command failed Output was: $output" -[ -d .agentfs ] || fail ".agentfs directory was not created +[ -d .vfs ] || fail ".vfs directory was not created Output was: $output" -[ -f ".agentfs/$TEST_AGENT_ID.db" ] || - fail "agent database was not created in .agentfs directory +[ -f ".vfs/$TEST_AGENT_ID.db" ] || + fail "agent database was not created in .vfs directory Output was: $output" -echo "$output" | grep -q "Created agent filesystem: .agentfs/$TEST_AGENT_ID.db" || +echo "$output" | grep -q "Created agent filesystem: .vfs/$TEST_AGENT_ID.db" || fail "Expected success message not found in output Output was: $output" # Running init again must fail without --force. -if run_agentfs init "$TEST_AGENT_ID" 2>&1 | grep -q "already exists"; then +if run_vfs init "$TEST_AGENT_ID" 2>&1 | grep -q "already exists"; then : # Expected behavior else fail "init should fail when agent database already exists" fi -output=$(run_agentfs init "$TEST_AGENT_ID" --force 2>&1) || +output=$(run_vfs init "$TEST_AGENT_ID" --force 2>&1) || fail "init --force command failed Output was: $output" -echo "$output" | grep -q "Created agent filesystem: .agentfs/$TEST_AGENT_ID.db" || +echo "$output" | grep -q "Created agent filesystem: .vfs/$TEST_AGENT_ID.db" || fail "Expected success message not found in init --force output Output was: $output" diff --git a/crates/agentfs-cli/tests/test-linux-syscalls.sh b/crates/vfs-cli/tests/test-linux-syscalls.sh similarity index 94% rename from crates/agentfs-cli/tests/test-linux-syscalls.sh rename to crates/vfs-cli/tests/test-linux-syscalls.sh index ff2f800e..da359749 100755 --- a/crates/agentfs-cli/tests/test-linux-syscalls.sh +++ b/crates/vfs-cli/tests/test-linux-syscalls.sh @@ -2,7 +2,7 @@ # # Test syscalls directly on Linux (baseline). # -# This establishes the expected behavior that AgentFS should match. +# This establishes the expected behavior that Vfs should match. # set -e diff --git a/crates/agentfs-cli/tests/test-mcp-server.sh b/crates/vfs-cli/tests/test-mcp-server.sh similarity index 96% rename from crates/agentfs-cli/tests/test-mcp-server.sh rename to crates/vfs-cli/tests/test-mcp-server.sh index 90e6addc..1233e620 100755 --- a/crates/agentfs-cli/tests/test-mcp-server.sh +++ b/crates/vfs-cli/tests/test-mcp-server.sh @@ -4,7 +4,7 @@ # - tools/list equals the dispatchable tool set exactly (kv_list dispatches, # phantom help tools rmdir/rm/unlink/copy_file are gone from help) # - notifications/initialized is accepted without a response (strict clients) -# - every tools/call writes a tool_calls audit row that `agentfs timeline` +# - every tools/call writes a tool_calls audit row that `vfs timeline` # renders, including while the server process is still running (cross-flow) # - write_file preserves the mode of existing files, creates new files 0644, # and its data is durable after the server exits @@ -20,7 +20,7 @@ CLI_DIR="$(cd "$DIR/.." && pwd)" echo -n "TEST mcp server... " -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-mcp-server.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-mcp-server.XXXXXX")" cleanup() { rm -rf "$ROOT" @@ -32,18 +32,18 @@ fail() { exit 1 } -if [ -n "${AGENTFS_BIN:-}" ]; then - BIN="$AGENTFS_BIN" +if [ -n "${VFS_BIN:-}" ]; then + BIN="$VFS_BIN" else cargo +nightly build --quiet --manifest-path "$CLI_DIR/Cargo.toml" \ - || fail "could not build agentfs" - BIN="$CLI_DIR/../../target/debug/agentfs" + || fail "could not build vfs" + BIN="$CLI_DIR/../../target/debug/vfs" fi -[ -x "$BIN" ] || fail "agentfs binary not found at $BIN" +[ -x "$BIN" ] || fail "vfs binary not found at $BIN" cd "$ROOT" "$BIN" init mcp >/dev/null 2>&1 || fail "init failed" -DB="$ROOT/.agentfs/mcp.db" +DB="$ROOT/.vfs/mcp.db" # --- Help must advertise exactly the real tool surface ---------------------- "$BIN" serve mcp --help >"$ROOT/help.out" 2>&1 || fail "serve mcp --help failed" diff --git a/crates/agentfs-cli/tests/test-migrate-consolidation.sh b/crates/vfs-cli/tests/test-migrate-consolidation.sh similarity index 81% rename from crates/agentfs-cli/tests/test-migrate-consolidation.sh rename to crates/vfs-cli/tests/test-migrate-consolidation.sh index 98fc11da..3cc0a9a0 100755 --- a/crates/agentfs-cli/tests/test-migrate-consolidation.sh +++ b/crates/vfs-cli/tests/test-migrate-consolidation.sh @@ -1,7 +1,7 @@ #!/usr/bin/env sh # # Migrate consolidation regression (D6): -# - one `agentfs migrate ` lands every supported old schema at +# - one `vfs migrate ` lands every supported old schema at # CURRENT in a single invocation and is idempotent # - open paths (fs ls) reject old schemas with guidance naming that command # - encrypted old databases migrate in place with --key/--cipher, leaving no @@ -21,7 +21,7 @@ ENC_CIPHER="aes256gcm" echo -n "TEST migrate consolidation... " -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-migrate-consolidation.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-migrate-consolidation.XXXXXX")" MNT="$ROOT/mnt" PINNED_TMP="$ROOT/pinned-tmp" mkdir -p "$MNT" "$PINNED_TMP" "$ROOT/home" @@ -43,23 +43,23 @@ fail() { exit 1 } -# Resolve the binary once: background legs must track the agentfs PID itself +# Resolve the binary once: background legs must track the vfs PID itself # (backgrounding a wrapper orphans the real process on cleanup kill). -if [ -n "${AGENTFS_BIN:-}" ]; then - BIN="$AGENTFS_BIN" +if [ -n "${VFS_BIN:-}" ]; then + BIN="$VFS_BIN" else cargo +nightly build --quiet --manifest-path "$CLI_DIR/Cargo.toml" || { - echo "FAILED: could not build agentfs" + echo "FAILED: could not build vfs" exit 1 } - BIN="$CLI_DIR/../../target/debug/agentfs" + BIN="$CLI_DIR/../../target/debug/vfs" fi if [ ! -x "$BIN" ]; then - echo "FAILED: agentfs binary not found at $BIN" + echo "FAILED: vfs binary not found at $BIN" exit 1 fi -run_agentfs() { +run_vfs() { "$BIN" "$@" } @@ -75,10 +75,10 @@ EOF [ -f "$FIXTURES/v0_0.db" ] || fail "missing committed fixtures under $FIXTURES" # --- Obsolete split command is gone ----------------------------------------- -if run_agentfs migrate-v0-5 --help >"$ROOT/mv5.out" 2>&1; then +if run_vfs migrate-v0-5 --help >"$ROOT/mv5.out" 2>&1; then fail "migrate-v0-5 still exists as a command" fi -if run_agentfs --help 2>/dev/null | grep -q 'migrate-v0-5'; then +if run_vfs --help 2>/dev/null | grep -q 'migrate-v0-5'; then fail "--help still advertises migrate-v0-5" fi @@ -88,15 +88,15 @@ for name in v0_0 v0_2 v0_4; do cp "$FIXTURES/$name.db" "$DB" # Open paths must refuse the old schema and name the command that fixes it. - if run_agentfs fs "$DB" ls / >"$ROOT/$name-ls-before.out" 2>"$ROOT/$name-ls-before.err"; then + if run_vfs fs "$DB" ls / >"$ROOT/$name-ls-before.out" 2>"$ROOT/$name-ls-before.err"; then fail "$name: fs ls opened an old-schema database without migration" fi - grep -q "agentfs migrate $DB" "$ROOT/$name-ls-before.err" \ - || fail "$name: schema-mismatch guidance does not name 'agentfs migrate $DB' (stderr: $(cat "$ROOT/$name-ls-before.err"))" + grep -q "vfs migrate $DB" "$ROOT/$name-ls-before.err" \ + || fail "$name: schema-mismatch guidance does not name 'vfs migrate $DB' (stderr: $(cat "$ROOT/$name-ls-before.err"))" grep -q 'migrate-v0-5' "$ROOT/$name-ls-before.err" \ && fail "$name: guidance mentions the deleted migrate-v0-5 command" - run_agentfs migrate "$DB" >"$ROOT/$name-migrate.out" 2>&1 \ + run_vfs migrate "$DB" >"$ROOT/$name-migrate.out" 2>&1 \ || fail "$name: migrate failed: $(cat "$ROOT/$name-migrate.out")" grep -q 'Migration completed successfully.' "$ROOT/$name-migrate.out" \ || fail "$name: migrate output missing completion line" @@ -107,14 +107,14 @@ for name in v0_0 v0_2 v0_4; do [ "$UV" = "5" ] || fail "$name: user_version after migrate is $UV, expected 5" # Idempotent second run. - run_agentfs migrate "$DB" >"$ROOT/$name-migrate2.out" 2>&1 \ + run_vfs migrate "$DB" >"$ROOT/$name-migrate2.out" 2>&1 \ || fail "$name: second migrate failed" grep -q 'Database is already at schema 0.5.' "$ROOT/$name-migrate2.out" \ || fail "$name: second migrate is not idempotent: $(cat "$ROOT/$name-migrate2.out")" - run_agentfs integrity "$DB" >"$ROOT/$name-integrity.out" 2>&1 \ + run_vfs integrity "$DB" >"$ROOT/$name-integrity.out" 2>&1 \ || fail "$name: integrity failed after migrate: $(cat "$ROOT/$name-integrity.out")" - run_agentfs fs "$DB" ls / >"$ROOT/$name-ls-after.out" 2>&1 \ + run_vfs fs "$DB" ls / >"$ROOT/$name-ls-after.out" 2>&1 \ || fail "$name: fs ls failed after migrate" grep -q 'small.txt' "$ROOT/$name-ls-after.out" \ || fail "$name: migrated filesystem lost its contents" @@ -125,7 +125,7 @@ ENC_DB="$ROOT/v0_4-encrypted.db" cp "$FIXTURES/v0_4-encrypted.db" "$ENC_DB" ( export TMPDIR="$PINNED_TMP" TMP="$PINNED_TMP" TEMP="$PINNED_TMP" - run_agentfs migrate "$ENC_DB" --key "$ENC_KEY" --cipher "$ENC_CIPHER" + run_vfs migrate "$ENC_DB" --key "$ENC_KEY" --cipher "$ENC_CIPHER" ) >"$ROOT/enc-migrate.out" 2>&1 \ || fail "encrypted migrate failed: $(cat "$ROOT/enc-migrate.out")" grep -q 'Migration completed successfully.' "$ROOT/enc-migrate.out" \ @@ -136,13 +136,13 @@ LEFTOVER="$(find "$PINNED_TMP" -mindepth 1 | head -5)" STRAYS="$(find "$ROOT" -maxdepth 1 -name 'v0_4-encrypted.db*' ! -name 'v0_4-encrypted.db' | head -5)" [ -z "$STRAYS" ] || fail "encrypted migrate left sidecar/copy artifacts: $STRAYS" -run_agentfs fs --key "$ENC_KEY" --cipher "$ENC_CIPHER" "$ENC_DB" ls / \ +run_vfs fs --key "$ENC_KEY" --cipher "$ENC_CIPHER" "$ENC_DB" ls / \ >"$ROOT/enc-ls.out" 2>&1 || fail "encrypted fs ls with key failed" grep -q 'small.txt' "$ROOT/enc-ls.out" || fail "encrypted migrated fs lost its contents" -if run_agentfs fs "$ENC_DB" ls / >/dev/null 2>&1; then +if run_vfs fs "$ENC_DB" ls / >/dev/null 2>&1; then fail "encrypted database opened without the key" fi -run_agentfs integrity "$ENC_DB" --key "$ENC_KEY" --cipher "$ENC_CIPHER" >/dev/null 2>&1 \ +run_vfs integrity "$ENC_DB" --key "$ENC_KEY" --cipher "$ENC_CIPHER" >/dev/null 2>&1 \ || fail "encrypted integrity with key failed" # --- Cross-flow: migrated old DB survives a mounted git workload ------------ @@ -193,8 +193,8 @@ mountpoint -q "$MNT" || fail "migrated database did not mount: $(tail -5 "$ROOT/ fi done git init -q . - git config user.email agentfs@example.invalid - git config user.name AgentFS + git config user.email vfs@example.invalid + git config user.name Vfs printf 'one\n' > a.txt printf 'two\n' > b.txt git add . @@ -213,7 +213,7 @@ done kill -0 "$MOUNT_PID" 2>/dev/null && fail "mount owner did not exit after unmount" MOUNT_PID="" -run_agentfs integrity "$WORK_DB" --json >"$ROOT/final-integrity.json" 2>"$ROOT/final-integrity.err" \ +run_vfs integrity "$WORK_DB" --json >"$ROOT/final-integrity.json" 2>"$ROOT/final-integrity.err" \ || fail "integrity after mounted workload failed: $(cat "$ROOT/final-integrity.err")" python3 - "$ROOT/final-integrity.json" <<'EOF' || fail "integrity --json did not report ok:true" import json, sys diff --git a/crates/agentfs-cli/tests/test-mount.sh b/crates/vfs-cli/tests/test-mount.sh similarity index 88% rename from crates/agentfs-cli/tests/test-mount.sh rename to crates/vfs-cli/tests/test-mount.sh index dccfab41..5fec126f 100755 --- a/crates/agentfs-cli/tests/test-mount.sh +++ b/crates/vfs-cli/tests/test-mount.sh @@ -7,7 +7,7 @@ DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" TEST_AGENT_ID="test-mount-agent" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-mount.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-mount.XXXXXX")" MOUNTPOINT="$ROOT/mnt" CYCLE_MOUNTPOINT="$ROOT/mnt-cycle" MOUNT_PID="" @@ -38,35 +38,35 @@ fail() { exit 1 } -# Resolve the binary once: background mount legs must track the agentfs PID +# Resolve the binary once: background mount legs must track the vfs PID # itself (backgrounding a wrapper orphans the real process on cleanup kill). -if [ -n "${AGENTFS_BIN:-}" ]; then - BIN="$AGENTFS_BIN" +if [ -n "${VFS_BIN:-}" ]; then + BIN="$VFS_BIN" else cargo build --quiet --manifest-path "$CLI_DIR/Cargo.toml" || { - echo "FAILED: could not build agentfs" + echo "FAILED: could not build vfs" exit 1 } - BIN="$CLI_DIR/../../target/debug/agentfs" + BIN="$CLI_DIR/../../target/debug/vfs" fi if [ ! -x "$BIN" ]; then - echo "FAILED: agentfs binary not found at $BIN" + echo "FAILED: vfs binary not found at $BIN" exit 1 fi -run_agentfs() { +run_vfs() { "$BIN" "$@" } -# The session DB lands under $ROOT/.agentfs instead of the repo working tree. +# The session DB lands under $ROOT/.vfs instead of the repo working tree. cd "$ROOT" -run_agentfs init "$TEST_AGENT_ID" > /dev/null 2>&1 +run_vfs init "$TEST_AGENT_ID" > /dev/null 2>&1 mkdir -p "$MOUNTPOINT" # Mount in foreground mode (background it ourselves so we can control it) -"$BIN" mount ".agentfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground & +"$BIN" mount ".vfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground & MOUNT_PID=$! # Wait for mount to be ready @@ -82,9 +82,9 @@ done mountpoint -q "$MOUNTPOINT" 2>/dev/null || fail "mount did not become ready in time" -# Test that 'agentfs mount' (no args) lists our mount -run_agentfs mount 2>/dev/null | grep -q "$MOUNTPOINT" || - fail "'agentfs mount' did not list our mountpoint" +# Test that 'vfs mount' (no args) lists our mount +run_vfs mount 2>/dev/null | grep -q "$MOUNTPOINT" || + fail "'vfs mount' did not list our mountpoint" # Write a file through the FUSE mount echo "hello from fuse mount" > "$MOUNTPOINT/hello.txt" @@ -150,8 +150,8 @@ CYCLE_WINDOW="${MOUNT_CYCLE_WINDOW:-25}" for cycle in 1 2 3; do CYCLE_MNT="$CYCLE_MOUNTPOINT.$cycle" mkdir -p "$CYCLE_MNT" - run_agentfs init "cycle-$cycle" > /dev/null 2>&1 - "$BIN" mount ".agentfs/cycle-$cycle.db" "$CYCLE_MNT" --foreground \ + run_vfs init "cycle-$cycle" > /dev/null 2>&1 + "$BIN" mount ".vfs/cycle-$cycle.db" "$CYCLE_MNT" --foreground \ >"$ROOT/cycle.$cycle.log" 2>&1 & CYCLE_PID=$! CYCLE_PIDS="$CYCLE_PIDS $CYCLE_PID" diff --git a/crates/agentfs-cli/tests/test-overlay-base-dir-rename-exdev.sh b/crates/vfs-cli/tests/test-overlay-base-dir-rename-exdev.sh similarity index 90% rename from crates/agentfs-cli/tests/test-overlay-base-dir-rename-exdev.sh rename to crates/vfs-cli/tests/test-overlay-base-dir-rename-exdev.sh index 13f7fe1e..a9c0ec82 100755 --- a/crates/agentfs-cli/tests/test-overlay-base-dir-rename-exdev.sh +++ b/crates/vfs-cli/tests/test-overlay-base-dir-rename-exdev.sh @@ -7,7 +7,7 @@ DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" TEST_AGENT_ID="test-overlay-base-dir-rename-agent" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-overlay-rename.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-overlay-rename.XXXXXX")" MOUNTPOINT="$ROOT/mnt" BASEDIR="$ROOT/base" MOUNT_LOG="$ROOT/mount.log" @@ -22,27 +22,27 @@ cleanup() { trap cleanup EXIT INT TERM -# Resolve the binary once: background mount legs must track the agentfs PID +# Resolve the binary once: background mount legs must track the vfs PID # itself (backgrounding a wrapper orphans the real process on cleanup kill). -if [ -n "${AGENTFS_BIN:-}" ]; then - BIN="$AGENTFS_BIN" +if [ -n "${VFS_BIN:-}" ]; then + BIN="$VFS_BIN" else cargo build --quiet --manifest-path "$CLI_DIR/Cargo.toml" || { - echo "FAILED: could not build agentfs" + echo "FAILED: could not build vfs" exit 1 } - BIN="$CLI_DIR/../../target/debug/agentfs" + BIN="$CLI_DIR/../../target/debug/vfs" fi if [ ! -x "$BIN" ]; then - echo "FAILED: agentfs binary not found at $BIN" + echo "FAILED: vfs binary not found at $BIN" exit 1 fi -run_agentfs() { +run_vfs() { "$BIN" "$@" } -# The session DB lands under $ROOT/.agentfs instead of the repo working tree. +# The session DB lands under $ROOT/.vfs instead of the repo working tree. cd "$ROOT" mkdir -p \ @@ -60,13 +60,13 @@ printf "merged probe nested\n" > "$BASEDIR/merged_probe/sub/nested.txt" printf "merged mv root\n" > "$BASEDIR/merged_mv_src/root.txt" printf "merged mv nested\n" > "$BASEDIR/merged_mv_src/sub/nested.txt" -if ! output=$(run_agentfs init "$TEST_AGENT_ID" --base "$BASEDIR" 2>&1); then +if ! output=$(run_vfs init "$TEST_AGENT_ID" --base "$BASEDIR" 2>&1); then echo "FAILED: init with --base failed" echo "Output was: $output" exit 1 fi -"$BIN" mount ".agentfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground >"$MOUNT_LOG" 2>&1 & +"$BIN" mount ".vfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground >"$MOUNT_LOG" 2>&1 & MOUNT_PID=$! MAX_WAIT=10 diff --git a/crates/agentfs-cli/tests/test-overlay-delta-in-base-dir.sh b/crates/vfs-cli/tests/test-overlay-delta-in-base-dir.sh similarity index 88% rename from crates/agentfs-cli/tests/test-overlay-delta-in-base-dir.sh rename to crates/vfs-cli/tests/test-overlay-delta-in-base-dir.sh index 45a47fda..97ed40ed 100755 --- a/crates/agentfs-cli/tests/test-overlay-delta-in-base-dir.sh +++ b/crates/vfs-cli/tests/test-overlay-delta-in-base-dir.sh @@ -7,7 +7,7 @@ DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" TEST_AGENT_ID="test-overlay-delta-in-base-dir-agent" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-overlay-delta.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-overlay-delta.XXXXXX")" MOUNTPOINT="$ROOT/mnt" BASEDIR="$ROOT/base" MOUNT_PID="" @@ -32,27 +32,27 @@ fail() { exit 1 } -# Resolve the binary once: background mount legs must track the agentfs PID +# Resolve the binary once: background mount legs must track the vfs PID # itself (backgrounding a wrapper orphans the real process on cleanup kill). -if [ -n "${AGENTFS_BIN:-}" ]; then - BIN="$AGENTFS_BIN" +if [ -n "${VFS_BIN:-}" ]; then + BIN="$VFS_BIN" else cargo build --quiet --manifest-path "$CLI_DIR/Cargo.toml" || { - echo "FAILED: could not build agentfs" + echo "FAILED: could not build vfs" exit 1 } - BIN="$CLI_DIR/../../target/debug/agentfs" + BIN="$CLI_DIR/../../target/debug/vfs" fi if [ ! -x "$BIN" ]; then - echo "FAILED: agentfs binary not found at $BIN" + echo "FAILED: vfs binary not found at $BIN" exit 1 fi -run_agentfs() { +run_vfs() { "$BIN" "$@" } -# The session DB lands under $ROOT/.agentfs instead of the repo working tree. +# The session DB lands under $ROOT/.vfs instead of the repo working tree. cd "$ROOT" # Create base directory with a subdirectory (simulating .git) @@ -61,14 +61,14 @@ echo "[core]" > "$BASEDIR/.git/config" echo "ref: refs/heads/main" > "$BASEDIR/.git/HEAD" # Initialize the database with --base for overlay -output=$(run_agentfs init "$TEST_AGENT_ID" --base "$BASEDIR" 2>&1) || +output=$(run_vfs init "$TEST_AGENT_ID" --base "$BASEDIR" 2>&1) || fail "init with --base failed Output was: $output" mkdir -p "$MOUNTPOINT" # Mount in foreground mode (background it ourselves so we can control it) -"$BIN" mount ".agentfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground & +"$BIN" mount ".vfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground & MOUNT_PID=$! # Wait for mount to be ready diff --git a/crates/agentfs-cli/tests/test-overlay-whiteout.sh b/crates/vfs-cli/tests/test-overlay-whiteout.sh similarity index 81% rename from crates/agentfs-cli/tests/test-overlay-whiteout.sh rename to crates/vfs-cli/tests/test-overlay-whiteout.sh index a46cd59e..55c52442 100755 --- a/crates/agentfs-cli/tests/test-overlay-whiteout.sh +++ b/crates/vfs-cli/tests/test-overlay-whiteout.sh @@ -7,7 +7,7 @@ DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" TEST_AGENT_ID="test-overlay-whiteout-agent" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-overlay-whiteout.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-overlay-whiteout.XXXXXX")" MOUNTPOINT="$ROOT/mnt" BASEDIR="$ROOT/base" MOUNT_PID="" @@ -32,23 +32,23 @@ fail() { exit 1 } -# Resolve the binary once: background mount legs must track the agentfs PID +# Resolve the binary once: background mount legs must track the vfs PID # itself (backgrounding a wrapper orphans the real process on cleanup kill). -if [ -n "${AGENTFS_BIN:-}" ]; then - BIN="$AGENTFS_BIN" +if [ -n "${VFS_BIN:-}" ]; then + BIN="$VFS_BIN" else cargo build --quiet --manifest-path "$CLI_DIR/Cargo.toml" || { - echo "FAILED: could not build agentfs" + echo "FAILED: could not build vfs" exit 1 } - BIN="$CLI_DIR/../../target/debug/agentfs" + BIN="$CLI_DIR/../../target/debug/vfs" fi if [ ! -x "$BIN" ]; then - echo "FAILED: agentfs binary not found at $BIN" + echo "FAILED: vfs binary not found at $BIN" exit 1 fi -run_agentfs() { +run_vfs() { "$BIN" "$@" } @@ -64,7 +64,7 @@ wait_mounted() { mountpoint -q "$MOUNTPOINT" 2>/dev/null } -# The session DB lands under $ROOT/.agentfs instead of the repo working tree. +# The session DB lands under $ROOT/.vfs instead of the repo working tree. cd "$ROOT" # Create base directory with a test file @@ -72,14 +72,14 @@ mkdir -p "$BASEDIR" echo "original content" > "$BASEDIR/testfile.txt" # Initialize the database with --base for overlay -output=$(run_agentfs init "$TEST_AGENT_ID" --base "$BASEDIR" 2>&1) || +output=$(run_vfs init "$TEST_AGENT_ID" --base "$BASEDIR" 2>&1) || fail "init with --base failed Output was: $output" mkdir -p "$MOUNTPOINT" # Mount in foreground mode (background it ourselves so we can control it) -"$BIN" mount ".agentfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground & +"$BIN" mount ".vfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground & MOUNT_PID=$! wait_mounted || fail "mount did not become ready in time" @@ -104,7 +104,7 @@ wait $MOUNT_PID 2>/dev/null || true MOUNT_PID="" # Remount to test persistence -"$BIN" mount ".agentfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground & +"$BIN" mount ".vfs/${TEST_AGENT_ID}.db" "$MOUNTPOINT" --foreground & MOUNT_PID=$! wait_mounted || fail "remount did not become ready in time" diff --git a/crates/agentfs-cli/tests/test-profile-error-summary.sh b/crates/vfs-cli/tests/test-profile-error-summary.sh similarity index 68% rename from crates/agentfs-cli/tests/test-profile-error-summary.sh rename to crates/vfs-cli/tests/test-profile-error-summary.sh index 5a6822bb..32156ff7 100755 --- a/crates/agentfs-cli/tests/test-profile-error-summary.sh +++ b/crates/vfs-cli/tests/test-profile-error-summary.sh @@ -6,15 +6,15 @@ echo -n "TEST profile summary on CLI error... " DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" -run_agentfs() { - if [ -n "${AGENTFS_BIN:-}" ]; then - "$AGENTFS_BIN" "$@" +run_vfs() { + if [ -n "${VFS_BIN:-}" ]; then + "$VFS_BIN" "$@" else cargo run --quiet --manifest-path "$CLI_DIR/Cargo.toml" -- "$@" fi } -output_file="$(mktemp "${TMPDIR:-/tmp}/agentfs-profile-error.XXXXXX")" +output_file="$(mktemp "${TMPDIR:-/tmp}/vfs-profile-error.XXXXXX")" trap 'rm -f "$output_file"' EXIT assert_one_summary_on_failure() { @@ -23,7 +23,7 @@ assert_one_summary_on_failure() { : >"$output_file" set +e - AGENTFS_PROFILE=1 "$@" >"$output_file" 2>&1 + VFS_PROFILE=1 "$@" >"$output_file" 2>&1 status=$? set -e @@ -33,7 +33,7 @@ assert_one_summary_on_failure() { exit 1 fi - summary_count="$(grep -c '"event":"agentfs_profile_summary"' "$output_file" || true)" + summary_count="$(grep -c '"event":"vfs_profile_summary"' "$output_file" || true)" if [ "$summary_count" -ne 1 ]; then echo "FAILED: expected exactly one profile summary for $description, saw $summary_count" cat "$output_file" @@ -43,14 +43,14 @@ assert_one_summary_on_failure() { assert_one_summary_on_failure \ "invalid clap arguments" \ - run_agentfs --definitely-not-an-agentfs-option + run_vfs --definitely-not-an-vfs-option assert_one_summary_on_failure \ "invalid encryption options" \ - run_agentfs fs --key deadbeef /tmp/agentfs-profile-error.db ls / + run_vfs fs --key deadbeef /tmp/vfs-profile-error.db ls / set +e -AGENTFS_PROFILE=1 run_agentfs completions show >"$output_file" 2>&1 +VFS_PROFILE=1 run_vfs completions show >"$output_file" 2>&1 status=$? set -e @@ -60,7 +60,7 @@ if [ "$status" -ne 0 ]; then exit 1 fi -summary_count="$(grep -c '"event":"agentfs_profile_summary"' "$output_file" || true)" +summary_count="$(grep -c '"event":"vfs_profile_summary"' "$output_file" || true)" if [ "$summary_count" -ne 1 ]; then echo "FAILED: expected exactly one profile summary on success, saw $summary_count" cat "$output_file" diff --git a/crates/agentfs-cli/tests/test-run-bash.sh b/crates/vfs-cli/tests/test-run-bash.sh similarity index 68% rename from crates/agentfs-cli/tests/test-run-bash.sh rename to crates/vfs-cli/tests/test-run-bash.sh index c9b2af87..154ef7ce 100755 --- a/crates/agentfs-cli/tests/test-run-bash.sh +++ b/crates/vfs-cli/tests/test-run-bash.sh @@ -6,30 +6,30 @@ echo -n "TEST interactive bash session... " DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-run-bash.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-run-bash.XXXXXX")" SESSION_ID="run-bash-$$" cleanup() { - rm -rf "$ROOT" "${HOME:?}/.agentfs/run/${SESSION_ID}" + rm -rf "$ROOT" "${HOME:?}/.vfs/run/${SESSION_ID}" } trap cleanup EXIT INT TERM -run_agentfs() { - if [ -n "${AGENTFS_BIN:-}" ]; then - "$AGENTFS_BIN" "$@" +run_vfs() { + if [ -n "${VFS_BIN:-}" ]; then + "$VFS_BIN" "$@" else cargo run --quiet --manifest-path "$CLI_DIR/Cargo.toml" -- "$@" fi } # The temp root is the overlay base layer. The session delta DB lands under -# ~/.agentfs/run/; pass an explicit session id so cleanup can remove -# exactly the session dir this test created (never sweep ~/.agentfs/run). +# ~/.vfs/run/; pass an explicit session id so cleanup can remove +# exactly the session dir this test created (never sweep ~/.vfs/run). cd "$ROOT" # Run bash session in overlay: write a file and read it back # The current directory becomes copy-on-write with the overlay sandbox -output=$(run_agentfs run --session "$SESSION_ID" /bin/bash -c ' +output=$(run_vfs run --session "$SESSION_ID" /bin/bash -c ' echo "hello from agent" > hello.txt cat hello.txt ' 2>&1) diff --git a/crates/agentfs-cli/tests/test-run-git.sh b/crates/vfs-cli/tests/test-run-git.sh similarity index 72% rename from crates/agentfs-cli/tests/test-run-git.sh rename to crates/vfs-cli/tests/test-run-git.sh index fd945c7d..74ceacba 100755 --- a/crates/agentfs-cli/tests/test-run-git.sh +++ b/crates/vfs-cli/tests/test-run-git.sh @@ -6,17 +6,17 @@ echo -n "TEST git init and commit in overlay... " DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-run-git.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-run-git.XXXXXX")" SESSION_ID="run-git-$$" cleanup() { - rm -rf "$ROOT" "${HOME:?}/.agentfs/run/${SESSION_ID}" + rm -rf "$ROOT" "${HOME:?}/.vfs/run/${SESSION_ID}" } trap cleanup EXIT INT TERM -run_agentfs() { - if [ -n "${AGENTFS_BIN:-}" ]; then - "$AGENTFS_BIN" "$@" +run_vfs() { + if [ -n "${VFS_BIN:-}" ]; then + "$VFS_BIN" "$@" else cargo run --quiet --manifest-path "$CLI_DIR/Cargo.toml" -- "$@" fi @@ -36,17 +36,17 @@ PATH="$ROOT/bin:$PATH" export PATH # The temp root is the overlay base layer. The session delta DB lands under -# ~/.agentfs/run/; pass an explicit session id so cleanup can remove -# exactly the session dir this test created (never sweep ~/.agentfs/run). +# ~/.vfs/run/; pass an explicit session id so cleanup can remove +# exactly the session dir this test created (never sweep ~/.vfs/run). cd "$ROOT" # Run git operations in overlay: init, add, commit. # Identity is set explicitly: read scoping hides ~/.gitconfig inside the # sandbox, and CI runners have no global identity anyway. -output=$(run_agentfs run --session "$SESSION_ID" /bin/bash -c ' +output=$(run_vfs run --session "$SESSION_ID" /bin/bash -c ' export GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null -export GIT_AUTHOR_NAME="AgentFS Test" GIT_AUTHOR_EMAIL="agentfs-test@example.com" -export GIT_COMMITTER_NAME="AgentFS Test" GIT_COMMITTER_EMAIL="agentfs-test@example.com" +export GIT_AUTHOR_NAME="Vfs Test" GIT_AUTHOR_EMAIL="vfs-test@example.com" +export GIT_COMMITTER_NAME="Vfs Test" GIT_COMMITTER_EMAIL="vfs-test@example.com" mkdir test-git-repo cd test-git-repo git init diff --git a/crates/agentfs-cli/tests/test-run-read-scoping.sh b/crates/vfs-cli/tests/test-run-read-scoping.sh similarity index 74% rename from crates/agentfs-cli/tests/test-run-read-scoping.sh rename to crates/vfs-cli/tests/test-run-read-scoping.sh index 221a8cb8..d731f3c7 100755 --- a/crates/agentfs-cli/tests/test-run-read-scoping.sh +++ b/crates/vfs-cli/tests/test-run-read-scoping.sh @@ -1,7 +1,7 @@ #!/bin/sh set -u -# Read scoping (sacred invariant 2 / VAL-CROSS-015): inside `agentfs run +# Read scoping (sacred invariant 2 / VAL-CROSS-015): inside `vfs run # --no-default-allows` a command must not be able to read host files placed # outside the allowed set (home and temp dirs, including /dev/shm, are # hidden), while re-running with `--allow ` re-exposes that directory; @@ -12,9 +12,9 @@ echo "TEST run read scoping..." DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" -run_agentfs() { - if [ -n "${AGENTFS_BIN:-}" ]; then - "$AGENTFS_BIN" "$@" +run_vfs() { + if [ -n "${VFS_BIN:-}" ]; then + "$VFS_BIN" "$@" else cargo run --quiet --manifest-path "$CLI_DIR/Cargo.toml" -- "$@" fi @@ -22,14 +22,14 @@ run_agentfs() { session_prefix="scope-test-$$" tmp_secret_dir="$(mktemp -d)" -home_secret_dir="$(mktemp -d "${HOME}/.agentfs-scope-test.XXXXXX")" -shm_secret_dir="$(mktemp -d /dev/shm/agentfs-scope-test.XXXXXX)" +home_secret_dir="$(mktemp -d "${HOME}/.vfs-scope-test.XXXXXX")" +shm_secret_dir="$(mktemp -d /dev/shm/vfs-scope-test.XXXXXX)" cleanup() { rm -rf "$tmp_secret_dir" "$home_secret_dir" "$shm_secret_dir" - rm -rf "${HOME:?}/.agentfs/run/${session_prefix}-denied" \ - "${HOME:?}/.agentfs/run/${session_prefix}-write" \ - "${HOME:?}/.agentfs/run/${session_prefix}-allowed" + rm -rf "${HOME:?}/.vfs/run/${session_prefix}-denied" \ + "${HOME:?}/.vfs/run/${session_prefix}-write" \ + "${HOME:?}/.vfs/run/${session_prefix}-allowed" } trap cleanup EXIT @@ -47,7 +47,7 @@ fail() { } # Leg 1 (denied reads): none of the tmp, home, or /dev/shm secrets is reachable. -denied_output=$(run_agentfs run --no-default-allows --session "${session_prefix}-denied" \ +denied_output=$(run_vfs run --no-default-allows --session "${session_prefix}-denied" \ /bin/sh -c "cat '$tmp_secret_dir/secret.txt'; cat '$shm_secret_dir/secret.txt'; cat '$home_secret_dir/secret.txt'" 2>&1) denied_status=$? case "$denied_output" in @@ -57,16 +57,16 @@ esac # Leg 2 (denied writes): writes to scoped host paths (incl. /dev/shm, which the # ro-remount pass skips under its /dev prefix) must not reach the host. -write_output=$(run_agentfs run --no-default-allows --session "${session_prefix}-write" \ - /bin/sh -c "echo intruder >'$tmp_secret_dir/evil.txt'; echo intruder >'$shm_secret_dir/evil.txt'; echo intruder >/dev/shm/agentfs-scope-test-stray-$$.txt" 2>&1) +write_output=$(run_vfs run --no-default-allows --session "${session_prefix}-write" \ + /bin/sh -c "echo intruder >'$tmp_secret_dir/evil.txt'; echo intruder >'$shm_secret_dir/evil.txt'; echo intruder >/dev/shm/vfs-scope-test-stray-$$.txt" 2>&1) [ ! -f "$tmp_secret_dir/evil.txt" ] || fail "sandbox write escaped to the host" "$write_output" [ ! -f "$shm_secret_dir/evil.txt" ] || fail "sandbox write escaped to host /dev/shm" "$write_output" -[ ! -f "/dev/shm/agentfs-scope-test-stray-$$.txt" ] || fail "sandbox write escaped to host /dev/shm root" "$write_output" +[ ! -f "/dev/shm/vfs-scope-test-stray-$$.txt" ] || fail "sandbox write escaped to host /dev/shm root" "$write_output" [ "$(cat "$tmp_secret_dir/secret.txt")" = "s3cr3t-tmp" ] || fail "host secret was mutated" "$write_output" [ "$(cat "$shm_secret_dir/secret.txt")" = "s3cr3t-shm" ] || fail "host shm secret was mutated" "$write_output" # Leg 3 (allowed reads): --allow re-exposes the directory with exact bytes. -allowed_output=$(run_agentfs run --no-default-allows --allow "$tmp_secret_dir" \ +allowed_output=$(run_vfs run --no-default-allows --allow "$tmp_secret_dir" \ --session "${session_prefix}-allowed" \ /bin/sh -c "cat '$tmp_secret_dir/secret.txt'" 2>&1) allowed_status=$? diff --git a/crates/agentfs-cli/tests/test-run-syscalls.sh b/crates/vfs-cli/tests/test-run-syscalls.sh similarity index 79% rename from crates/agentfs-cli/tests/test-run-syscalls.sh rename to crates/vfs-cli/tests/test-run-syscalls.sh index fd6b8906..65339371 100755 --- a/crates/agentfs-cli/tests/test-run-syscalls.sh +++ b/crates/vfs-cli/tests/test-run-syscalls.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Test syscalls through agentfs run (FUSE overlay). +# Test syscalls through vfs run (FUSE overlay). # # This tests the copy-on-write behavior when files exist in the base layer # and are modified through the overlay. @@ -9,16 +9,16 @@ # set -eu -echo -n "TEST syscalls (agentfs run - FUSE overlay)... " +echo -n "TEST syscalls (vfs run - FUSE overlay)... " DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-run-syscalls.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-run-syscalls.XXXXXX")" SESSION_ID="run-syscalls-$$" cleanup() { - rm -rf "$ROOT" "${HOME:?}/.agentfs/run/${SESSION_ID}" + rm -rf "$ROOT" "${HOME:?}/.vfs/run/${SESSION_ID}" } trap cleanup EXIT INT TERM @@ -27,9 +27,9 @@ fail() { exit 1 } -run_agentfs() { - if [ -n "${AGENTFS_BIN:-}" ]; then - "$AGENTFS_BIN" "$@" +run_vfs() { + if [ -n "${VFS_BIN:-}" ]; then + "$VFS_BIN" "$@" else cargo run --quiet --manifest-path "$CLI_DIR/Cargo.toml" -- "$@" fi @@ -41,13 +41,13 @@ make -C "$DIR/syscall" > /dev/null 2>&1 cp "$DIR/syscall/test-syscalls" "$ROOT/test-syscalls" # The temp root is the overlay base layer; `init` puts its DB under -# $ROOT/.agentfs instead of the repo working tree. The run session's delta DB -# lands under ~/.agentfs/run/; pass an explicit session id so cleanup +# $ROOT/.vfs instead of the repo working tree. The run session's delta DB +# lands under ~/.vfs/run/; pass an explicit session id so cleanup # can remove exactly the session dir this test created (never sweep -# ~/.agentfs/run). +# ~/.vfs/run). cd "$ROOT" -run_agentfs init > /dev/null 2>&1 +run_vfs init > /dev/null 2>&1 # Create pre-existing files in the BASE LAYER (current directory) # These will trigger copy-on-write when modified through the overlay @@ -86,9 +86,9 @@ echo -n "content for fallocate copyup test" > copyup_fallocate_test.txt # Run syscall tests through FUSE overlay # The test binary runs inside the overlay where: # - Files from current directory are visible (base layer) -# - Modifications go to the delta layer (AgentFS database) +# - Modifications go to the delta layer (Vfs database) # - O_APPEND on existing.txt triggers copy-on-write -output=$(run_agentfs run --session "$SESSION_ID" ./test-syscalls . 2>&1) || +output=$(run_vfs run --session "$SESSION_ID" ./test-syscalls . 2>&1) || fail "run exited nonzero Output was: $output" @@ -97,6 +97,6 @@ echo "$output" | grep -q "All tests passed!" || Output was: $output" # Note: output.txt is created in the delta layer (session-specific) so we can't -# verify it with a separate agentfs run. The "All tests passed!" check is sufficient. +# verify it with a separate vfs run. The "All tests passed!" check is sufficient. echo "OK" diff --git a/crates/agentfs-cli/tests/test-second-opener.sh b/crates/vfs-cli/tests/test-second-opener.sh similarity index 79% rename from crates/agentfs-cli/tests/test-second-opener.sh rename to crates/vfs-cli/tests/test-second-opener.sh index f79f4619..71efc34a 100755 --- a/crates/agentfs-cli/tests/test-second-opener.sh +++ b/crates/vfs-cli/tests/test-second-opener.sh @@ -1,7 +1,7 @@ #!/usr/bin/env sh # # Second-opener-of-one-DB regression (m7-cli-polish, VAL-CLI-025): -# while one `agentfs exec` holds a database open through a mount, a second +# while one `vfs exec` holds a database open through a mount, a second # opener of the same database must fail cleanly — exit nonzero with a single # concise `Error:` line (turso file lock), bounded by a timeout, without # panicking, and without disturbing the first session. Afterwards integrity @@ -13,7 +13,7 @@ CLI_DIR="$(cd "$DIR/.." && pwd)" echo -n "TEST second opener of one DB... " -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-second-opener.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-second-opener.XXXXXX")" FIRST_PID="" cleanup() { @@ -30,32 +30,32 @@ fail() { exit 1 } -# Resolve the binary once: background legs must track the agentfs PID itself +# Resolve the binary once: background legs must track the vfs PID itself # (backgrounding a wrapper orphans the real process on cleanup kill). -if [ -n "${AGENTFS_BIN:-}" ]; then - BIN="$AGENTFS_BIN" +if [ -n "${VFS_BIN:-}" ]; then + BIN="$VFS_BIN" else cargo +nightly build --quiet --manifest-path "$CLI_DIR/Cargo.toml" || { - echo "FAILED: could not build agentfs" + echo "FAILED: could not build vfs" exit 1 } - BIN="$CLI_DIR/../../target/debug/agentfs" + BIN="$CLI_DIR/../../target/debug/vfs" fi if [ ! -x "$BIN" ]; then - echo "FAILED: agentfs binary not found at $BIN" + echo "FAILED: vfs binary not found at $BIN" exit 1 fi -run_agentfs() { +run_vfs() { "$BIN" "$@" } -# agentfs_dir() is cwd-relative, so init from inside $ROOT. -(cd "$ROOT" && run_agentfs init shared) >"$ROOT/init.log" 2>&1 || +# vfs_dir() is cwd-relative, so init from inside $ROOT. +(cd "$ROOT" && run_vfs init shared) >"$ROOT/init.log" 2>&1 || fail "init failed: $(cat "$ROOT/init.log")" -DB="$ROOT/.agentfs/shared.db" +DB="$ROOT/.vfs/shared.db" [ -f "$DB" ] || fail "init did not create $DB" -run_agentfs fs "$DB" write /keep.txt kept >"$ROOT/write.log" 2>&1 || +run_vfs fs "$DB" write /keep.txt kept >"$ROOT/write.log" 2>&1 || fail "fs write failed: $(cat "$ROOT/write.log")" # First opener: exec holds the DB (and its file lock) while the child sleeps. @@ -106,11 +106,11 @@ FIRST_STATUS=$? FIRST_PID="" [ "$FIRST_STATUS" -eq 0 ] || fail "first exec failed after second-opener attempt: $(cat "$ROOT/first.log")" -run_agentfs fs "$DB" cat /keep.txt >"$ROOT/cat.log" 2>&1 || +run_vfs fs "$DB" cat /keep.txt >"$ROOT/cat.log" 2>&1 || fail "post-test cat failed: $(cat "$ROOT/cat.log")" grep -q 'kept' "$ROOT/cat.log" || fail "post-test content mismatch: $(cat "$ROOT/cat.log")" -run_agentfs integrity --json "$DB" >"$ROOT/integrity.json" 2>&1 || +run_vfs integrity --json "$DB" >"$ROOT/integrity.json" 2>&1 || fail "integrity failed: $(cat "$ROOT/integrity.json")" echo "OK" diff --git a/crates/agentfs-cli/tests/test-sigkill-recovery.sh b/crates/vfs-cli/tests/test-sigkill-recovery.sh similarity index 90% rename from crates/agentfs-cli/tests/test-sigkill-recovery.sh rename to crates/vfs-cli/tests/test-sigkill-recovery.sh index 2c69ef55..df126430 100755 --- a/crates/agentfs-cli/tests/test-sigkill-recovery.sh +++ b/crates/vfs-cli/tests/test-sigkill-recovery.sh @@ -15,7 +15,7 @@ DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" REPO_ROOT="$(cd "$CLI_DIR/../.." && pwd)" CARGO_MANIFEST="$CLI_DIR/Cargo.toml" -AGENTFS_BIN="${AGENTFS_BIN:-}" +VFS_BIN="${VFS_BIN:-}" START_TIMEOUT="${SIGKILL_RECOVERY_START_TIMEOUT:-30}" EXIT_TIMEOUT="${SIGKILL_RECOVERY_EXIT_TIMEOUT:-10}" HOST_HOME="${HOME:-}" @@ -70,13 +70,13 @@ if [ -r /proc/sys/kernel/unprivileged_userns_clone ] && skip "unprivileged user namespaces are disabled" fi -if [ -z "$AGENTFS_BIN" ]; then +if [ -z "$VFS_BIN" ]; then cargo build --quiet --manifest-path "$CARGO_MANIFEST" >/dev/null 2>&1 || - fail "failed to build agentfs CLI" - AGENTFS_BIN="$REPO_ROOT/target/debug/agentfs" + fail "failed to build vfs CLI" + VFS_BIN="$REPO_ROOT/target/debug/vfs" fi -[ -x "$AGENTFS_BIN" ] || fail "AGENTFS_BIN is not executable: $AGENTFS_BIN" +[ -x "$VFS_BIN" ] || fail "VFS_BIN is not executable: $VFS_BIN" unmount_path() { path="$1" @@ -143,7 +143,7 @@ cleanup() { if [ -n "$TEST_ROOT" ] && [ -d "$TEST_ROOT" ]; then case "$TEST_ROOT" in - "${TMPDIR:-/tmp}"/agentfs-sigkill-recovery.*) + "${TMPDIR:-/tmp}"/vfs-sigkill-recovery.*) rm -rf "$TEST_ROOT" ;; *) @@ -220,12 +220,12 @@ assert_acked_files_present() { done <"$ACK_LOG" } -TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-sigkill-recovery.XXXXXX")" +TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-sigkill-recovery.XXXXXX")" TEST_HOME="$TEST_ROOT/home" WORKDIR="$TEST_ROOT/work" LOGDIR="$TEST_ROOT/logs" SESSION_ID="sigkill-recovery-$$" -RUN_DIR="$TEST_HOME/.agentfs/run/$SESSION_ID" +RUN_DIR="$TEST_HOME/.vfs/run/$SESSION_ID" DELTA_DB="$RUN_DIR/delta.db" FUSE_MNT="$RUN_DIR/mnt" ACK_LOG="$LOGDIR/acked.txt" @@ -245,8 +245,8 @@ mkdir -p "$TEST_HOME/.cache" "$TEST_HOME/.config" "$WORKDIR" "$LOGDIR" "$REMOUNT CARGO_HOME="$CARGO_HOME_FOR_TEST" \ RUSTUP_HOME="$RUSTUP_HOME_FOR_TEST" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN_FOR_TEST" \ - AGENTFS_FUSE_URING=0 \ - "$AGENTFS_BIN" run --session "$SESSION_ID" --allow "$LOGDIR" \ + VFS_FUSE_URING=0 \ + "$VFS_BIN" run --session "$SESSION_ID" --allow "$LOGDIR" \ /bin/bash -c ' set -euo pipefail printf "%s\n" "$$" > "$1" @@ -278,7 +278,7 @@ done while :; do sleep 1 done -' agentfs-sigkill-workload "$CHILD_PID_FILE" "$ACK_LOG" "$READY_FILE" +' vfs-sigkill-workload "$CHILD_PID_FILE" "$ACK_LOG" "$READY_FILE" ) >"$OWNER_LOG" 2>&1 & OWNER_PID=$! @@ -308,9 +308,9 @@ if mountpoint -q "$FUSE_MNT" 2>/dev/null; then exit 1 fi -"$AGENTFS_BIN" integrity "$DELTA_DB" --json >/dev/null +"$VFS_BIN" integrity "$DELTA_DB" --json >/dev/null -"$AGENTFS_BIN" mount "$DELTA_DB" "$REMOUNT" --backend fuse --foreground >"$REMOUNT_LOG" 2>&1 & +"$VFS_BIN" mount "$DELTA_DB" "$REMOUNT" --backend fuse --foreground >"$REMOUNT_LOG" 2>&1 & REMOUNT_PID=$! if ! wait_for_remount; then dump_context "remount did not become ready after SIGKILL recovery" @@ -331,8 +331,8 @@ if mount | grep "$SESSION_ID" >/dev/null 2>&1 || mount | grep "$REMOUNT" >/dev/n dump_context "mount residue after SIGKILL recovery" exit 1 fi -if ps -e -o pid= -o args= | grep "$SESSION_ID" | grep agentfs | grep -v grep >/dev/null 2>&1; then - dump_context "agentfs process residue after SIGKILL recovery" +if ps -e -o pid= -o args= | grep "$SESSION_ID" | grep vfs | grep -v grep >/dev/null 2>&1; then + dump_context "vfs process residue after SIGKILL recovery" exit 1 fi diff --git a/crates/agentfs-cli/tests/test-signal-teardown.sh b/crates/vfs-cli/tests/test-signal-teardown.sh similarity index 85% rename from crates/agentfs-cli/tests/test-signal-teardown.sh rename to crates/vfs-cli/tests/test-signal-teardown.sh index 0429347c..8b473e3f 100755 --- a/crates/agentfs-cli/tests/test-signal-teardown.sh +++ b/crates/vfs-cli/tests/test-signal-teardown.sh @@ -14,7 +14,7 @@ DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" REPO_ROOT="$(cd "$CLI_DIR/../.." && pwd)" CARGO_MANIFEST="$CLI_DIR/Cargo.toml" -AGENTFS_BIN="${AGENTFS_BIN:-}" +VFS_BIN="${VFS_BIN:-}" SIGNAL_DELAY="${SIGNAL_TEARDOWN_SIGNAL_DELAY:-3s}" HOST_HOME="${HOME:-}" CARGO_HOME_FOR_TEST="${CARGO_HOME:-$HOST_HOME/.cargo}" @@ -46,17 +46,17 @@ command -v git >/dev/null 2>&1 || skip "git is unavailable" command -v timeout >/dev/null 2>&1 || skip "timeout is unavailable" [ -e /dev/fuse ] || skip "requires /dev/fuse for FUSE mounts" -if [ -z "$AGENTFS_BIN" ]; then +if [ -z "$VFS_BIN" ]; then cargo build --quiet --manifest-path "$CARGO_MANIFEST" >/dev/null 2>&1 || { - echo "FAILED: failed to build agentfs CLI" + echo "FAILED: failed to build vfs CLI" exit 1 } - AGENTFS_BIN="$REPO_ROOT/target/debug/agentfs" + VFS_BIN="$REPO_ROOT/target/debug/vfs" fi -if [ ! -x "$AGENTFS_BIN" ]; then - echo "FAILED: AGENTFS_BIN is not executable: $AGENTFS_BIN" +if [ ! -x "$VFS_BIN" ]; then + echo "FAILED: VFS_BIN is not executable: $VFS_BIN" exit 1 fi @@ -130,7 +130,7 @@ for path in sorted(after - before): print(path) PY case "$path" in - /tmp/agentfs-init-*|/tmp/agentfs-clone-*|/tmp/agentfs-exec-*) + /tmp/vfs-init-*|/tmp/vfs-clone-*|/tmp/vfs-exec-*) if command -v fusermount3 >/dev/null 2>&1; then fusermount3 -u "$path" 2>/dev/null || fusermount3 -uz "$path" 2>/dev/null || true elif command -v fusermount >/dev/null 2>&1; then @@ -155,12 +155,12 @@ cleanup() { if [ -n "$TEST_HOME" ]; then cleanup_mounts_matching "$TEST_HOME" fi - cleanup_new_tmp_dirs "agentfs-init-" "$BEFORE_INIT_DIRS" - cleanup_new_tmp_dirs "agentfs-clone-" "$BEFORE_CLONE_DIRS" - cleanup_new_tmp_dirs "agentfs-exec-" "$BEFORE_EXEC_DIRS" + cleanup_new_tmp_dirs "vfs-init-" "$BEFORE_INIT_DIRS" + cleanup_new_tmp_dirs "vfs-clone-" "$BEFORE_CLONE_DIRS" + cleanup_new_tmp_dirs "vfs-exec-" "$BEFORE_EXEC_DIRS" if [ -n "$TEST_ROOT" ] && [ -d "$TEST_ROOT" ]; then case "$TEST_ROOT" in - "${TMPDIR:-/tmp}"/agentfs-signal-teardown.*) + "${TMPDIR:-/tmp}"/vfs-signal-teardown.*) rm -rf "$TEST_ROOT" ;; *) @@ -245,13 +245,13 @@ run_init_signal_case() { CARGO_HOME="$CARGO_HOME_FOR_TEST" \ RUSTUP_HOME="$RUSTUP_HOME_FOR_TEST" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN_FOR_TEST" \ - AGENTFS_FUSE_URING=0 \ + VFS_FUSE_URING=0 \ timeout --preserve-status --foreground -s "$signal" "$SIGNAL_DELAY" \ - "$AGENTFS_BIN" init "$id" --backend fuse -c 'exec sleep 30' + "$VFS_BIN" init "$id" --backend fuse -c 'exec sleep 30' - assert_no_mount "init -c $signal" "agentfs:$id" + assert_no_mount "init -c $signal" "vfs:$id" assert_no_processes "init -c $signal" "$TEST_HOME" - assert_no_new_tmp_dirs "init -c $signal" "agentfs-init-" "$BEFORE_INIT_DIRS" + assert_no_new_tmp_dirs "init -c $signal" "vfs-init-" "$BEFORE_INIT_DIRS" } write_git_wrapper() { @@ -287,15 +287,15 @@ run_clone_signal_case() { CARGO_HOME="$CARGO_HOME_FOR_TEST" \ RUSTUP_HOME="$RUSTUP_HOME_FOR_TEST" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN_FOR_TEST" \ - AGENTFS_FUSE_URING=0 \ + VFS_FUSE_URING=0 \ REAL_GIT="$REAL_GIT" \ PATH="$wrapper_dir:$PATH" \ timeout --preserve-status --foreground -s "$signal" "$SIGNAL_DELAY" \ - "$AGENTFS_BIN" clone "$db" "$source" repo --backend fuse + "$VFS_BIN" clone "$db" "$source" repo --backend fuse assert_no_mount "clone $signal" "$db" assert_no_processes "clone $signal" "$TEST_ROOT" - assert_no_new_tmp_dirs "clone $signal" "agentfs-clone-" "$BEFORE_CLONE_DIRS" + assert_no_new_tmp_dirs "clone $signal" "vfs-clone-" "$BEFORE_CLONE_DIRS" } run_exec_signal_case() { @@ -304,7 +304,7 @@ run_exec_signal_case() { new_case_home "exec-$signal" id="signal-exec-$$-$signal" - env HOME="$TEST_HOME" "$AGENTFS_BIN" init "$id" >/dev/null 2>&1 + env HOME="$TEST_HOME" "$VFS_BIN" init "$id" >/dev/null 2>&1 run_timeout_expect "exec $signal" "$expected" \ env HOME="$TEST_HOME" \ XDG_CACHE_HOME="$TEST_HOME/.cache" \ @@ -312,13 +312,13 @@ run_exec_signal_case() { CARGO_HOME="$CARGO_HOME_FOR_TEST" \ RUSTUP_HOME="$RUSTUP_HOME_FOR_TEST" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN_FOR_TEST" \ - AGENTFS_FUSE_URING=0 \ + VFS_FUSE_URING=0 \ timeout --preserve-status --foreground -s "$signal" "$SIGNAL_DELAY" \ - "$AGENTFS_BIN" exec "$id" --backend fuse sh -c 'exec sleep 30' + "$VFS_BIN" exec "$id" --backend fuse sh -c 'exec sleep 30' - assert_no_mount "exec $signal" "agentfs:.*$id" + assert_no_mount "exec $signal" "vfs:.*$id" assert_no_processes "exec $signal" "$TEST_HOME" - assert_no_new_tmp_dirs "exec $signal" "agentfs-exec-" "$BEFORE_EXEC_DIRS" + assert_no_new_tmp_dirs "exec $signal" "vfs-exec-" "$BEFORE_EXEC_DIRS" } run_mount_foreground_signal_case() { @@ -328,7 +328,7 @@ run_mount_foreground_signal_case() { mountpoint="$TEST_ROOT/mount-$signal" mkdir -p "$mountpoint" - env HOME="$TEST_HOME" "$AGENTFS_BIN" init "$id" >/dev/null 2>&1 + env HOME="$TEST_HOME" "$VFS_BIN" init "$id" >/dev/null 2>&1 run_timeout_expect "mount --foreground $signal" 0 \ env HOME="$TEST_HOME" \ XDG_CACHE_HOME="$TEST_HOME/.cache" \ @@ -336,15 +336,15 @@ run_mount_foreground_signal_case() { CARGO_HOME="$CARGO_HOME_FOR_TEST" \ RUSTUP_HOME="$RUSTUP_HOME_FOR_TEST" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN_FOR_TEST" \ - AGENTFS_FUSE_URING=0 \ + VFS_FUSE_URING=0 \ timeout --preserve-status --foreground -s "$signal" "$SIGNAL_DELAY" \ - "$AGENTFS_BIN" mount "$id" "$mountpoint" --backend fuse --foreground + "$VFS_BIN" mount "$id" "$mountpoint" --backend fuse --foreground assert_no_mount "mount --foreground $signal" "$mountpoint" assert_no_processes "mount --foreground $signal" "$TEST_HOME" } -TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-signal-teardown.XXXXXX")" +TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-signal-teardown.XXXXXX")" BEFORE_INIT_DIRS="$TEST_ROOT/before-init-dirs.txt" BEFORE_CLONE_DIRS="$TEST_ROOT/before-clone-dirs.txt" BEFORE_EXEC_DIRS="$TEST_ROOT/before-exec-dirs.txt" @@ -358,9 +358,9 @@ for candidate in /usr/bin/git /bin/git; do fi done [ -n "$REAL_GIT" ] || REAL_GIT="$(command -v git)" -snapshot_tmp_dirs "agentfs-init-" "$BEFORE_INIT_DIRS" -snapshot_tmp_dirs "agentfs-clone-" "$BEFORE_CLONE_DIRS" -snapshot_tmp_dirs "agentfs-exec-" "$BEFORE_EXEC_DIRS" +snapshot_tmp_dirs "vfs-init-" "$BEFORE_INIT_DIRS" +snapshot_tmp_dirs "vfs-clone-" "$BEFORE_CLONE_DIRS" +snapshot_tmp_dirs "vfs-exec-" "$BEFORE_EXEC_DIRS" for signal in INT TERM; do run_init_signal_case "$signal" diff --git a/crates/agentfs-cli/tests/test-symlinks.sh b/crates/vfs-cli/tests/test-symlinks.sh similarity index 72% rename from crates/agentfs-cli/tests/test-symlinks.sh rename to crates/vfs-cli/tests/test-symlinks.sh index 8b4b519e..2115dead 100755 --- a/crates/agentfs-cli/tests/test-symlinks.sh +++ b/crates/vfs-cli/tests/test-symlinks.sh @@ -6,29 +6,29 @@ echo -n "TEST symlink handling... " DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" -ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-symlinks.XXXXXX")" +ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-symlinks.XXXXXX")" SESSION_PREFIX="symlinks-$$" cleanup() { rm -rf "$ROOT" for leg in 1 2 3 4 5; do - rm -rf "${HOME:?}/.agentfs/run/${SESSION_PREFIX}-${leg}" + rm -rf "${HOME:?}/.vfs/run/${SESSION_PREFIX}-${leg}" done } trap cleanup EXIT INT TERM -run_agentfs() { - if [ -n "${AGENTFS_BIN:-}" ]; then - "$AGENTFS_BIN" "$@" +run_vfs() { + if [ -n "${VFS_BIN:-}" ]; then + "$VFS_BIN" "$@" else cargo run --quiet --manifest-path "$CLI_DIR/Cargo.toml" -- "$@" fi } # The temp root is the overlay base layer. Each leg's session delta DB lands -# under ~/.agentfs/run/; pass explicit session ids so cleanup can +# under ~/.vfs/run/; pass explicit session ids so cleanup can # remove exactly the session dirs this test created (never sweep -# ~/.agentfs/run). +# ~/.vfs/run). cd "$ROOT" # Create test directory with symlinks on the host (these will be visible in the sandbox) @@ -39,7 +39,7 @@ ln -s target_dir "$TEST_DIR/link_to_dir" ln -s target_dir/file.txt "$TEST_DIR/link_to_file" # Test 1 & 2: Verify symlinks are reported correctly (not as directories) -output=$(run_agentfs run --session "$SESSION_PREFIX-1" /bin/bash -c "ls -la $TEST_DIR/" 2>&1) +output=$(run_vfs run --session "$SESSION_PREFIX-1" /bin/bash -c "ls -la $TEST_DIR/" 2>&1) # The output should contain 'lrwxrwxrwx' for symlinks (not 'drwxr-xr-x' for directory) if ! echo "$output" | grep -qE "^lrwx.* link_to_dir"; then @@ -56,7 +56,7 @@ fi # Test 3: Verify rm can remove symlink to directory (this was the original bug) # Previously this would fail with "Is a directory" because symlinks were misidentified -output=$(run_agentfs run --session "$SESSION_PREFIX-2" /bin/bash -c "rm $TEST_DIR/link_to_dir && echo 'symlink removed successfully'" 2>&1) +output=$(run_vfs run --session "$SESSION_PREFIX-2" /bin/bash -c "rm $TEST_DIR/link_to_dir && echo 'symlink removed successfully'" 2>&1) if ! echo "$output" | grep -q "symlink removed successfully"; then echo "FAILED: could not remove symlink to directory" @@ -72,7 +72,7 @@ if ! cat "$TEST_DIR/target_dir/file.txt" | grep -q "test content"; then fi # Test 5: Create a symlink inside the sandbox (tests FUSE symlink creation) -output=$(run_agentfs run --session "$SESSION_PREFIX-3" /bin/bash -c "ln -s target_dir/file.txt $TEST_DIR/new_symlink && readlink $TEST_DIR/new_symlink" 2>&1) +output=$(run_vfs run --session "$SESSION_PREFIX-3" /bin/bash -c "ln -s target_dir/file.txt $TEST_DIR/new_symlink && readlink $TEST_DIR/new_symlink" 2>&1) if ! echo "$output" | grep -q "target_dir/file.txt"; then echo "FAILED: could not create symlink in sandbox" @@ -81,7 +81,7 @@ if ! echo "$output" | grep -q "target_dir/file.txt"; then fi # Test 6: Create and follow symlink to read file content -output=$(run_agentfs run --session "$SESSION_PREFIX-4" /bin/bash -c "ln -s target_dir $TEST_DIR/new_dir_link && cat $TEST_DIR/new_dir_link/file.txt" 2>&1) +output=$(run_vfs run --session "$SESSION_PREFIX-4" /bin/bash -c "ln -s target_dir $TEST_DIR/new_dir_link && cat $TEST_DIR/new_dir_link/file.txt" 2>&1) if ! echo "$output" | grep -q "test content"; then echo "FAILED: could not read through newly created symlink" @@ -90,7 +90,7 @@ if ! echo "$output" | grep -q "test content"; then fi # Test 7: Verify symlinks created in sandbox are visible via ls -l -output=$(run_agentfs run --session "$SESSION_PREFIX-5" /bin/bash -c "ln -s foo $TEST_DIR/test_link && ls -la $TEST_DIR/test_link" 2>&1) +output=$(run_vfs run --session "$SESSION_PREFIX-5" /bin/bash -c "ln -s foo $TEST_DIR/test_link && ls -la $TEST_DIR/test_link" 2>&1) if ! echo "$output" | grep -qE "^lrwx.*test_link -> foo"; then echo "FAILED: newly created symlink not shown correctly in ls" diff --git a/crates/agentfs-cli/tests/test-teardown-bounded.sh b/crates/vfs-cli/tests/test-teardown-bounded.sh similarity index 90% rename from crates/agentfs-cli/tests/test-teardown-bounded.sh rename to crates/vfs-cli/tests/test-teardown-bounded.sh index 1d890383..e34118c7 100755 --- a/crates/agentfs-cli/tests/test-teardown-bounded.sh +++ b/crates/vfs-cli/tests/test-teardown-bounded.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Bounded teardown regression for `agentfs run --session`. +# Bounded teardown regression for `vfs run --session`. # # The owner process must exit promptly after SIGTERM while a real FUSE session # has had joiners and filesystem activity. This exercises both the legacy @@ -19,7 +19,7 @@ DIR="$(cd "$(dirname "$0")" && pwd)" CLI_DIR="$(cd "$DIR/.." && pwd)" REPO_ROOT="$(cd "$CLI_DIR/../.." && pwd)" CARGO_MANIFEST="$CLI_DIR/Cargo.toml" -AGENTFS_BIN="${AGENTFS_BIN:-}" +VFS_BIN="${VFS_BIN:-}" TEARDOWN_TIMEOUT="${TEARDOWN_TIMEOUT:-10}" START_TIMEOUT="${TEARDOWN_START_TIMEOUT:-30}" HOST_HOME="${HOME:-}" @@ -81,17 +81,17 @@ fi validate_positive_integer TEARDOWN_TIMEOUT "$TEARDOWN_TIMEOUT" validate_positive_integer TEARDOWN_START_TIMEOUT "$START_TIMEOUT" -if [ -z "$AGENTFS_BIN" ]; then +if [ -z "$VFS_BIN" ]; then cargo build --quiet --manifest-path "$CARGO_MANIFEST" >/dev/null 2>&1 || { - echo "FAILED: failed to build agentfs CLI" + echo "FAILED: failed to build vfs CLI" exit 1 } - AGENTFS_BIN="$REPO_ROOT/target/debug/agentfs" + VFS_BIN="$REPO_ROOT/target/debug/vfs" fi -if [ ! -x "$AGENTFS_BIN" ]; then - echo "FAILED: AGENTFS_BIN is not executable: $AGENTFS_BIN" +if [ ! -x "$VFS_BIN" ]; then + echo "FAILED: VFS_BIN is not executable: $VFS_BIN" exit 1 fi @@ -129,7 +129,7 @@ cleanup_current() { if [ -n "$TEST_ROOT" ] && [ -d "$TEST_ROOT" ]; then case "$TEST_ROOT" in - "${TMPDIR:-/tmp}"/agentfs-teardown-bounded.*) + "${TMPDIR:-/tmp}"/vfs-teardown-bounded.*) rm -rf "$TEST_ROOT" ;; *) @@ -207,8 +207,8 @@ run_joiner_workload() { CARGO_HOME="$CARGO_HOME_FOR_TEST" \ RUSTUP_HOME="$RUSTUP_HOME_FOR_TEST" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN_FOR_TEST" \ - AGENTFS_FUSE_URING="$leg" \ - "$AGENTFS_BIN" run --session "$SESSION_ID" \ + VFS_FUSE_URING="$leg" \ + "$VFS_BIN" run --session "$SESSION_ID" \ /bin/bash -c ' set -euo pipefail mkdir -p bounded/repo/src @@ -234,7 +234,7 @@ verify_session_after_teardown() { HOME="$TEST_HOME" \ XDG_CACHE_HOME="$TEST_HOME/.cache" \ XDG_CONFIG_HOME="$TEST_HOME/.config" \ - "$AGENTFS_BIN" integrity "$DELTA_DB" --json >"$integrity_log" 2>&1 || + "$VFS_BIN" integrity "$DELTA_DB" --json >"$integrity_log" 2>&1 || { dump_failure_context "$leg integrity check failed after SIGTERM teardown" sed 's/^/ /' "$integrity_log" @@ -249,8 +249,8 @@ verify_session_after_teardown() { CARGO_HOME="$CARGO_HOME_FOR_TEST" \ RUSTUP_HOME="$RUSTUP_HOME_FOR_TEST" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN_FOR_TEST" \ - AGENTFS_FUSE_URING="$leg" \ - "$AGENTFS_BIN" run --session "$SESSION_ID" \ + VFS_FUSE_URING="$leg" \ + "$VFS_BIN" run --session "$SESSION_ID" \ /bin/bash -c ' set -euo pipefail test "$(cat bounded/payload.txt)" = "bounded teardown payload" @@ -290,7 +290,7 @@ assert_no_session_residue() { if [ -n "$FUSE_MNT" ] && mount | grep "$FUSE_MNT" >/dev/null 2>&1; then return 1 fi - if ps -e -o pid= -o args= | grep "$SESSION_ID" | grep agentfs | grep -v grep >/dev/null 2>&1; then + if ps -e -o pid= -o args= | grep "$SESSION_ID" | grep vfs | grep -v grep >/dev/null 2>&1; then return 1 fi return 0 @@ -303,12 +303,12 @@ run_leg() { label="uring" fi - TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-teardown-bounded.XXXXXX")" + TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-teardown-bounded.XXXXXX")" TEST_HOME="$TEST_ROOT/home" WORKDIR="$TEST_ROOT/work" LOGDIR="$TEST_ROOT/logs" SESSION_ID="teardown-bounded-$$-$leg" - RUN_DIR="$TEST_HOME/.agentfs/run/$SESSION_ID" + RUN_DIR="$TEST_HOME/.vfs/run/$SESSION_ID" DELTA_DB="$RUN_DIR/delta.db" FUSE_MNT="$RUN_DIR/mnt" OWNER_LOG="$LOGDIR/owner.log" @@ -330,7 +330,7 @@ run_leg() { ln -sf "$GIT_REAL" "$TEST_HOME/bin/git" printf '[core]\n\thooksPath = %s\n' "$TEST_HOME/git-hooks-none" >"$TEST_HOME/.gitconfig" PATH="$TEST_HOME/bin:$PATH" - # The PATH shim only holds host-side: inside `agentfs run` temp dirs are + # The PATH shim only holds host-side: inside `vfs run` temp dirs are # hidden, so sandboxed workloads must call "$GIT_REAL" by absolute path. export PATH GIT_REAL @@ -342,9 +342,9 @@ run_leg() { CARGO_HOME="$CARGO_HOME_FOR_TEST" \ RUSTUP_HOME="$RUSTUP_HOME_FOR_TEST" \ RUSTUP_TOOLCHAIN="$RUSTUP_TOOLCHAIN_FOR_TEST" \ - AGENTFS_FUSE_URING="$leg" \ - RUST_LOG=agentfs=info \ - "$AGENTFS_BIN" run --session "$SESSION_ID" \ + VFS_FUSE_URING="$leg" \ + RUST_LOG=vfs=info \ + "$VFS_BIN" run --session "$SESSION_ID" \ /bin/bash -c 'trap "exit 0" TERM INT; while :; do sleep 1; done' ) >"$OWNER_LOG" 2>&1 & OWNER_PID=$! @@ -416,7 +416,7 @@ run_abort_leg() { return 0 fi - TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-teardown-bounded.XXXXXX")" + TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/vfs-teardown-bounded.XXXXXX")" TEST_HOME="$TEST_ROOT/home" WORKDIR="$TEST_ROOT/work" LOGDIR="$TEST_ROOT/logs" @@ -451,10 +451,10 @@ INNER abort_rc=0 if command -v timeout >/dev/null 2>&1; then timeout "$((START_TIMEOUT + TEARDOWN_TIMEOUT))" \ - unshare -Ur -m "$inner" "$AGENTFS_BIN" "$TEST_HOME" "$WORKDIR" "$SESSION_ID" \ + unshare -Ur -m "$inner" "$VFS_BIN" "$TEST_HOME" "$WORKDIR" "$SESSION_ID" \ >"$OWNER_LOG" 2>&1 || abort_rc=$? else - unshare -Ur -m "$inner" "$AGENTFS_BIN" "$TEST_HOME" "$WORKDIR" "$SESSION_ID" \ + unshare -Ur -m "$inner" "$VFS_BIN" "$TEST_HOME" "$WORKDIR" "$SESSION_ID" \ >"$OWNER_LOG" 2>&1 || abort_rc=$? fi @@ -488,10 +488,10 @@ INNER return 0 } -# A timed-out abort leg can strand the namespaced agentfs owner (timeout only +# A timed-out abort leg can strand the namespaced vfs owner (timeout only # kills the unshare wrapper); reap it by PID via its unique session id. kill_abort_leg_stragglers() { - ps -e -o pid= -o args= | grep "$SESSION_ID" | grep agentfs | grep -v grep | + ps -e -o pid= -o args= | grep "$SESSION_ID" | grep vfs | grep -v grep | while read -r pid _; do kill -KILL "$pid" 2>/dev/null || true done diff --git a/crates/agentfs-cli/tests/test_fd.c b/crates/vfs-cli/tests/test_fd.c similarity index 100% rename from crates/agentfs-cli/tests/test_fd.c rename to crates/vfs-cli/tests/test_fd.c diff --git a/crates/vfs-core/.gitignore b/crates/vfs-core/.gitignore new file mode 100644 index 00000000..47493d9e --- /dev/null +++ b/crates/vfs-core/.gitignore @@ -0,0 +1,4 @@ +target + +# Vfs local databases +.vfs/ diff --git a/crates/agentfs-core/Cargo.toml b/crates/vfs-core/Cargo.toml similarity index 93% rename from crates/agentfs-core/Cargo.toml rename to crates/vfs-core/Cargo.toml index 442eec3c..1d1ac0f0 100644 --- a/crates/agentfs-core/Cargo.toml +++ b/crates/vfs-core/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "agentfs-core" +name = "vfs-core" version.workspace = true edition.workspace = true -description = "AgentFS SDK for Rust" +description = "Vfs SDK for Rust" license.workspace = true [lints] diff --git a/crates/agentfs-core/benches/overlayfs.rs b/crates/vfs-core/benches/overlayfs.rs similarity index 90% rename from crates/agentfs-core/benches/overlayfs.rs rename to crates/vfs-core/benches/overlayfs.rs index 0156eed2..63ceb31e 100644 --- a/crates/agentfs-core/benches/overlayfs.rs +++ b/crates/vfs-core/benches/overlayfs.rs @@ -2,10 +2,10 @@ //! //! Run with: cargo bench --bench overlayfs -use agentfs_core::fs::{AgentFS, FileSystem, HostFS, OverlayFS}; use criterion::{criterion_group, criterion_main, Criterion}; use std::sync::Arc; use tempfile::tempdir; +use vfs_core::fs::{FileSystem, HostFS, OverlayFS, Vfs}; const ROOT_INO: i64 = 1; @@ -27,9 +27,9 @@ fn bench_remove_file(c: &mut Criterion) { let base = Arc::new(HostFS::new(base_dir.path()).expect("Failed to create HostFS")); let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()) + let delta = Vfs::new(db_path.to_str().unwrap()) .await - .expect("Failed to create AgentFS"); + .expect("Failed to create Vfs"); let overlay = OverlayFS::new(base, delta); overlay diff --git a/crates/agentfs-core/benches/workload.rs b/crates/vfs-core/benches/workload.rs similarity index 96% rename from crates/agentfs-core/benches/workload.rs rename to crates/vfs-core/benches/workload.rs index 3481e84f..b54842d1 100644 --- a/crates/agentfs-core/benches/workload.rs +++ b/crates/vfs-core/benches/workload.rs @@ -5,14 +5,12 @@ //! //! Run with: cargo bench --bench workload -use agentfs_core::fs::{ - AgentFS, FileSystem, HostFS, OverlayFS, DEFAULT_DIR_MODE, DEFAULT_FILE_MODE, -}; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use rand::distributions::WeightedIndex; use rand::prelude::*; use std::sync::Arc; use tempfile::tempdir; +use vfs_core::fs::{FileSystem, HostFS, OverlayFS, Vfs, DEFAULT_DIR_MODE, DEFAULT_FILE_MODE}; const ROOT_INO: i64 = 1; @@ -306,7 +304,7 @@ impl WorkloadGenerator { } } -async fn resolve_path(overlay: &OverlayFS, path: &str) -> agentfs_core::error::Result> { +async fn resolve_path(overlay: &OverlayFS, path: &str) -> vfs_core::error::Result> { let mut ino = ROOT_INO; for component in path.trim_matches('/').split('/').filter(|c| !c.is_empty()) { let Some(stats) = overlay.lookup(ino, component).await? else { @@ -335,7 +333,7 @@ fn split_parent_name(path: &str) -> Option<(String, String)> { async fn resolve_parent( overlay: &OverlayFS, path: &str, -) -> agentfs_core::error::Result> { +) -> vfs_core::error::Result> { let Some((parent, name)) = split_parent_name(path) else { return Ok(None); }; @@ -412,9 +410,9 @@ fn bench_workload(c: &mut Criterion) { HostFS::new(base_dir.path()).expect("Failed to create HostFS"), ); let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()) + let delta = Vfs::new(db_path.to_str().unwrap()) .await - .expect("Failed to create AgentFS"); + .expect("Failed to create Vfs"); let overlay = OverlayFS::new(base, delta); overlay @@ -471,9 +469,9 @@ fn bench_individual_ops(c: &mut Criterion) { HostFS::new(base_dir.path()).expect("Failed to create HostFS"), ); let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()) + let delta = Vfs::new(db_path.to_str().unwrap()) .await - .expect("Failed to create AgentFS"); + .expect("Failed to create Vfs"); let overlay = OverlayFS::new(base, delta); overlay diff --git a/crates/agentfs-core/src/config/core.rs b/crates/vfs-core/src/config/core.rs similarity index 87% rename from crates/agentfs-core/src/config/core.rs rename to crates/vfs-core/src/config/core.rs index b204ab6a..359bcc49 100644 --- a/crates/agentfs-core/src/config/core.rs +++ b/crates/vfs-core/src/config/core.rs @@ -12,14 +12,14 @@ pub const DEFAULT_WRITE_BATCH_GLOBAL_BYTES: usize = 64 * 1024 * 1024; pub const DEFAULT_WRITE_BATCH_TXN_INODES: usize = 1024; pub const DEFAULT_WRITE_BATCH_TXN_BYTES: usize = 32 * 1024 * 1024; -const WRITE_BATCHER_MS_ENV: &str = "AGENTFS_BATCH_MS"; -const WRITE_BATCHER_BYTES_ENV: &str = "AGENTFS_BATCH_BYTES"; -const WRITE_BATCHER_GLOBAL_BYTES_ENV: &str = "AGENTFS_BATCH_GLOBAL_BYTES"; -const WRITE_BATCHER_TXN_INODES_ENV: &str = "AGENTFS_BATCH_TXN_INODES"; -const WRITE_BATCHER_TXN_BYTES_ENV: &str = "AGENTFS_BATCH_TXN_BYTES"; -const OVERLAY_READS_ENV: &str = "AGENTFS_OVERLAY_READS"; -const DRAIN_ON_SETATTR_ENV: &str = "AGENTFS_DRAIN_ON_SETATTR"; -const KEEPCACHE_DELTA_ENV: &str = "AGENTFS_KEEPCACHE_DELTA"; +const WRITE_BATCHER_MS_ENV: &str = "VFS_BATCH_MS"; +const WRITE_BATCHER_BYTES_ENV: &str = "VFS_BATCH_BYTES"; +const WRITE_BATCHER_GLOBAL_BYTES_ENV: &str = "VFS_BATCH_GLOBAL_BYTES"; +const WRITE_BATCHER_TXN_INODES_ENV: &str = "VFS_BATCH_TXN_INODES"; +const WRITE_BATCHER_TXN_BYTES_ENV: &str = "VFS_BATCH_TXN_BYTES"; +const OVERLAY_READS_ENV: &str = "VFS_OVERLAY_READS"; +const DRAIN_ON_SETATTR_ENV: &str = "VFS_DRAIN_ON_SETATTR"; +const KEEPCACHE_DELTA_ENV: &str = "VFS_KEEPCACHE_DELTA"; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Geometry { diff --git a/crates/agentfs-core/src/config/env.rs b/crates/vfs-core/src/config/env.rs similarity index 100% rename from crates/agentfs-core/src/config/env.rs rename to crates/vfs-core/src/config/env.rs diff --git a/crates/agentfs-core/src/config/mod.rs b/crates/vfs-core/src/config/mod.rs similarity index 91% rename from crates/agentfs-core/src/config/mod.rs rename to crates/vfs-core/src/config/mod.rs index 7db7a66f..9b2dc1c5 100644 --- a/crates/agentfs-core/src/config/mod.rs +++ b/crates/vfs-core/src/config/mod.rs @@ -22,12 +22,12 @@ mod tests { #[test] fn runtime_env_bool_grammar_is_shared() { let covered_runtime_bool_knobs = [ - "AGENTFS_FUSE_WRITEBACK", - "AGENTFS_FUSE_NOOPEN", - "AGENTFS_FUSE_NOFLUSH", - "AGENTFS_FUSE_URING", - "AGENTFS_PROFILE", - "AGENTFS_DRAIN_ON_SETATTR", + "VFS_FUSE_WRITEBACK", + "VFS_FUSE_NOOPEN", + "VFS_FUSE_NOFLUSH", + "VFS_FUSE_URING", + "VFS_PROFILE", + "VFS_DRAIN_ON_SETATTR", ]; println!( "covered runtime bool knobs: {}", @@ -58,7 +58,7 @@ mod tests { ); } - let key = format!("AGENTFS_TEST_BOOL_GRAMMAR_{}", std::process::id()); + let key = format!("VFS_TEST_BOOL_GRAMMAR_{}", std::process::id()); std::env::set_var(&key, "garbage"); let reader = EnvReader::new(); assert!( @@ -78,21 +78,21 @@ mod tests { let repo_root = manifest_dir .parent() .and_then(Path::parent) - .expect("crates/agentfs-core should live two levels below the repo root"); + .expect("crates/vfs-core should live two levels below the repo root"); let source_roots = [ SourceRoot::new( - "crates/agentfs-core/src", + "crates/vfs-core/src", manifest_dir.join("src"), SourceKind::Sdk, ), SourceRoot::new( - "crates/agentfs-cli/src", - repo_root.join("crates").join("agentfs-cli").join("src"), + "crates/vfs-cli/src", + repo_root.join("crates").join("vfs-cli").join("src"), SourceKind::Cli, ), SourceRoot::new( - "crates/agentfs-fuse/src", - repo_root.join("crates").join("agentfs-fuse").join("src"), + "crates/vfs-fuse/src", + repo_root.join("crates").join("vfs-fuse").join("src"), SourceKind::Fuse, ), ]; @@ -241,7 +241,7 @@ mod tests { #[test] fn core_config_ignores_legacy_partial_origin_env() { - let key = concat!("AGENTFS_OVERLAY_", "PARTIAL_ORIGIN"); + let key = concat!("VFS_OVERLAY_", "PARTIAL_ORIGIN"); let previous = std::env::var(key).ok(); std::env::set_var(key, "1"); diff --git a/crates/agentfs-core/src/error.rs b/crates/vfs-core/src/error.rs similarity index 97% rename from crates/agentfs-core/src/error.rs rename to crates/vfs-core/src/error.rs index 8230d585..58b546f2 100644 --- a/crates/agentfs-core/src/error.rs +++ b/crates/vfs-core/src/error.rs @@ -1,8 +1,8 @@ -//! Error types for the AgentFS SDK. +//! Error types for the Vfs SDK. use thiserror::Error; -/// The main error type for the AgentFS SDK. +/// The main error type for the Vfs SDK. /// /// Wrapper variants chain their cause through `source()` only and keep it out /// of `Display`: `#[from]` already exposes the inner error to reporters that diff --git a/crates/agentfs-core/src/fs/host/common.rs b/crates/vfs-core/src/fs/host/common.rs similarity index 100% rename from crates/agentfs-core/src/fs/host/common.rs rename to crates/vfs-core/src/fs/host/common.rs diff --git a/crates/agentfs-core/src/fs/host/darwin.rs b/crates/vfs-core/src/fs/host/darwin.rs similarity index 100% rename from crates/agentfs-core/src/fs/host/darwin.rs rename to crates/vfs-core/src/fs/host/darwin.rs diff --git a/crates/agentfs-core/src/fs/host/linux.rs b/crates/vfs-core/src/fs/host/linux.rs similarity index 100% rename from crates/agentfs-core/src/fs/host/linux.rs rename to crates/vfs-core/src/fs/host/linux.rs diff --git a/crates/agentfs-core/src/fs/host/mod.rs b/crates/vfs-core/src/fs/host/mod.rs similarity index 100% rename from crates/agentfs-core/src/fs/host/mod.rs rename to crates/vfs-core/src/fs/host/mod.rs diff --git a/crates/agentfs-core/src/fs/mod.rs b/crates/vfs-core/src/fs/mod.rs similarity index 98% rename from crates/agentfs-core/src/fs/mod.rs rename to crates/vfs-core/src/fs/mod.rs index 9a1bf1cf..9b360f98 100644 --- a/crates/agentfs-core/src/fs/mod.rs +++ b/crates/vfs-core/src/fs/mod.rs @@ -1,6 +1,6 @@ -pub mod agentfs; pub mod host; pub mod overlay; +pub mod vfs; use crate::error::Result; use async_trait::async_trait; @@ -8,7 +8,6 @@ use std::sync::Arc; use thiserror::Error; // Re-export implementations -pub use agentfs::{AgentFS, ImportEntry, ImportOptions, ImportSession, ImportedEntry}; #[cfg(target_os = "macos")] pub use host::HostFS; #[cfg(target_os = "linux")] @@ -16,6 +15,7 @@ pub use host::HostFS; pub use overlay::{ OverlayFS, PartialOriginMode, PartialOriginPolicy, DEFAULT_PARTIAL_ORIGIN_THRESHOLD_BYTES, }; +pub use vfs::{ImportEntry, ImportOptions, ImportSession, ImportedEntry, Vfs}; /// Filesystem-specific errors with errno semantics #[derive(Debug, Error)] @@ -488,10 +488,10 @@ pub trait FileSystem: Send + Sync { /// Register a hook that runs when this filesystem reaps an inode. /// - /// Filesystems without an AgentFS lifecycle return `false`; callers should + /// Filesystems without a Vfs lifecycle return `false`; callers should /// still invalidate adapter-local state around explicit unlink/rename /// operations when they need immediate semantics over wrapper inode spaces. - fn register_reap_hook(&self, _hook: Arc) -> bool { + fn register_reap_hook(&self, _hook: Arc) -> bool { false } diff --git a/crates/agentfs-core/src/fs/overlay/copyup.rs b/crates/vfs-core/src/fs/overlay/copyup.rs similarity index 100% rename from crates/agentfs-core/src/fs/overlay/copyup.rs rename to crates/vfs-core/src/fs/overlay/copyup.rs diff --git a/crates/agentfs-core/src/fs/overlay/fs.rs b/crates/vfs-core/src/fs/overlay/fs.rs similarity index 99% rename from crates/agentfs-core/src/fs/overlay/fs.rs rename to crates/vfs-core/src/fs/overlay/fs.rs index b2171b43..8b31e98f 100644 --- a/crates/agentfs-core/src/fs/overlay/fs.rs +++ b/crates/vfs-core/src/fs/overlay/fs.rs @@ -418,7 +418,7 @@ impl FileSystem for OverlayFS { let info = self.get_inode_info(ino).ok_or(FsError::NotFound)?; match info.layer { Layer::Base => Ok(None), - // Delta (DB-backed) files inherit the AgentFS keep-cache policy: + // Delta (DB-backed) files inherit the Vfs keep-cache policy: // the adapter fingerprint guard revalidates per open. Layer::Delta => { FileSystem::keep_cache_for_read_open(&self.delta, info.underlying_ino, flags).await @@ -893,7 +893,7 @@ impl FileSystem for OverlayFS { match info.layer { Layer::Delta => { - // Delta (AgentFS) doesn't cache fds, but call it anyway for completeness. + // Delta (Vfs) doesn't cache fds, but call it anyway for completeness. FileSystem::forget(&self.delta, info.underlying_ino, nlookup).await; } Layer::Base => { diff --git a/crates/agentfs-core/src/fs/overlay/maps.rs b/crates/vfs-core/src/fs/overlay/maps.rs similarity index 100% rename from crates/agentfs-core/src/fs/overlay/maps.rs rename to crates/vfs-core/src/fs/overlay/maps.rs diff --git a/crates/agentfs-core/src/fs/overlay/mod.rs b/crates/vfs-core/src/fs/overlay/mod.rs similarity index 97% rename from crates/agentfs-core/src/fs/overlay/mod.rs rename to crates/vfs-core/src/fs/overlay/mod.rs index c1e47fa3..a5e9de89 100644 --- a/crates/agentfs-core/src/fs/overlay/mod.rs +++ b/crates/vfs-core/src/fs/overlay/mod.rs @@ -23,7 +23,7 @@ use std::{ use turso::{Connection, Value}; use super::{ - agentfs::{AgentFS, ReapHook}, + vfs::{ReapHook, Vfs}, BoxedFile, File, FileSystem, Stats, WriteRange, }; @@ -166,7 +166,7 @@ impl ReapHook for OverlaySidecarReapHook { /// A copy-on-write overlay filesystem using inode-based operations. /// -/// Combines a read-only base layer with a writable delta layer (`AgentFS`). +/// Combines a read-only base layer with a writable delta layer (`Vfs`). /// All modifications are written to the delta layer, while reads fall back /// to the base layer if not found in delta. /// @@ -176,7 +176,7 @@ impl ReapHook for OverlaySidecarReapHook { /// directory is removed through the overlay and later recreated at the same /// path, base children can resurface on `readdir`, and the base-directory /// rename guard will still return `EXDEV` while that base path is visible. -/// That diverges from kernel overlayfs, but it is the accepted AgentFS +/// That diverges from kernel overlayfs, but it is the accepted Vfs /// behavior. The `resolves_to_visible_base_directory` signal must stay shared /// by both readdir base-child merging and the rename guard if opacity is ever /// revisited. @@ -184,7 +184,7 @@ pub struct OverlayFS { /// The underlying read-only base filesystem base: Arc, /// The delta layer where modifications go - delta: AgentFS, + delta: Vfs, /// Overlay inode maps, reverse maps, path maps, allocator, and lookup refs. maps: Mutex, /// Set of whiteout paths (deleted from base) @@ -200,14 +200,14 @@ pub struct OverlayFS { impl OverlayFS { /// Create a new overlay filesystem - pub fn new(base: Arc, delta: AgentFS) -> Self { + pub fn new(base: Arc, delta: Vfs) -> Self { let partial_origin_policy = delta.partial_origin_policy(); Self::new_with_partial_origin_policy(base, delta, partial_origin_policy) } pub fn new_with_partial_origin_policy( base: Arc, - delta: AgentFS, + delta: Vfs, partial_origin_policy: PartialOriginPolicy, ) -> Self { Self::new_with_partial_origin_policy_inner(base, delta, partial_origin_policy) @@ -216,7 +216,7 @@ impl OverlayFS { #[cfg(test)] fn new_with_partial_origin( base: Arc, - delta: AgentFS, + delta: Vfs, partial_origin_enabled: bool, ) -> Self { let mode = if partial_origin_enabled { @@ -229,7 +229,7 @@ impl OverlayFS { fn new_with_partial_origin_policy_inner( base: Arc, - delta: AgentFS, + delta: Vfs, partial_origin_policy: PartialOriginPolicy, ) -> Self { delta.register_reap_hook(Self::sidecar_reap_hook()); @@ -325,7 +325,7 @@ impl OverlayFS { /// Get a reference to the delta layer #[cfg(test)] - fn delta(&self) -> &AgentFS { + fn delta(&self) -> &Vfs { &self.delta } } diff --git a/crates/agentfs-core/src/fs/overlay/partial.rs b/crates/vfs-core/src/fs/overlay/partial.rs similarity index 99% rename from crates/agentfs-core/src/fs/overlay/partial.rs rename to crates/vfs-core/src/fs/overlay/partial.rs index 2afabd5e..96c44260 100644 --- a/crates/agentfs-core/src/fs/overlay/partial.rs +++ b/crates/vfs-core/src/fs/overlay/partial.rs @@ -1,4 +1,4 @@ -use super::super::agentfs::store::{self, ChunkWriteHooks, WriteRangeRef}; +use super::super::vfs::store::{self, ChunkWriteHooks, WriteRangeRef}; use super::*; use crate::fs::{File, FsError, WriteRange}; use std::sync::Arc; @@ -15,7 +15,7 @@ pub(super) struct PartialOrigin { } pub(super) struct OverlayPartialFile { - pub(super) delta: AgentFS, + pub(super) delta: Vfs, pub(super) base: Arc, pub(super) base_file: super::super::BoxedFile, pub(super) origin: PartialOrigin, diff --git a/crates/agentfs-core/src/fs/overlay/tests.rs b/crates/vfs-core/src/fs/overlay/tests.rs similarity index 94% rename from crates/agentfs-core/src/fs/overlay/tests.rs rename to crates/vfs-core/src/fs/overlay/tests.rs index 1e03db3e..352f80ca 100644 --- a/crates/agentfs-core/src/fs/overlay/tests.rs +++ b/crates/vfs-core/src/fs/overlay/tests.rs @@ -15,7 +15,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -205,7 +205,7 @@ let nested = overlay.lookup(subdir.ino, "nested.txt").await?.unwrap(); let nested_base_ino = overlay.get_inode_info(nested.ino).unwrap().underlying_ino; - let (delta_stats, _file) = ::create_file( + let (delta_stats, _file) = ::create_file( overlay.delta(), ROOT_INO, "base.txt", @@ -236,7 +236,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let base_content = patterned_bytes(chunk_size * 3 + 17, 0x21); std::fs::write(base_dir.path().join("large.bin"), &base_content)?; @@ -285,7 +285,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let mut expected = patterned_bytes(chunk_size * 2 + 17, 0x2a); std::fs::write(base_dir.path().join("large.bin"), &expected)?; @@ -345,7 +345,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let base_content = patterned_bytes(chunk_size * 2 + 11, 0x17); std::fs::write(base_dir.path().join("large.bin"), &base_content)?; @@ -381,7 +381,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let threshold = (chunk_size * 2) as u64; let small_content = patterned_bytes(chunk_size + 31, 0x05); @@ -433,7 +433,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let base_path = base_dir.path().join("file.txt"); std::fs::write(&base_path, b"metadata base")?; @@ -492,7 +492,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let mut expected = patterned_bytes(chunk_size * 2 + 32, 0x42); std::fs::write(base_dir.path().join("large.bin"), &expected)?; @@ -533,7 +533,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let base_content = patterned_bytes(chunk_size * 2, 0x63); std::fs::write(base_dir.path().join("large.bin"), &base_content)?; @@ -564,7 +564,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; std::fs::write(base_dir.path().join("large.bin"), b"base contents")?; let base = Arc::new(HostFS::new(base_dir.path())?); @@ -592,7 +592,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; std::fs::write(base_dir.path().join("large.bin"), b"base contents")?; let base = Arc::new(HostFS::new(base_dir.path())?); @@ -619,7 +619,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let mut expected = patterned_bytes(chunk_size * 2 + 9, 0x31); std::fs::write(base_dir.path().join("large.bin"), &expected)?; @@ -638,7 +638,7 @@ drop(file); drop(overlay); - let reopened_delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let reopened_delta = Vfs::new(db_path.to_str().unwrap()).await?; let reopened_base = Arc::new(HostFS::new(base_dir.path())?); let reopened = OverlayFS::new_with_partial_origin(reopened_base, reopened_delta, true); reopened.init(base_dir.path().to_str().unwrap()).await?; @@ -659,7 +659,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let mut expected = patterned_bytes(chunk_size + 9, 0x41); std::fs::write(base_dir.path().join("large.bin"), &expected)?; @@ -676,7 +676,7 @@ drop(file); drop(overlay); - let reopened_delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let reopened_delta = Vfs::new(db_path.to_str().unwrap()).await?; let reopened_base = Arc::new(HostFS::new(base_dir.path())?); let reopened = OverlayFS::new_with_partial_origin(reopened_base, reopened_delta, true); reopened.init(base_dir.path().to_str().unwrap()).await?; @@ -701,7 +701,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let mut expected = patterned_bytes(chunk_size + 16, 0x51); std::fs::write(base_dir.path().join("large.bin"), &expected)?; @@ -732,7 +732,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let base_content = patterned_bytes(chunk_size + 16, 0x71); std::fs::write(base_dir.path().join("large.bin"), &base_content)?; @@ -749,7 +749,7 @@ std::fs::write(base_dir.path().join("large.bin"), b"changed base")?; - let reopened_delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let reopened_delta = Vfs::new(db_path.to_str().unwrap()).await?; let reopened_base = Arc::new(HostFS::new(base_dir.path())?); let reopened = OverlayFS::new_with_partial_origin(reopened_base, reopened_delta, true); reopened.init(base_dir.path().to_str().unwrap()).await?; @@ -768,7 +768,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let base_file = base_dir.path().join("large.bin"); let base_content = patterned_bytes(chunk_size * 2, 0x37); @@ -799,7 +799,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let base_content = patterned_bytes(chunk_size + 16, 0x73); std::fs::write(base_dir.path().join("large.bin"), &base_content)?; @@ -818,7 +818,7 @@ let changed_same_size = patterned_bytes(base_content.len(), 0x74); std::fs::write(base_dir.path().join("large.bin"), changed_same_size)?; - let reopened_delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let reopened_delta = Vfs::new(db_path.to_str().unwrap()).await?; let reopened_base = Arc::new(HostFS::new(base_dir.path())?); let reopened = OverlayFS::new_with_partial_origin(reopened_base, reopened_delta, true); reopened.init(base_dir.path().to_str().unwrap()).await?; @@ -838,7 +838,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); let restored_db_path = delta_dir.path().join("restored.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let mut expected = patterned_bytes(chunk_size * 2 + 33, 0x91); std::fs::write(base_dir.path().join("large.bin"), &expected)?; @@ -858,7 +858,7 @@ std::fs::copy(&db_path, &restored_db_path)?; - let restored_delta = AgentFS::new(restored_db_path.to_str().unwrap()).await?; + let restored_delta = Vfs::new(restored_db_path.to_str().unwrap()).await?; let restored_base = Arc::new(HostFS::new(base_dir.path())?); let restored = OverlayFS::new_with_partial_origin(restored_base, restored_delta, true); restored.init(base_dir.path().to_str().unwrap()).await?; @@ -887,7 +887,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let base_content = patterned_bytes(chunk_size + 19, 0xa1); std::fs::write(base_dir.path().join("large.bin"), &base_content)?; @@ -937,7 +937,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); std::fs::write( base_dir.path().join("src.bin"), @@ -992,8 +992,8 @@ let crash_db = crash_dir.path().join("crash.db"); let crash_db_path = crash_db.to_string_lossy().into_owned(); { - let agent = crate::AgentFS::open( - crate::AgentFSOptions::with_path(crash_db_path.clone()).with_base(base_dir.path()), + let agent = crate::Vfs::open( + crate::VfsOptions::with_path(crash_db_path.clone()).with_base(base_dir.path()), ) .await?; let (stats, file) = FileSystem::create_file( @@ -1012,8 +1012,8 @@ std::mem::forget(file); } - let reopened_agent = crate::AgentFS::open( - crate::AgentFSOptions::with_path(crash_db_path).with_base(base_dir.path()), + let reopened_agent = crate::Vfs::open( + crate::VfsOptions::with_path(crash_db_path).with_base(base_dir.path()), ) .await?; let reopened_base = Arc::new(HostFS::new(base_dir.path())?); @@ -1029,7 +1029,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; assert_eq!(delta.reap_hook_count(), 0); assert!( @@ -1054,7 +1054,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let mut expected = patterned_bytes(chunk_size + 21, 0xb1); std::fs::write(base_dir.path().join("large.bin"), &expected)?; @@ -1101,7 +1101,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let mut expected = patterned_bytes(chunk_size + 23, 0xc1); std::fs::write(base_dir.path().join("large.bin"), &expected)?; @@ -1122,7 +1122,7 @@ .await?; drop(overlay); - let reopened_delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let reopened_delta = Vfs::new(db_path.to_str().unwrap()).await?; let reopened_base = Arc::new(HostFS::new(base_dir.path())?); let reopened = OverlayFS::new_with_partial_origin(reopened_base, reopened_delta, true); reopened.init(base_dir.path().to_str().unwrap()).await?; @@ -1148,7 +1148,7 @@ let base_dir = tempdir()?; let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let chunk_size = delta.chunk_size(); let base_content = patterned_bytes(chunk_size * 3 + 17, 0x84); std::fs::write(base_dir.path().join("large.bin"), &base_content)?; @@ -1342,7 +1342,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -1508,7 +1508,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -1550,7 +1550,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -1600,7 +1600,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -1644,7 +1644,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -1691,7 +1691,7 @@ /// delta entry with the same name from a wrong parent. /// /// Reproduces the ENOTDIR bug: - /// 1. Base has /crates/agentfs-core/ (directories) + /// 1. Base has /crates/vfs-core/ (directories) /// 2. Delta has a *file* named "rust" under delta root (from some unrelated op) /// 3. lookup(sdk_ino, "rust") should return the base *directory*, not the delta file /// @@ -1700,11 +1700,11 @@ /// Then delta.lookup(root, "rust") finds the unrelated file and returns it. #[tokio::test] async fn test_overlay_lookup_base_subdir_not_shadowed_by_wrong_delta_parent() -> Result<()> { - // Base: /crates/agentfs-core/Cargo.toml (nested directories + file) + // Base: /crates/vfs-core/Cargo.toml (nested directories + file) let base_dir = tempdir()?; - std::fs::create_dir_all(base_dir.path().join("crates/agentfs-core"))?; + std::fs::create_dir_all(base_dir.path().join("crates/vfs-core"))?; std::fs::write( - base_dir.path().join("crates/agentfs-core/Cargo.toml"), + base_dir.path().join("crates/vfs-core/Cargo.toml"), b"[package]\nname = \"test\"", )?; @@ -1712,16 +1712,16 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; - // Create a *file* named "agentfs-core" at the overlay root (in delta). + // Create a *file* named "vfs-core" at the overlay root (in delta). // This is the entry that could shadow the base directory if the delta // path walk uses the wrong parent inode. let (_file_stats, file) = overlay - .create_file(ROOT_INO, "agentfs-core", DEFAULT_FILE_MODE, 0, 0) + .create_file(ROOT_INO, "vfs-core", DEFAULT_FILE_MODE, 0, 0) .await?; file.pwrite(0, b"this is a file, not a directory").await?; @@ -1729,18 +1729,18 @@ let crates_stats = overlay.lookup(ROOT_INO, "crates").await?.unwrap(); assert!(crates_stats.is_directory(), "crates should be a directory"); - // Lookup "agentfs-core" under "crates" — MUST return the base + // Lookup "vfs-core" under "crates" — MUST return the base // *directory*, not the delta *file* with the same name under root. let core_stats = overlay - .lookup(crates_stats.ino, "agentfs-core") + .lookup(crates_stats.ino, "vfs-core") .await? .unwrap(); assert!( core_stats.is_directory(), - "crates/agentfs-core should be a directory from base, not the file from delta root" + "crates/vfs-core should be a directory from base, not the file from delta root" ); - // Verify we can traverse further into crates/agentfs-core/Cargo.toml + // Verify we can traverse further into crates/vfs-core/Cargo.toml let toml_stats = overlay.lookup(core_stats.ino, "Cargo.toml").await?.unwrap(); assert!(toml_stats.is_file(), "Cargo.toml should be a file"); @@ -1754,50 +1754,50 @@ #[tokio::test] async fn test_overlay_readdir_plus_consistent_with_lookup_for_base_dir() -> Result<()> { let base_dir = tempdir()?; - std::fs::create_dir_all(base_dir.path().join("crates/agentfs-core"))?; - std::fs::write(base_dir.path().join("crates/agentfs-core/lib.rs"), b"fn main() {}")?; + std::fs::create_dir_all(base_dir.path().join("crates/vfs-core"))?; + std::fs::write(base_dir.path().join("crates/vfs-core/lib.rs"), b"fn main() {}")?; let base = Arc::new(HostFS::new(base_dir.path())?); let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; - // Create a file named "agentfs-core" at the root in delta (wrong-parent scenario) + // Create a file named "vfs-core" at the root in delta (wrong-parent scenario) let (_stats, file) = overlay - .create_file(ROOT_INO, "agentfs-core", DEFAULT_FILE_MODE, 0, 0) + .create_file(ROOT_INO, "vfs-core", DEFAULT_FILE_MODE, 0, 0) .await?; file.pwrite(0, b"decoy").await?; // Lookup "crates" to get its overlay inode let crates_stats = overlay.lookup(ROOT_INO, "crates").await?.unwrap(); - // readdir_plus on "crates" should list "agentfs-core" as a directory + // readdir_plus on "crates" should list "vfs-core" as a directory let entries = overlay .readdir_plus(crates_stats.ino) .await? .expect("readdir_plus should succeed on crates"); - let core_entry = entries.iter().find(|e| e.name == "agentfs-core"); + let core_entry = entries.iter().find(|e| e.name == "vfs-core"); assert!( core_entry.is_some(), - "readdir_plus should list 'agentfs-core'" + "readdir_plus should list 'vfs-core'" ); assert!( core_entry.unwrap().stats.is_directory(), - "readdir_plus should report 'agentfs-core' as directory" + "readdir_plus should report 'vfs-core' as directory" ); - // lookup on "crates" for "agentfs-core" should also return a directory + // lookup on "crates" for "vfs-core" should also return a directory let core_lookup = overlay - .lookup(crates_stats.ino, "agentfs-core") + .lookup(crates_stats.ino, "vfs-core") .await? .unwrap(); assert!( core_lookup.is_directory(), - "lookup should report 'agentfs-core' as directory, matching readdir_plus" + "lookup should report 'vfs-core' as directory, matching readdir_plus" ); Ok(()) @@ -1819,7 +1819,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -1887,7 +1887,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -1918,13 +1918,13 @@ Ok(()) } - /// Test the exact cargo scenario: path dependency at ../crates/agentfs-core/Cargo.toml + /// Test the exact cargo scenario: path dependency at ../crates/vfs-core/Cargo.toml /// accessed after some delta writes have occurred. #[tokio::test] async fn test_overlay_cargo_path_dependency_scenario() -> Result<()> { - // Simulate the agentfs repo structure: + // Simulate the vfs repo structure: // /cli/Cargo.toml - // /crates/agentfs-core/Cargo.toml + // /crates/vfs-core/Cargo.toml let base_dir = tempdir()?; std::fs::create_dir_all(base_dir.path().join("cli/src"))?; std::fs::write( @@ -1932,13 +1932,13 @@ b"[package]\nname = \"cli\"", )?; std::fs::write(base_dir.path().join("cli/src/main.rs"), b"fn main() {}")?; - std::fs::create_dir_all(base_dir.path().join("crates/agentfs-core/src"))?; + std::fs::create_dir_all(base_dir.path().join("crates/vfs-core/src"))?; std::fs::write( - base_dir.path().join("crates/agentfs-core/Cargo.toml"), + base_dir.path().join("crates/vfs-core/Cargo.toml"), b"[package]\nname = \"sdk\"", )?; std::fs::write( - base_dir.path().join("crates/agentfs-core/src/lib.rs"), + base_dir.path().join("crates/vfs-core/src/lib.rs"), b"pub fn hello() {}", )?; @@ -1946,7 +1946,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -1955,18 +1955,18 @@ let cli_stats = overlay.lookup(ROOT_INO, "cli").await?.unwrap(); let _target_stats = overlay.mkdir(cli_stats.ino, "target", 0o755, 0, 0).await?; - // Now simulate cargo resolving ../crates/agentfs-core/Cargo.toml + // Now simulate cargo resolving ../crates/vfs-core/Cargo.toml // This is the path that fails with ENOTDIR in the bug report let crates_stats = overlay.lookup(ROOT_INO, "crates").await?.unwrap(); assert!(crates_stats.is_directory(), "crates must be a directory"); let core_stats = overlay - .lookup(crates_stats.ino, "agentfs-core") + .lookup(crates_stats.ino, "vfs-core") .await? .unwrap(); assert!( core_stats.is_directory(), - "crates/agentfs-core must be a directory (ENOTDIR bug)" + "crates/vfs-core must be a directory (ENOTDIR bug)" ); let toml_stats = overlay.lookup(core_stats.ino, "Cargo.toml").await?.unwrap(); @@ -2002,7 +2002,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2068,7 +2068,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2113,7 +2113,7 @@ // Session 1: create delta file (creates origin mapping for /dir/) let base = Arc::new(HostFS::new(base_dir.path())?); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2125,7 +2125,7 @@ // Session 2: remount and verify base files are still accessible let base = Arc::new(HostFS::new(base_dir.path())?); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2159,7 +2159,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2212,7 +2212,7 @@ // Session 1: create delta file (creates origin mapping for /dir/) let base = Arc::new(HostFS::new(base_dir.path())?); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2224,7 +2224,7 @@ // Session 2: remount and unlink the base file let base = Arc::new(HostFS::new(base_dir.path())?); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2253,7 +2253,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2306,7 +2306,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2362,7 +2362,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2429,7 +2429,7 @@ // Session 1: copy-up file.txt to delta via write let base = Arc::new(HostFS::new(base_dir.path())?); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2440,7 +2440,7 @@ // Session 2: remount, unlink, recreate, verify new content let base = Arc::new(HostFS::new(base_dir.path())?); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2475,7 +2475,7 @@ // Session 1: hard-link triggers copy_up of src.txt let base = Arc::new(HostFS::new(base_dir.path())?); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2487,7 +2487,7 @@ // Session 2: remount, unlink source, verify link survives let base = Arc::new(HostFS::new(base_dir.path())?); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2513,7 +2513,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2584,7 +2584,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2643,7 +2643,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2689,7 +2689,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2739,7 +2739,7 @@ let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2780,7 +2780,7 @@ let base = Arc::new(HostFS::new(base_dir.path())?); let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2837,7 +2837,7 @@ let base = Arc::new(HostFS::new(base_dir.path())?); let delta_dir = tempdir()?; let db_path = delta_dir.path().join("delta.db"); - let delta = AgentFS::new(db_path.to_str().unwrap()).await?; + let delta = Vfs::new(db_path.to_str().unwrap()).await?; let overlay = OverlayFS::new(base, delta); overlay.init(base_dir.path().to_str().unwrap()).await?; @@ -2931,7 +2931,7 @@ .unwrap_or(0)) } - async fn insert_manual_sidecars(delta: &AgentFS, ino: i64) -> Result<()> { + async fn insert_manual_sidecars(delta: &Vfs, ino: i64) -> Result<()> { let conn = delta.get_connection().await?; conn.execute( "INSERT OR REPLACE INTO fs_origin (delta_ino, base_ino) VALUES (?, ?)", diff --git a/crates/agentfs-core/src/fs/overlay/whiteouts.rs b/crates/vfs-core/src/fs/overlay/whiteouts.rs similarity index 100% rename from crates/agentfs-core/src/fs/overlay/whiteouts.rs rename to crates/vfs-core/src/fs/overlay/whiteouts.rs diff --git a/crates/agentfs-core/src/fs/agentfs/batcher.rs b/crates/vfs-core/src/fs/vfs/batcher.rs similarity index 94% rename from crates/agentfs-core/src/fs/agentfs/batcher.rs rename to crates/vfs-core/src/fs/vfs/batcher.rs index ec59e48b..c56a6530 100644 --- a/crates/agentfs-core/src/fs/agentfs/batcher.rs +++ b/crates/vfs-core/src/fs/vfs/batcher.rs @@ -70,16 +70,16 @@ pub(super) trait Drain: Send + Sync { #[derive(Clone)] pub(super) struct BatcherPendingView { - batcher: Arc, + batcher: Arc, } #[derive(Clone)] pub(super) struct BatcherDrain { - batcher: Arc, + batcher: Arc, } #[derive(Debug, Clone, Copy)] -pub(super) enum AgentFSWriteBatchDrainReason { +pub(super) enum VfsWriteBatchDrainReason { Timer, Bytes, Explicit, @@ -293,7 +293,7 @@ struct RetiredGeneration { } #[derive(Default)] -struct AgentFSWriteBatcherState { +struct VfsWriteBatcherState { pending: HashMap, /// Monotonic per-inode version for the pending overlay. Cache fills that /// read SQLite before a concurrent drain must compare the generation they @@ -324,7 +324,7 @@ struct AgentFSWriteBatcherState { drain_scheduled: bool, } -impl AgentFSWriteBatcherState { +impl VfsWriteBatcherState { fn generation(&self, ino: i64) -> PendingGeneration { PendingGeneration( self.pending @@ -352,7 +352,7 @@ impl AgentFSWriteBatcherState { self.last_generation = self .last_generation .checked_add(1) - .expect("AgentFS write batcher generation counter exhausted"); + .expect("Vfs write batcher generation counter exhausted"); self.last_generation } @@ -360,7 +360,7 @@ impl AgentFSWriteBatcherState { self.retired_generation_epoch = self .retired_generation_epoch .checked_add(1) - .expect("AgentFS write batcher retired-generation epoch exhausted"); + .expect("Vfs write batcher retired-generation epoch exhausted"); self.retired_generations.insert( ino, RetiredGeneration { @@ -413,7 +413,7 @@ impl AgentFSWriteBatcherState { /// The batcher stores only transient `WriteRange` values and drains them into /// the canonical SQLite tables. It never creates sidecars and normal durability /// boundaries (`flush`, `fsync`, `release`, `destroy`) explicitly drain it. -pub(super) struct AgentFSWriteBatcher { +pub(super) struct VfsWriteBatcher { pool: ConnectionPool, chunk_size: usize, inline_threshold: usize, @@ -422,10 +422,10 @@ pub(super) struct AgentFSWriteBatcher { batch_bytes: usize, batch_global_bytes: usize, /// Per-transaction inode-count bound for batched drains - /// (`AGENTFS_BATCH_TXN_INODES`). See `drain_pending_batched`. + /// (`VFS_BATCH_TXN_INODES`). See `drain_pending_batched`. txn_max_inodes: usize, /// Per-transaction pending-bytes bound for batched drains - /// (`AGENTFS_BATCH_TXN_BYTES`). See `drain_pending_batched`. + /// (`VFS_BATCH_TXN_BYTES`). See `drain_pending_batched`. txn_max_bytes: usize, /// Tier 4 mitigation: parking_lot `RwLock` so `peek_pending` / /// `peek_pending_max_end` can acquire read-only access without contending @@ -433,11 +433,11 @@ pub(super) struct AgentFSWriteBatcher { /// lock is safe inside async fns. Holding it across an await would block /// the tokio worker — `take_pending_locked` and friends always extract /// owned state under the lock and drop the guard before any I/O. - state: RwLock, + state: RwLock, commit_lock: AsyncMutex<()>, } -impl AgentFSWriteBatcher { +impl VfsWriteBatcher { pub(super) fn split(batcher: &Arc) -> (BatcherPendingView, BatcherDrain) { ( BatcherPendingView { @@ -466,7 +466,7 @@ impl AgentFSWriteBatcher { batch_global_bytes: config.global_bytes, txn_max_inodes: config.txn_max_inodes.max(1), txn_max_bytes: config.txn_max_bytes.max(1), - state: RwLock::new(AgentFSWriteBatcherState::default()), + state: RwLock::new(VfsWriteBatcherState::default()), commit_lock: AsyncMutex::new(()), } } @@ -498,8 +498,8 @@ impl AgentFSWriteBatcher { .or_insert_with(|| PendingInodeWrites::new(generation)); entry.push_ranges(ranges, byte_count)?; entry.bump_generation(generation); - crate::telemetry::record_agentfs_batcher_enqueue(); - crate::telemetry::record_agentfs_batcher_pending_bytes(entry.pending_bytes as u64); + crate::telemetry::record_vfs_batcher_enqueue(); + crate::telemetry::record_vfs_batcher_pending_bytes(entry.pending_bytes as u64); entry.pending_bytes >= self.batch_bytes }; @@ -546,20 +546,17 @@ impl AgentFSWriteBatcher { ) -> Result<()> { let outcome = self.queue(ino, ranges)?; if outcome.drain_all { - self.drain_all_reason(AgentFSWriteBatchDrainReason::Bytes) + self.drain_all_reason(VfsWriteBatchDrainReason::Bytes) .await?; } else if outcome.drain_inode { - self.drain_pending_batched(AgentFSWriteBatchDrainReason::Bytes, Some(ino)) + self.drain_pending_batched(VfsWriteBatchDrainReason::Bytes, Some(ino)) .await?; } Ok(()) } - async fn drain_all_reason( - self: &Arc, - reason: AgentFSWriteBatchDrainReason, - ) -> Result<()> { - // Always batch on full drain: destroy / finalize / public AgentFS::drain_all. + async fn drain_all_reason(self: &Arc, reason: VfsWriteBatchDrainReason) -> Result<()> { + // Always batch on full drain: destroy / finalize / public Vfs::drain_all. loop { self.drain_pending_batched(reason, None).await?; let still_pending = { @@ -578,7 +575,7 @@ impl AgentFSWriteBatcher { /// the same commit-then-remove discipline. /// /// One transaction is bounded by `txn_max_inodes` / `txn_max_bytes` - /// (`AGENTFS_BATCH_TXN_INODES` / `AGENTFS_BATCH_TXN_BYTES`); when the + /// (`VFS_BATCH_TXN_INODES` / `VFS_BATCH_TXN_BYTES`); when the /// pending map exceeds the bound the call commits a bounded subset and /// returns `Ok(true)` so the caller immediately drains again /// (back-to-back transactions) instead of building one unbounded txn. @@ -594,7 +591,7 @@ impl AgentFSWriteBatcher { /// per-transaction bounds. async fn drain_pending_batched( self: &Arc, - reason: AgentFSWriteBatchDrainReason, + reason: VfsWriteBatchDrainReason, required_ino: Option, ) -> Result { let _commit_guard = self.commit_lock.lock().await; @@ -669,21 +666,21 @@ impl AgentFSWriteBatcher { // so the ranges are simply retried on the next drain. let normalized = normalize_write_ranges(&range_refs)?; if !normalized.is_empty() { - crate::telemetry::record_agentfs_batcher_coalesced_ranges( + crate::telemetry::record_vfs_batcher_coalesced_ranges( ranges.len().saturating_sub(normalized.len()) as u64, ); // Per-inode drain accounting (one tick per inode whose DATA we // actually commit, matching the old reporting cardinality — // time-only commits are not counted as drains). match reason { - AgentFSWriteBatchDrainReason::Timer => { - crate::telemetry::record_agentfs_batcher_drain_timer(); + VfsWriteBatchDrainReason::Timer => { + crate::telemetry::record_vfs_batcher_drain_timer(); } - AgentFSWriteBatchDrainReason::Bytes => { - crate::telemetry::record_agentfs_batcher_drain_bytes(); + VfsWriteBatchDrainReason::Bytes => { + crate::telemetry::record_vfs_batcher_drain_bytes(); } - AgentFSWriteBatchDrainReason::Explicit => { - crate::telemetry::record_agentfs_batcher_drain_explicit(); + VfsWriteBatchDrainReason::Explicit => { + crate::telemetry::record_vfs_batcher_drain_explicit(); } } } @@ -768,7 +765,7 @@ impl AgentFSWriteBatcher { // instead of aborting the whole multi-inode batch. Err(Error::Fs(FsError::NotFound)) => { tracing::debug!( - "AgentFS write batcher: dropping pending writes for deleted inode {}", + "Vfs write batcher: dropping pending writes for deleted inode {}", ino ); inode_missing = true; @@ -822,8 +819,8 @@ impl AgentFSWriteBatcher { // pass. Arm it if it is not already running (e.g. explicit drains // triggered by fsync / kill-switch paths outside the scheduler). self.ensure_drain_scheduled(); - crate::telemetry::record_agentfs_batcher_commit_latency(started.elapsed()); - crate::telemetry::record_agentfs_batcher_commit_txn(to_commit.len() as u64); + crate::telemetry::record_vfs_batcher_commit_latency(started.elapsed()); + crate::telemetry::record_vfs_batcher_commit_txn(to_commit.len() as u64); Ok(more_pending) } @@ -862,10 +859,10 @@ impl AgentFSWriteBatcher { /// Instead of one timer task per written inode — which degenerated into a /// storm of small, serialized SQLite transactions during a git-clone burst /// — ONE task is armed when the first pending write arrives. Each cycle it - /// sleeps `AGENTFS_BATCH_MS` so concurrent writers coalesce, then commits + /// sleeps `VFS_BATCH_MS` so concurrent writers coalesce, then commits /// everything that is pending at that instant in as few `BEGIN IMMEDIATE` /// transactions as the per-transaction bounds allow - /// (`AGENTFS_BATCH_TXN_INODES` / `AGENTFS_BATCH_TXN_BYTES`), back-to-back. + /// (`VFS_BATCH_TXN_INODES` / `VFS_BATCH_TXN_BYTES`), back-to-back. /// Writes that arrive while a commit is in flight are picked up by the /// next cycle. The task exits only when nothing is pending; an enqueue /// that observes `drain_scheduled == false` arms a fresh one. Explicit @@ -881,7 +878,7 @@ impl AgentFSWriteBatcher { // and the pass is retried on the next cycle. loop { match self - .drain_pending_batched(AgentFSWriteBatchDrainReason::Timer, None) + .drain_pending_batched(VfsWriteBatchDrainReason::Timer, None) .await { Ok(true) => continue, @@ -889,7 +886,7 @@ impl AgentFSWriteBatcher { Err(error) => { tracing::warn!( ?error, - "AgentFS write batcher: scheduled group drain failed (will retry)" + "Vfs write batcher: scheduled group drain failed (will retry)" ); break; } @@ -987,7 +984,7 @@ impl AgentFSWriteBatcher { // ----- Tier Four: in-memory overlay read API ----- // - // These methods let `AgentFSFile::pread` / `getattr` / `truncate` consult + // These methods let `VfsFile::pread` / `getattr` / `truncate` consult // the batcher's pending state directly, instead of forcing a synchronous // SQLite drain for read-after-write consistency. The drain becomes a // pure durability operation, only triggered by explicit `fsync` / @@ -997,7 +994,7 @@ impl AgentFSWriteBatcher { /// Returned ranges are normalised (non-overlapping, sorted) and clipped /// to the requested window. The batcher's pending state is not modified. /// Callers merge the result over SQLite data with "pending wins" - /// semantics; see `AgentFSFile::pread`. + /// semantics; see `VfsFile::pread`. fn pending_ranges(&self, ino: i64, offset: u64, size: u64) -> Vec { if size == 0 { return Vec::new(); @@ -1168,7 +1165,7 @@ impl AgentFSWriteBatcher { } /// Drop any pending bytes beyond `new_size` and shrink ranges that span - /// the truncation boundary. Called by `AgentFSFile::truncate` so the + /// the truncation boundary. Called by `VfsFile::truncate` so the /// overlay agrees with the post-truncate file state without needing to /// drain first. pub(super) fn truncate_pending(&self, ino: i64, new_size: u64) { @@ -1297,27 +1294,27 @@ impl PendingView for BatcherPendingView { impl Drain for BatcherDrain { async fn drain_inode(&self, ino: i64) -> Result<()> { self.batcher - .drain_pending_batched(AgentFSWriteBatchDrainReason::Explicit, Some(ino)) + .drain_pending_batched(VfsWriteBatchDrainReason::Explicit, Some(ino)) .await?; Ok(()) } async fn drain_all(&self) -> Result<()> { self.batcher - .drain_all_reason(AgentFSWriteBatchDrainReason::Explicit) + .drain_all_reason(VfsWriteBatchDrainReason::Explicit) .await } async fn drain_inode_bytes(&self, ino: i64) -> Result<()> { self.batcher - .drain_pending_batched(AgentFSWriteBatchDrainReason::Bytes, Some(ino)) + .drain_pending_batched(VfsWriteBatchDrainReason::Bytes, Some(ino)) .await?; Ok(()) } async fn drain_all_bytes(&self) -> Result<()> { self.batcher - .drain_all_reason(AgentFSWriteBatchDrainReason::Bytes) + .drain_all_reason(VfsWriteBatchDrainReason::Bytes) .await } diff --git a/crates/agentfs-core/src/fs/agentfs/caches.rs b/crates/vfs-core/src/fs/vfs/caches.rs similarity index 97% rename from crates/agentfs-core/src/fs/agentfs/caches.rs rename to crates/vfs-core/src/fs/vfs/caches.rs index 6b1e9abf..fc110ea6 100644 --- a/crates/agentfs-core/src/fs/agentfs/caches.rs +++ b/crates/vfs-core/src/fs/vfs/caches.rs @@ -1,4 +1,4 @@ -//! Bounded dentry, negative-dentry, and attr caches for AgentFS. +//! Bounded dentry, negative-dentry, and attr caches for Vfs. //! //! The cache layer is intentionally synchronous and conservative: namespace //! and metadata mutations invalidate affected keys before reporting success, @@ -67,7 +67,7 @@ impl DentryCache { /// LRU cache for safe negative directory entry lookups. /// /// A negative entry means "this (parent, name) did not exist in the last -/// serialized AgentFS view". Every namespace mutation invalidates exactly the +/// serialized Vfs view". Every namespace mutation invalidates exactly the /// affected key before the mutation reports success, so cached ENOENT results /// cannot hide later creates or renames made through this filesystem. pub(super) struct NegativeDentryCache { diff --git a/crates/agentfs-core/src/fs/agentfs/file.rs b/crates/vfs-core/src/fs/vfs/file.rs similarity index 96% rename from crates/agentfs-core/src/fs/agentfs/file.rs rename to crates/vfs-core/src/fs/vfs/file.rs index 848ae748..e7c208f7 100644 --- a/crates/agentfs-core/src/fs/agentfs/file.rs +++ b/crates/vfs-core/src/fs/vfs/file.rs @@ -1,6 +1,6 @@ -//! Open-file handle implementation for AgentFS. +//! Open-file handle implementation for Vfs. //! -//! `AgentFSFile` owns per-handle read, write, truncate, fsync, and fstat +//! `VfsFile` owns per-handle read, write, truncate, fsync, and fstat //! behavior. It overlays pending batched writes onto committed SQLite state //! without forcing drains on read paths, preserving the noopen/writeback hot //! path. @@ -14,11 +14,11 @@ use super::batcher::EnqueueOutcome; use super::store::WriteRangeRef; use super::*; -/// An open file handle for AgentFS. +/// An open file handle for Vfs. /// /// This struct holds the inode number resolved at open time, allowing /// efficient read/write/fsync operations without path lookups. -pub struct AgentFSFile { +pub struct VfsFile { pub(super) pool: ConnectionPool, pub(super) ino: i64, pub(super) chunk_size: usize, @@ -26,7 +26,7 @@ pub struct AgentFSFile { pub(super) attr_cache: Arc, pub(super) pending_view: Option, pub(super) write_drain: Option, - /// Same semantics as the field on `AgentFS`; cloned at open time so the + /// Same semantics as the field on `Vfs`; cloned at open time so the /// hot read/write path doesn't have to chase an extra indirection. pub(super) overlay_reads: bool, /// Present for user-visible handles so unlink defers inode reaping while @@ -36,7 +36,7 @@ pub struct AgentFSFile { } #[async_trait] -impl File for AgentFSFile { +impl File for VfsFile { async fn pread(&self, offset: u64, size: u64) -> Result> { // Tier Four: NO `drain_writes()` prelude. Read SQLite-resident bytes // (committed state) and overlay pending writes from the in-memory @@ -114,7 +114,7 @@ impl File for AgentFSFile { // Tier Four: with the batcher wired AND overlay reads enabled, // route through enqueue so the overlay holds the write and readers // see it via `pread`'s peek_pending merge. Drain only on - // fsync/destroy/timer. When `AGENTFS_OVERLAY_READS=0` the + // fsync/destroy/timer. When `VFS_OVERLAY_READS=0` the // overlay-reads escape hatch is engaged: skip the batcher and commit // directly so the legacy Tier 3 read path (which drains before // reading) sees the write. @@ -289,7 +289,7 @@ impl File for AgentFSFile { } } -impl AgentFSFile { +impl VfsFile { async fn finish_enqueue(drain: &BatcherDrain, ino: i64, outcome: EnqueueOutcome) -> Result<()> { if outcome.drain_all { drain.drain_all_bytes().await diff --git a/crates/agentfs-core/src/fs/agentfs/fs.rs b/crates/vfs-core/src/fs/vfs/fs.rs similarity index 99% rename from crates/agentfs-core/src/fs/agentfs/fs.rs rename to crates/vfs-core/src/fs/vfs/fs.rs index 3c653876..60685dec 100644 --- a/crates/agentfs-core/src/fs/agentfs/fs.rs +++ b/crates/vfs-core/src/fs/vfs/fs.rs @@ -1,6 +1,6 @@ -//! Canonical `FileSystem` trait implementation for AgentFS. +//! Canonical `FileSystem` trait implementation for Vfs. //! -//! This module is the only AgentFS mutation implementation. Path helpers, CLI +//! This module is the only Vfs mutation implementation. Path helpers, CLI //! surfaces, FUSE, and NFS resolve into these inode-oriented operations so //! namespace, metadata, lifecycle, and batcher semantics cannot diverge. @@ -19,7 +19,7 @@ use super::batcher::PendingTimeChange; use super::*; #[async_trait] -impl FileSystem for AgentFS { +impl FileSystem for Vfs { async fn lookup(&self, parent_ino: i64, name: &str) -> Result> { crate::telemetry::record_lookup(); if name.len() > MAX_NAME_LEN { @@ -605,7 +605,7 @@ impl FileSystem for AgentFS { return Err(FsError::NotFound.into()); } - Ok(Arc::new(AgentFSFile { + Ok(Arc::new(VfsFile { pool: self.pool.clone(), ino, chunk_size: self.chunk_size, @@ -840,7 +840,7 @@ impl FileSystem for AgentFS { }; self.cache_attr(stats.clone()); - let file: BoxedFile = Arc::new(AgentFSFile { + let file: BoxedFile = Arc::new(VfsFile { pool: self.pool.clone(), ino, chunk_size: self.chunk_size, @@ -1557,23 +1557,23 @@ impl FileSystem for AgentFS { } async fn statfs(&self) -> Result { - AgentFS::statfs(self).await + Vfs::statfs(self).await } async fn drain_inode_writes(&self, ino: i64) -> Result<()> { - AgentFS::drain_inode_writes(self, ino).await + Vfs::drain_inode_writes(self, ino).await } async fn drain_all(&self) -> Result<()> { - AgentFS::drain_all(self).await + Vfs::drain_all(self).await } async fn finalize(&self) -> Result<()> { - AgentFS::finalize(self).await + Vfs::finalize(self).await } fn register_reap_hook(&self, hook: Arc) -> bool { - AgentFS::register_reap_hook(self, hook) + Vfs::register_reap_hook(self, hook) } // `forget` deliberately uses the default no-op trait impl: a FORGET only diff --git a/crates/agentfs-core/src/fs/agentfs/import.rs b/crates/vfs-core/src/fs/vfs/import.rs similarity index 97% rename from crates/agentfs-core/src/fs/agentfs/import.rs rename to crates/vfs-core/src/fs/vfs/import.rs index c517e36d..e5f0c6b3 100644 --- a/crates/agentfs-core/src/fs/agentfs/import.rs +++ b/crates/vfs-core/src/fs/vfs/import.rs @@ -1,4 +1,4 @@ -//! Bulk import session for AgentFS clone and import flows. +//! Bulk import session for Vfs clone and import flows. //! //! Import keeps one pooled connection and a directory inode map across chunks, //! committing entries in bounded transactions sized by the batcher config. @@ -44,14 +44,14 @@ pub struct ImportOptions { pub timestamp: (i64, i64), } -/// A streaming bulk import started by [`AgentFS::begin_import`]. Holds one +/// A streaming bulk import started by [`Vfs::begin_import`]. Holds one /// pooled connection plus the directory-path -> ino map across /// [`ImportSession::import_chunk`] calls, so a producer can feed entries as /// they become available (e.g. as `git cat-file --batch` emits blobs) /// instead of buffering the whole tree in memory. Every parent directory must /// appear in an earlier chunk or before its children in the same chunk. pub struct ImportSession { - fs: AgentFS, + fs: Vfs, conn: crate::pool::PooledConnection, dest_parent: i64, opts: ImportOptions, @@ -84,7 +84,7 @@ impl ImportSession { } } -impl AgentFS { +impl Vfs { #[cfg(test)] pub(super) async fn import_entries( &self, @@ -301,7 +301,7 @@ impl AgentFS { txn.commit().await?; #[cfg(test)] self.import_commit_sizes.lock().unwrap().push(staged.len()); - crate::telemetry::record_agentfs_batcher_commit_txn(staged.len() as u64); + crate::telemetry::record_vfs_batcher_commit_txn(staged.len() as u64); for (parent_ino, name, stats) in staged { self.cache_dentry(parent_ino, &name, stats.ino); diff --git a/crates/agentfs-core/src/fs/agentfs/lifecycle.rs b/crates/vfs-core/src/fs/vfs/lifecycle.rs similarity index 98% rename from crates/agentfs-core/src/fs/agentfs/lifecycle.rs rename to crates/vfs-core/src/fs/vfs/lifecycle.rs index 0ce60284..1b34b698 100644 --- a/crates/agentfs-core/src/fs/agentfs/lifecycle.rs +++ b/crates/vfs-core/src/fs/vfs/lifecycle.rs @@ -174,7 +174,7 @@ impl Lifecycle { } } -/// Tracks inodes with live `AgentFSFile` handles so unlink and +/// Tracks inodes with live `VfsFile` handles so unlink and /// rename-replace can defer row deletion: POSIX requires an /// unlinked-but-open file to stay readable and writable until its last /// handle closes. @@ -239,7 +239,7 @@ impl OpenInodes { } } -/// RAII registration of one `AgentFSFile` in [`OpenInodes`]. +/// RAII registration of one `VfsFile` in [`OpenInodes`]. pub(crate) struct OpenInodeGuard { registry: Arc, ino: i64, diff --git a/crates/agentfs-core/src/fs/agentfs/mod.rs b/crates/vfs-core/src/fs/vfs/mod.rs similarity index 96% rename from crates/agentfs-core/src/fs/agentfs/mod.rs rename to crates/vfs-core/src/fs/vfs/mod.rs index a82a06ca..65c9acef 100644 --- a/crates/agentfs-core/src/fs/agentfs/mod.rs +++ b/crates/vfs-core/src/fs/vfs/mod.rs @@ -1,6 +1,6 @@ -//! AgentFS core facade and module spine. +//! Vfs core facade and module spine. //! -//! This module owns the shared `AgentFS` state, connection setup, path +//! This module owns the shared `Vfs` state, connection setup, path //! resolution helpers, cache invalidation hooks, and lifecycle spine. Focused //! child modules implement caches, file handles, bulk import, path delegates, //! and the canonical `FileSystem` trait implementation. @@ -37,10 +37,10 @@ mod path_api; pub(in crate::fs) mod store; use batcher::{ - AgentFSWriteBatcher, BatcherDrain, BatcherPendingView, Drain, PendingGeneration, PendingView, + BatcherDrain, BatcherPendingView, Drain, PendingGeneration, PendingView, VfsWriteBatcher, }; use caches::{AttrCache, DentryCache, NegativeDentryCache}; -pub use file::AgentFSFile; +pub use file::VfsFile; pub use import::{ImportEntry, ImportOptions, ImportSession, ImportedEntry}; pub use lifecycle::ReapHook; use lifecycle::{Lifecycle, OpenInodeGuard}; @@ -71,7 +71,7 @@ const FILE_BACKED_SETUP_SQL: &[&str] = &[ const MEMORY_SETUP_SQL: &[&str] = &[TEMP_STORE_MEMORY_SQL]; const ATTR_CACHE_MAX_SIZE: usize = 10000; -/// Production connection-pool options for local file-backed AgentFS databases. +/// Production connection-pool options for local file-backed Vfs databases. pub(crate) fn file_backed_connection_pool_options() -> PoolOptions { PoolOptions { max_connections: FILE_BACKED_MAX_CONNECTIONS, @@ -79,7 +79,7 @@ pub(crate) fn file_backed_connection_pool_options() -> PoolOptions { } } -/// Production connection-pool options for local in-memory AgentFS databases. +/// Production connection-pool options for local in-memory Vfs databases. pub(crate) fn memory_connection_pool_options() -> PoolOptions { PoolOptions::single_connection().with_setup_sql(MEMORY_SETUP_SQL.iter().copied()) } @@ -113,7 +113,7 @@ fn remove_checkpointed_sidecars(path: &Path) -> Result<()> { /// A filesystem backed by SQLite #[derive(Clone)] -pub struct AgentFS { +pub struct Vfs { pool: ConnectionPool, db_path: Option>, chunk_size: usize, @@ -131,11 +131,11 @@ pub struct AgentFS { write_drain: Option, /// Concrete batcher retained only for white-box unit tests. #[cfg(test)] - write_batcher: Option>, + write_batcher: Option>, /// Bulk-import transaction sizes observed by white-box tests. #[cfg(test)] import_commit_sizes: Arc>>, - /// Tier 4 escape hatch: when false (`AGENTFS_OVERLAY_READS=0`), the SDK + /// Tier 4 escape hatch: when false (`VFS_OVERLAY_READS=0`), the SDK /// behaves like Tier 3 — every pwrite drains, every pread drains, /// `merge_pending_view` is a no-op. ON by default. overlay_reads: bool, @@ -150,7 +150,7 @@ fn current_timestamp() -> Result<(i64, i64)> { Ok((dur.as_secs() as i64, dur.subsec_nanos() as i64)) } -impl AgentFS { +impl Vfs { /// Create a new filesystem pub async fn new(db_path: &str) -> Result { let db = Builder::new_local(db_path).build().await?; @@ -215,21 +215,21 @@ impl AgentFS { let attr_cache = Arc::new(AttrCache::new(ATTR_CACHE_MAX_SIZE)); // Tier Three Axis D: default the write batcher to ON. CLI callers pass - // the FUSE writeback decision through AgentFSOptions, while SDK callers + // the FUSE writeback decision through VfsOptions, while SDK callers // can supply CoreConfig directly. let (pending_view, write_drain, _write_batcher) = if core_config.batcher.enabled { let invalidate = { let attr_cache = Arc::clone(&attr_cache); Arc::new(move |ino| attr_cache.remove(ino)) as batcher::Invalidate }; - let batcher = Arc::new(AgentFSWriteBatcher::from_config( + let batcher = Arc::new(VfsWriteBatcher::from_config( pool.clone(), chunk_size, inline_threshold, invalidate, &core_config.batcher, )); - let (pending_view, write_drain) = AgentFSWriteBatcher::split(&batcher); + let (pending_view, write_drain) = VfsWriteBatcher::split(&batcher); (Some(pending_view), Some(write_drain), Some(batcher)) } else { (None, None, None) @@ -560,7 +560,7 @@ impl AgentFS { /// read from SQLite, so callers that hold a pool connection don't need to /// drain (which would deadlock on single-conn pools): /// - `size` is OR-ed with the pending max write end (mirrors the logic in - /// `AgentFS::getattr` and `AgentFSFile::pread`); + /// `Vfs::getattr` and `VfsFile::pread`); /// - explicitly-set times stashed by `utimens` (`PendingTimeChange`) are /// overlaid so a deferred SETATTR is visible before its drain commits. /// @@ -572,7 +572,7 @@ impl AgentFS { }; // Escape hatch: when overlay reads are disabled, callers' SQLite // size view is already authoritative because pwrites went straight - // to SQLite (see AgentFSFile::pwrite) and utimens never stashes. + // to SQLite (see VfsFile::pwrite) and utimens never stashes. // No merge needed. if !self.overlay_reads { return; @@ -583,7 +583,7 @@ impl AgentFS { view.merge_into_stats(ino, stats); } - /// Drain all pending batched writes for this AgentFS instance. + /// Drain all pending batched writes for this Vfs instance. pub async fn drain_all(&self) -> Result<()> { if let Some(drain) = &self.write_drain { drain.drain_all().await?; @@ -892,7 +892,7 @@ impl AgentFS { let path = self.normalize_path(path); let ino = self.resolve_path(&path).await?.ok_or(FsError::NotFound)?; - Ok(Arc::new(AgentFSFile { + Ok(Arc::new(VfsFile { pool: self.pool.clone(), ino, chunk_size: self.chunk_size, @@ -960,4 +960,4 @@ impl AgentFS { // Keep the extracted test body byte-for-byte; this feature is a pure move. #[rustfmt::skip] #[path = "tests.rs"] -mod agentfs_tests; +mod vfs_tests; diff --git a/crates/agentfs-core/src/fs/agentfs/path_api.rs b/crates/vfs-core/src/fs/vfs/path_api.rs similarity index 98% rename from crates/agentfs-core/src/fs/agentfs/path_api.rs rename to crates/vfs-core/src/fs/vfs/path_api.rs index cd794d0b..9b778472 100644 --- a/crates/agentfs-core/src/fs/agentfs/path_api.rs +++ b/crates/vfs-core/src/fs/vfs/path_api.rs @@ -1,4 +1,4 @@ -//! Path-based AgentFS convenience delegates. +//! Path-based Vfs convenience delegates. //! //! These are the six live path helpers retained after M2. Each resolves a //! path to the canonical inode-oriented `FileSystem` trait operation so @@ -10,7 +10,7 @@ use crate::fs::FileSystem; use super::*; -impl AgentFS { +impl Vfs { /// Get file statistics, following symlinks. pub async fn stat(&self, path: &str) -> Result> { let path = self.normalize_path(path); diff --git a/crates/agentfs-core/src/fs/agentfs/store.rs b/crates/vfs-core/src/fs/vfs/store.rs similarity index 99% rename from crates/agentfs-core/src/fs/agentfs/store.rs rename to crates/vfs-core/src/fs/vfs/store.rs index 52b72824..f2069cfd 100644 --- a/crates/agentfs-core/src/fs/agentfs/store.rs +++ b/crates/vfs-core/src/fs/vfs/store.rs @@ -184,7 +184,7 @@ pub(super) async fn file_storage(conn: &Connection, ino: i64) -> Result Result<(AgentFS, tempfile::TempDir)> { + async fn create_test_fs() -> Result<(Vfs, tempfile::TempDir)> { create_test_fs_with_config(CoreConfig::from_env()).await } async fn create_test_fs_with_config( config: CoreConfig, - ) -> Result<(AgentFS, tempfile::TempDir)> { + ) -> Result<(Vfs, tempfile::TempDir)> { let dir = tempdir()?; let db_path = dir.path().join("test.db"); let db = Builder::new_local(db_path.to_str().unwrap()) @@ -19,7 +19,7 @@ .await?; let pool = ConnectionPool::with_options(DatabaseType::Local(db), file_backed_connection_pool_options()); - let fs = AgentFS::from_pool_with_path_and_config(pool, Some(db_path), config).await?; + let fs = Vfs::from_pool_with_path_and_config(pool, Some(db_path), config).await?; Ok((fs, dir)) } @@ -38,24 +38,24 @@ let mut disabled = CoreConfig::default(); disabled.batcher.enabled = false; - let disabled_agent = crate::AgentFS::open( - crate::AgentFSOptions::with_path(dir.path().join("disabled.db").to_string_lossy()) + let disabled_agent = crate::Vfs::open( + crate::VfsOptions::with_path(dir.path().join("disabled.db").to_string_lossy()) .with_core_config(disabled), ) .await?; assert!( disabled_agent.fs.write_batcher.is_none(), - "AgentFSOptions CoreConfig should be able to disable the write batcher" + "VfsOptions CoreConfig should be able to disable the write batcher" ); - let enabled_agent = crate::AgentFS::open( - crate::AgentFSOptions::with_path(dir.path().join("enabled.db").to_string_lossy()) + let enabled_agent = crate::Vfs::open( + crate::VfsOptions::with_path(dir.path().join("enabled.db").to_string_lossy()) .with_core_config(test_config_with_long_batch_window()), ) .await?; assert!( enabled_agent.fs.write_batcher.is_some(), - "AgentFSOptions CoreConfig should be able to enable the write batcher" + "VfsOptions CoreConfig should be able to enable the write batcher" ); Ok(()) @@ -85,15 +85,15 @@ Ok(rows.next().await?.is_some()) } - fn cached_attr(fs: &AgentFS, ino: i64) -> Option { + fn cached_attr(fs: &Vfs, ino: i64) -> Option { fs.attr_cache.get(ino) } - fn negative_cached(fs: &AgentFS, parent_ino: i64, name: &str) -> bool { + fn negative_cached(fs: &Vfs, parent_ino: i64, name: &str) -> bool { fs.negative_dentry_cache.contains(parent_ino, name) } - async fn parent_and_name_for_test(fs: &AgentFS, path: &str) -> Result<(i64, String)> { + async fn parent_and_name_for_test(fs: &Vfs, path: &str) -> Result<(i64, String)> { let path = fs.normalize_path(path); let components = fs.split_path(&path); if components.is_empty() { @@ -111,7 +111,7 @@ Ok((parent_ino, components.last().unwrap().clone())) } - async fn rename_path_via_trait(fs: &AgentFS, from: &str, to: &str) -> Result<()> { + async fn rename_path_via_trait(fs: &Vfs, from: &str, to: &str) -> Result<()> { let (oldparent_ino, oldname) = parent_and_name_for_test(fs, from).await?; let (newparent_ino, newname) = parent_and_name_for_test(fs, to).await?; FileSystem::rename(fs, oldparent_ino, &oldname, newparent_ino, &newname).await @@ -1649,7 +1649,7 @@ .await?; } - let err = match AgentFS::new(db_path.to_str().unwrap()).await { + let err = match Vfs::new(db_path.to_str().unwrap()).await { Ok(_) => panic!("opening a database with a conflicting schema column must fail"), Err(err) => err, }; @@ -1683,10 +1683,10 @@ let dir = tempdir()?; let db_path = dir.path().join("already-upgraded.db"); - let first = AgentFS::new(db_path.to_str().unwrap()).await?; + let first = Vfs::new(db_path.to_str().unwrap()).await?; drop(first); - let reopened = AgentFS::new(db_path.to_str().unwrap()).await?; + let reopened = Vfs::new(db_path.to_str().unwrap()).await?; let conn = reopened.pool.get_connection().await?; for column_name in [ @@ -1714,7 +1714,7 @@ .await? .expect("schema version config should exist") .get(0)?; - assert_eq!(version, schema::AGENTFS_SCHEMA_VERSION); + assert_eq!(version, schema::VFS_SCHEMA_VERSION); Ok(()) } @@ -1761,7 +1761,7 @@ } let err = - match crate::AgentFS::open(crate::AgentFSOptions::with_path(db_path.to_string_lossy())) + match crate::Vfs::open(crate::VfsOptions::with_path(db_path.to_string_lossy())) .await { Ok(_) => panic!("open must not upgrade a v0.4 schema"), @@ -1782,7 +1782,7 @@ } let agent = - crate::AgentFS::open(crate::AgentFSOptions::with_path(db_path.to_string_lossy())) + crate::Vfs::open(crate::VfsOptions::with_path(db_path.to_string_lossy())) .await?; let conn = agent.get_connection().await?; assert_eq!( @@ -1837,22 +1837,22 @@ } #[tokio::test] - async fn test_memory_agentfs_connections_use_temp_store_memory() -> Result<()> { - let agentfs = crate::AgentFS::open(crate::AgentFSOptions::ephemeral()).await?; + async fn test_memory_vfs_connections_use_temp_store_memory() -> Result<()> { + let vfs = crate::Vfs::open(crate::VfsOptions::ephemeral()).await?; - let conn = agentfs.get_connection().await?; + let conn = vfs.get_connection().await?; assert_eq!(read_pragma_i64(&conn, "PRAGMA temp_store").await, 2); drop(conn); - let core_agentfs = AgentFS::new(":memory:").await?; - let core_conn = core_agentfs.pool.get_connection().await?; + let core_vfs = Vfs::new(":memory:").await?; + let core_conn = core_vfs.pool.get_connection().await?; assert_eq!(read_pragma_i64(&core_conn, "PRAGMA temp_store").await, 2); Ok(()) } #[tokio::test] - async fn test_file_backed_agentfs_concurrent_operations_complete() -> Result<()> { + async fn test_file_backed_vfs_concurrent_operations_complete() -> Result<()> { let (fs, _dir) = create_test_fs().await?; let (_, file) = fs.create_file("/seed.txt", DEFAULT_FILE_MODE, 0, 0).await?; file.pwrite(0, b"seed").await?; @@ -2035,7 +2035,7 @@ Ok(()) } - async fn count_rows(fs: &AgentFS, table: &str, ino: i64) -> Result { + async fn count_rows(fs: &Vfs, table: &str, ino: i64) -> Result { let conn = fs.pool.get_connection().await?; let mut rows = conn .query( @@ -2098,7 +2098,7 @@ } struct PendingStateReapHook { - batcher: Arc, + batcher: Arc, saw_pending: std::sync::atomic::AtomicBool, } @@ -2113,7 +2113,7 @@ } } - async fn count_probe_rows(fs: &AgentFS, ino: i64) -> Result { + async fn count_probe_rows(fs: &Vfs, ino: i64) -> Result { let conn = fs.pool.get_connection().await?; let mut rows = conn .query("SELECT COUNT(*) FROM reap_hook_probe WHERE ino = ?", (ino,)) @@ -2366,7 +2366,7 @@ let db_path = db_path.to_str().unwrap(); let ino = { - let fs = AgentFS::new(db_path).await?; + let fs = Vfs::new(db_path).await?; let (stats, file) = fs .create_file("/ghost.bin", DEFAULT_FILE_MODE, 0, 0) .await?; @@ -2379,7 +2379,7 @@ stats.ino }; - let fs = AgentFS::new(db_path).await?; + let fs = Vfs::new(db_path).await?; assert_eq!(count_rows(&fs, "fs_inode", ino).await?, 0); assert_eq!(count_rows(&fs, "fs_data", ino).await?, 0); @@ -2393,7 +2393,7 @@ let db_path = db_path.to_str().unwrap(); let deferred_ino = { - let fs = AgentFS::new(db_path).await?; + let fs = Vfs::new(db_path).await?; let (stats, file) = fs .create_file("/deferred.bin", DEFAULT_FILE_MODE, 0, 0) .await?; @@ -2408,7 +2408,7 @@ }; let crashed_ino = { - let fs = AgentFS::new(db_path).await?; + let fs = Vfs::new(db_path).await?; let (stats, file) = fs .create_file("/crashed.bin", DEFAULT_FILE_MODE, 0, 0) .await?; @@ -2419,7 +2419,7 @@ stats.ino }; - let fs = AgentFS::new(db_path).await?; + let fs = Vfs::new(db_path).await?; for ino in [deferred_ino, crashed_ino] { assert_eq!( count_rows(&fs, "fs_inode", ino).await?, @@ -2965,21 +2965,21 @@ } // Build a batcher with an explicit config so the test is independent of the - // process-global AGENTFS_BATCH_* env vars (which other tests mutate + // process-global VFS_BATCH_* env vars (which other tests mutate // concurrently). Reuses `fs`'s pool/attr cache so commits hit real inodes. fn test_batcher( - fs: &AgentFS, + fs: &Vfs, batch_ms_secs: u64, batch_bytes: usize, batch_global_bytes: usize, - ) -> Arc { + ) -> Arc { let config = BatcherConfig { window: std::time::Duration::from_secs(batch_ms_secs), inode_bytes: batch_bytes, global_bytes: batch_global_bytes, ..BatcherConfig::default() }; - Arc::new(AgentFSWriteBatcher::from_config( + Arc::new(VfsWriteBatcher::from_config( fs.pool.clone(), fs.chunk_size, fs.inline_threshold, @@ -3639,7 +3639,7 @@ let mut stale_stats = store::getattr(&conn, created.ino) .await? .expect("file should exist"); - self::AgentFS::merge_pending_view(&fs, created.ino, Some(&mut stale_stats)); + self::Vfs::merge_pending_view(&fs, created.ino, Some(&mut stale_stats)); drop(conn); file.drain_writes().await?; @@ -3672,7 +3672,7 @@ let mut stale_stats = store::getattr(&conn, created.ino) .await? .expect("file should exist"); - self::AgentFS::merge_pending_view(&fs, created.ino, Some(&mut stale_stats)); + self::Vfs::merge_pending_view(&fs, created.ino, Some(&mut stale_stats)); drop(conn); file.drain_writes().await?; @@ -3930,7 +3930,7 @@ } /// Spec acceptance criterion for Tier 4: - /// "`agentfs_batcher_drains_explicit / agentfs_batcher_enqueues` ratio + /// "`vfs_batcher_drains_explicit / vfs_batcher_enqueues` ratio /// drops to <0.2 (vs ~1.0 today) — confirms read path no longer triggers /// Explicit drains." /// @@ -3946,8 +3946,8 @@ .await?; let pre = crate::telemetry::snapshot(); - let pre_enq = pre.counter("agentfs_batcher_enqueues"); - let pre_explicit = pre.counter("agentfs_batcher_drains_explicit"); + let pre_enq = pre.counter("vfs_batcher_enqueues"); + let pre_explicit = pre.counter("vfs_batcher_drains_explicit"); // 200 write-then-read cycles, no intervening fsync. Tier 3 would // drain Explicit on every read; Tier 4 must not. @@ -3957,8 +3957,8 @@ } let post = crate::telemetry::snapshot(); - let enq = post.counter("agentfs_batcher_enqueues") - pre_enq; - let explicit = post.counter("agentfs_batcher_drains_explicit") - pre_explicit; + let enq = post.counter("vfs_batcher_enqueues") - pre_enq; + let explicit = post.counter("vfs_batcher_drains_explicit") - pre_explicit; assert!(enq >= 200, "expected ≥200 enqueues, got {enq}"); let ratio = explicit as f64 / enq.max(1) as f64; assert!( diff --git a/crates/agentfs-core/src/kv.rs b/crates/vfs-core/src/kv.rs similarity index 96% rename from crates/agentfs-core/src/kv.rs rename to crates/vfs-core/src/kv.rs index 4162776f..9a4e397b 100644 --- a/crates/agentfs-core/src/kv.rs +++ b/crates/vfs-core/src/kv.rs @@ -18,9 +18,9 @@ impl KvStore { pub(crate) async fn new(db_path: &str) -> Result { let db = Builder::new_local(db_path).build().await?; let options = if db_path == ":memory:" { - crate::fs::agentfs::memory_connection_pool_options() + crate::fs::vfs::memory_connection_pool_options() } else { - crate::fs::agentfs::file_backed_connection_pool_options() + crate::fs::vfs::file_backed_connection_pool_options() }; let pool = ConnectionPool::with_options(DatabaseType::Local(db), options); let kv = Self { pool }; diff --git a/crates/agentfs-core/src/lib.rs b/crates/vfs-core/src/lib.rs similarity index 82% rename from crates/agentfs-core/src/lib.rs rename to crates/vfs-core/src/lib.rs index 4bc8afbc..7d5bbb0b 100644 --- a/crates/agentfs-core/src/lib.rs +++ b/crates/vfs-core/src/lib.rs @@ -1,4 +1,4 @@ -//! AgentFS core: the SQLite-backed virtual filesystem engine. +//! Vfs core: the SQLite-backed virtual filesystem engine. //! //! This is the only externally consumed crate. It owns the storage engine //! (chunk/inline layout per docs/SPEC.md), the write batcher, inode @@ -11,7 +11,7 @@ //! //! Owned invariants: //! -//! - All virtual filesystem state lives in the single AgentFS SQLite +//! - All virtual filesystem state lives in the single Vfs SQLite //! database. Sandboxed writes never touch the host filesystem; overlay //! reads are scoped to the configured read-only base directory. //! - Buffered (volatile-ack) writes are acceleration state only: durable @@ -64,43 +64,43 @@ pub use fs::{ }; pub use kv::KvStore; pub use mounts::{get_mounts, Mount}; -pub use options::{agentfs_dir, AgentFSOptions, EncryptionConfig, SyncOptions}; -pub use schema::{SchemaVersion, AGENTFS_SCHEMA_VERSION, CURRENT}; +pub use options::{vfs_dir, EncryptionConfig, SyncOptions, VfsOptions}; +pub use schema::{SchemaVersion, CURRENT, VFS_SCHEMA_VERSION}; pub use semantics::{AckDurability, Semantics, WriteReceipt}; pub use toolcalls::{ToolCall, ToolCallStats, ToolCallStatus, ToolCalls}; -/// The main AgentFS SDK struct +/// The main Vfs SDK struct /// /// This provides a unified interface to the filesystem, key-value store, /// and tool calls tracking backed by a SQLite database. -pub struct AgentFS { +pub struct Vfs { pool: ConnectionPool, sync_db: Option, pub kv: KvStore, - pub fs: fs::AgentFS, + pub fs: fs::Vfs, pub tools: ToolCalls, } -impl AgentFS { - /// Open an AgentFS instance +impl Vfs { + /// Open a Vfs instance /// /// # Arguments /// * `options` - Configuration options (use Default::default() for ephemeral) /// /// # Examples /// ```no_run - /// use agentfs_core::{AgentFS, AgentFSOptions}; + /// use vfs_core::{Vfs, VfsOptions}; /// - /// # async fn example() -> agentfs_core::error::Result<()> { + /// # async fn example() -> vfs_core::error::Result<()> { /// // Persistent storage - /// let agent = AgentFS::open(AgentFSOptions::with_id("my-agent")).await?; + /// let agent = Vfs::open(VfsOptions::with_id("my-agent")).await?; /// /// // Ephemeral in-memory - /// let agent = AgentFS::open(AgentFSOptions::ephemeral()).await?; + /// let agent = Vfs::open(VfsOptions::ephemeral()).await?; /// # Ok(()) /// # } /// ``` - pub async fn open(options: AgentFSOptions) -> Result { + pub async fn open(options: VfsOptions) -> Result { let core_config = options .core_config .clone() @@ -173,12 +173,12 @@ impl AgentFS { let pool = if db_path == ":memory:" { ConnectionPool::with_options( DatabaseType::Local(db), - fs::agentfs::memory_connection_pool_options(), + fs::vfs::memory_connection_pool_options(), ) } else { ConnectionPool::with_options( DatabaseType::Local(db), - fs::agentfs::file_backed_connection_pool_options(), + fs::vfs::file_backed_connection_pool_options(), ) }; (None, pool) @@ -186,7 +186,7 @@ impl AgentFS { // Initialize or normalize schema for existing databases before any // schema-owned callers read or write sidecar sections. Old schema - // versions are rejected here; upgrades are `agentfs migrate`'s job. + // versions are rejected here; upgrades are `vfs migrate`'s job. let mut conn = pool.get_connection().await?; if let Err(error) = schema::require_current(&conn).await { conn.mark_unhealthy_if_fatal(&error); @@ -224,7 +224,7 @@ impl AgentFS { ) -> Result { let reap_hooks = Self::overlay_reap_hooks_for_pool(&pool, false).await?; let kv = KvStore::from_pool(pool.clone()).await?; - let fs = fs::AgentFS::from_pool_with_path_config_and_reap_hooks( + let fs = fs::Vfs::from_pool_with_path_config_and_reap_hooks( pool.clone(), None, core_config, @@ -247,10 +247,10 @@ impl AgentFS { sync_db: Option, db_path: Option, core_config: CoreConfig, - reap_hooks: Vec>, + reap_hooks: Vec>, ) -> Result { let kv = KvStore::from_pool(pool.clone()).await?; - let fs = fs::AgentFS::from_pool_with_path_config_and_reap_hooks( + let fs = fs::Vfs::from_pool_with_path_config_and_reap_hooks( pool.clone(), db_path, core_config, @@ -271,7 +271,7 @@ impl AgentFS { async fn overlay_reap_hooks_for_pool( pool: &ConnectionPool, force_overlay_hook: bool, - ) -> Result>> { + ) -> Result>> { if force_overlay_hook || Self::overlay_config_exists(pool).await? { Ok(vec![fs::OverlayFS::sidecar_reap_hook()]) } else { @@ -300,25 +300,22 @@ impl AgentFS { Ok(rows.next().await?.is_some()) } - /// Create a new AgentFS instance (deprecated, use `open` instead) + /// Create a new Vfs instance (deprecated, use `open` instead) /// /// # Arguments /// * `db_path` - Path to the SQLite database file (use ":memory:" for in-memory database) - #[deprecated( - since = "0.2.0", - note = "Use AgentFS::open with AgentFSOptions instead" - )] + #[deprecated(since = "0.2.0", note = "Use Vfs::open with VfsOptions instead")] pub async fn new(db_path: &str) -> Result { let db = Builder::new_local(db_path).build().await?; let pool = if db_path == ":memory:" { ConnectionPool::with_options( DatabaseType::Local(db), - fs::agentfs::memory_connection_pool_options(), + fs::vfs::memory_connection_pool_options(), ) } else { ConnectionPool::with_options( DatabaseType::Local(db), - fs::agentfs::file_backed_connection_pool_options(), + fs::vfs::file_backed_connection_pool_options(), ) }; Self::build_from_pool_and_config(pool, None, CoreConfig::from_env()).await @@ -564,63 +561,61 @@ mod tests { use super::*; #[tokio::test] - async fn test_agentfs_creation() { - let agentfs = AgentFS::open(AgentFSOptions::ephemeral()).await.unwrap(); + async fn test_vfs_creation() { + let vfs = Vfs::open(VfsOptions::ephemeral()).await.unwrap(); // Just verify we can get the connection - let _conn = agentfs.get_connection().await.unwrap(); + let _conn = vfs.get_connection().await.unwrap(); } #[tokio::test] - async fn test_agentfs_with_id() { - let agentfs = AgentFS::open(AgentFSOptions::with_id("test-agent")) - .await - .unwrap(); + async fn test_vfs_with_id() { + let vfs = Vfs::open(VfsOptions::with_id("test-agent")).await.unwrap(); // Just verify we can get the connection - let _conn = agentfs.get_connection().await.unwrap(); + let _conn = vfs.get_connection().await.unwrap(); // Cleanup - let agentfs_dir = agentfs_dir(); + let vfs_dir = vfs_dir(); let file_names = ["test-agent.db", "test-agent.db-shm", "test-agent.db-wal"]; for file_name in file_names { - let _ = std::fs::remove_file(agentfs_dir.join(file_name)); + let _ = std::fs::remove_file(vfs_dir.join(file_name)); } } #[tokio::test] async fn test_kv_operations() { - let agentfs = AgentFS::open(AgentFSOptions::ephemeral()).await.unwrap(); + let vfs = Vfs::open(VfsOptions::ephemeral()).await.unwrap(); // Set a value - agentfs.kv.set("test_key", &"test_value").await.unwrap(); + vfs.kv.set("test_key", &"test_value").await.unwrap(); // Get the value - let value: Option = agentfs.kv.get("test_key").await.unwrap(); + let value: Option = vfs.kv.get("test_key").await.unwrap(); assert_eq!(value, Some("test_value".to_string())); // Delete the value - agentfs.kv.delete("test_key").await.unwrap(); + vfs.kv.delete("test_key").await.unwrap(); // Verify deletion - let value: Option = agentfs.kv.get("test_key").await.unwrap(); + let value: Option = vfs.kv.get("test_key").await.unwrap(); assert_eq!(value, None); } #[tokio::test] async fn test_filesystem_operations() { - let agentfs = AgentFS::open(AgentFSOptions::ephemeral()).await.unwrap(); + let vfs = Vfs::open(VfsOptions::ephemeral()).await.unwrap(); // Create a directory - agentfs.fs.mkdir("/test_dir", 0, 0).await.unwrap(); + vfs.fs.mkdir("/test_dir", 0, 0).await.unwrap(); // Check directory exists - let stats = agentfs.fs.stat("/test_dir").await.unwrap(); + let stats = vfs.fs.stat("/test_dir").await.unwrap(); assert!(stats.is_some()); let dir_stats = stats.unwrap(); assert!(dir_stats.is_directory()); // Write a file - let data = b"Hello, AgentFS!"; - let (_, file) = agentfs + let data = b"Hello, Vfs!"; + let (_, file) = vfs .fs .create_file("/test_dir/test.txt", DEFAULT_FILE_MODE, 0, 0) .await @@ -628,7 +623,7 @@ mod tests { file.pwrite(0, data).await.unwrap(); // Read the file - let read_data = agentfs + let read_data = vfs .fs .read_file("/test_dir/test.txt") .await @@ -637,42 +632,41 @@ mod tests { assert_eq!(read_data, data); // List directory - let entries = agentfs.fs.readdir(dir_stats.ino).await.unwrap().unwrap(); + let entries = vfs.fs.readdir(dir_stats.ino).await.unwrap().unwrap(); assert_eq!(entries, vec!["test.txt"]); } #[tokio::test] async fn test_tool_calls() { - let agentfs = AgentFS::open(AgentFSOptions::ephemeral()).await.unwrap(); + let vfs = Vfs::open(VfsOptions::ephemeral()).await.unwrap(); // Start a tool call - let id = agentfs + let id = vfs .tools .start("test_tool", Some(serde_json::json!({"param": "value"}))) .await .unwrap(); // Mark it as successful - agentfs - .tools + vfs.tools .success(id, Some(serde_json::json!({"result": "success"}))) .await .unwrap(); // Get the tool call - let call = agentfs.tools.get(id).await.unwrap().unwrap(); + let call = vfs.tools.get(id).await.unwrap().unwrap(); assert_eq!(call.name, "test_tool"); assert_eq!(call.status, ToolCallStatus::Success); // Get stats - let stats = agentfs.tools.stats_for("test_tool").await.unwrap().unwrap(); + let stats = vfs.tools.stats_for("test_tool").await.unwrap().unwrap(); assert_eq!(stats.total_calls, 1); assert_eq!(stats.successful, 1); } #[test] fn test_resolve_memory() { - let opts = AgentFSOptions::resolve(":memory:").unwrap(); + let opts = VfsOptions::resolve(":memory:").unwrap(); assert!(opts.id.is_none()); assert!(opts.path.is_none()); } @@ -684,7 +678,7 @@ mod tests { let temp_file = temp_dir.join("test_resolve_existing.db"); std::fs::write(&temp_file, b"test").unwrap(); - let opts = AgentFSOptions::resolve(temp_file.to_str().unwrap()).unwrap(); + let opts = VfsOptions::resolve(temp_file.to_str().unwrap()).unwrap(); assert!(opts.id.is_none()); assert_eq!(opts.path, Some(temp_file.to_str().unwrap().to_string())); @@ -694,13 +688,13 @@ mod tests { #[test] fn test_resolve_valid_agent_id_with_existing_db() { - // Setup: create .agentfs directory and a test database - let agentfs_dir = agentfs_dir(); - let _ = std::fs::create_dir_all(agentfs_dir); - let db_path = agentfs_dir.join("test-resolve-agent.db"); + // Setup: create .vfs directory and a test database + let vfs_dir = vfs_dir(); + let _ = std::fs::create_dir_all(vfs_dir); + let db_path = vfs_dir.join("test-resolve-agent.db"); std::fs::write(&db_path, b"test").unwrap(); - let opts = AgentFSOptions::resolve("test-resolve-agent").unwrap(); + let opts = VfsOptions::resolve("test-resolve-agent").unwrap(); assert!(opts.id.is_none()); assert_eq!(opts.path, Some(db_path.to_string_lossy().to_string())); @@ -712,7 +706,7 @@ mod tests { fn test_resolve_invalid_agent_id() { // Path traversal is path-shaped: rejected as a missing database, not // as a malformed agent ID. - let result = AgentFSOptions::resolve("../evil"); + let result = VfsOptions::resolve("../evil"); assert!(result.is_err()); assert!(result .unwrap_err() @@ -720,19 +714,19 @@ mod tests { .contains("database not found")); // Agent IDs with spaces should be rejected - let result = AgentFSOptions::resolve("invalid agent"); + let result = VfsOptions::resolve("invalid agent"); assert!(result.is_err()); assert!(result.unwrap_err().to_string().contains("invalid agent ID")); // Agent IDs with special characters should be rejected - let result = AgentFSOptions::resolve("agent@test"); + let result = VfsOptions::resolve("agent@test"); assert!(result.is_err()); assert!(result.unwrap_err().to_string().contains("invalid agent ID")); } #[test] fn test_resolve_nonexistent_agent() { - let result = AgentFSOptions::resolve("nonexistent-agent-12345"); + let result = VfsOptions::resolve("nonexistent-agent-12345"); assert!(result.is_err()); assert!(result.unwrap_err().to_string().contains("not found")); } @@ -744,7 +738,7 @@ mod tests { "definitely-missing-dir/agent.db", "definitely-missing.db", ] { - let err = AgentFSOptions::resolve(arg).unwrap_err(); + let err = VfsOptions::resolve(arg).unwrap_err(); let message = err.to_string(); assert!( message.contains("database not found") && message.contains(arg), @@ -757,7 +751,7 @@ mod tests { fn test_db_path_is_absolute() { // Mount teardown chdirs the process to `/`; a relative db path handed // to turso would make every later by-path operation resolve wrong. - let by_path = AgentFSOptions::with_path("some-dir/relative.db") + let by_path = VfsOptions::with_path("some-dir/relative.db") .db_path() .unwrap(); assert!( @@ -766,36 +760,36 @@ mod tests { ); assert!(by_path.ends_with("/some-dir/relative.db")); - let by_id = AgentFSOptions::with_id("db-path-absolute-test") + let by_id = VfsOptions::with_id("db-path-absolute-test") .db_path() .unwrap(); assert!( std::path::Path::new(&by_id).is_absolute(), "with_id must absolutize: {by_id}" ); - assert!(by_id.ends_with("/.agentfs/db-path-absolute-test.db")); + assert!(by_id.ends_with("/.vfs/db-path-absolute-test.db")); - assert_eq!(AgentFSOptions::ephemeral().db_path().unwrap(), ":memory:"); + assert_eq!(VfsOptions::ephemeral().db_path().unwrap(), ":memory:"); assert_eq!( - AgentFSOptions::with_path(":memory:").db_path().unwrap(), + VfsOptions::with_path(":memory:").db_path().unwrap(), ":memory:" ); } #[test] fn test_resolve_valid_agent_id_formats() { - // Setup: create .agentfs directory and test databases - let agentfs_dir = agentfs_dir(); - let _ = std::fs::create_dir_all(agentfs_dir); + // Setup: create .vfs directory and test databases + let vfs_dir = vfs_dir(); + let _ = std::fs::create_dir_all(vfs_dir); // Test various valid ID formats let valid_ids = ["my-agent", "my_agent", "MyAgent123", "agent-123_test"]; for id in valid_ids { - let db_path = agentfs_dir.join(format!("{}.db", id)); + let db_path = vfs_dir.join(format!("{}.db", id)); std::fs::write(&db_path, b"test").unwrap(); - let opts = AgentFSOptions::resolve(id).unwrap(); + let opts = VfsOptions::resolve(id).unwrap(); assert!(opts.id.is_none()); assert_eq!(opts.path, Some(db_path.to_string_lossy().to_string())); @@ -807,7 +801,7 @@ mod tests { #[tokio::test] async fn test_encrypted_database_creation() { let hex_key = "b1bbfda4f589dc9daaf004fe21111e00dc00c98237102f5c7002a5669fc76327"; - let db_path = agentfs_dir().join("test-encrypted-agent.db"); + let db_path = vfs_dir().join("test-encrypted-agent.db"); let file_names = [ "test-encrypted-agent.db", @@ -815,23 +809,19 @@ mod tests { "test-encrypted-agent.db-wal", ]; for file_name in file_names { - let _ = std::fs::remove_file(agentfs_dir().join(file_name)); + let _ = std::fs::remove_file(vfs_dir().join(file_name)); } // create encrypted database and write data { - let agentfs = AgentFS::open( - AgentFSOptions::with_id("test-encrypted-agent") + let vfs = Vfs::open( + VfsOptions::with_id("test-encrypted-agent") .with_encryption_key(hex_key, "aegis256"), ) .await .unwrap(); - agentfs - .kv - .set("test_key", &"encrypted_value") - .await - .unwrap(); + vfs.kv.set("test_key", &"encrypted_value").await.unwrap(); } // verify database file exists @@ -839,14 +829,14 @@ mod tests { // reopen with correct key - data should be readable { - let agentfs = AgentFS::open( - AgentFSOptions::with_path(db_path.to_str().unwrap()) + let vfs = Vfs::open( + VfsOptions::with_path(db_path.to_str().unwrap()) .with_encryption_key(hex_key, "aegis256"), ) .await .unwrap(); - let value: Option = agentfs.kv.get("test_key").await.unwrap(); + let value: Option = vfs.kv.get("test_key").await.unwrap(); assert_eq!(value, Some("encrypted_value".to_string())); } @@ -855,13 +845,13 @@ mod tests { let path_clone = db_path.clone(); let result = std::panic::catch_unwind(|| { tokio::runtime::Runtime::new().unwrap().block_on(async { - let agentfs = AgentFS::open( - AgentFSOptions::with_path(path_clone.to_str().unwrap()) + let vfs = Vfs::open( + VfsOptions::with_path(path_clone.to_str().unwrap()) .with_encryption_key(wrong_key, "aegis256"), ) .await .unwrap(); - let _: Option = agentfs.kv.get("test_key").await.unwrap(); + let _: Option = vfs.kv.get("test_key").await.unwrap(); }) }); assert!(result.is_err(), "Opening with wrong key should panic"); @@ -870,18 +860,17 @@ mod tests { let path_clone = db_path.clone(); let result = std::panic::catch_unwind(|| { tokio::runtime::Runtime::new().unwrap().block_on(async { - let agentfs = - AgentFS::open(AgentFSOptions::with_path(path_clone.to_str().unwrap())) - .await - .unwrap(); - let _: Option = agentfs.kv.get("test_key").await.unwrap(); + let vfs = Vfs::open(VfsOptions::with_path(path_clone.to_str().unwrap())) + .await + .unwrap(); + let _: Option = vfs.kv.get("test_key").await.unwrap(); }) }); assert!(result.is_err(), "Opening without key should panic"); // cleanup for file_name in file_names { - let _ = std::fs::remove_file(agentfs_dir().join(file_name)); + let _ = std::fs::remove_file(vfs_dir().join(file_name)); } } } diff --git a/crates/agentfs-core/src/mounts.rs b/crates/vfs-core/src/mounts.rs similarity index 73% rename from crates/agentfs-core/src/mounts.rs rename to crates/vfs-core/src/mounts.rs index 6d755f01..7bbe1b73 100644 --- a/crates/agentfs-core/src/mounts.rs +++ b/crates/vfs-core/src/mounts.rs @@ -1,15 +1,15 @@ use std::path::PathBuf; -/// Information about a mounted agentfs filesystem +/// Information about a mounted vfs filesystem #[derive(Debug, Clone)] pub struct Mount { - /// The ID (from the mount source, e.g., "agentfs:my-agent" -> "my-agent") + /// The ID (from the mount source, e.g., "vfs:my-agent" -> "my-agent") pub id: String, /// The mountpoint path pub mountpoint: PathBuf, } -/// Get all currently mounted agentfs filesystems by parsing /proc/mounts +/// Get all currently mounted vfs filesystems by parsing /proc/mounts /// /// This is the authoritative source for mount information - if it's in /proc/mounts, /// it's mounted. If not, it's not. No stale state possible. @@ -22,8 +22,8 @@ pub fn get_mounts() -> Vec { .lines() .filter_map(|line| { let parts: Vec<&str> = line.split_whitespace().collect(); - if parts.len() >= 2 && parts[0].starts_with("agentfs:") { - let agent_id = parts[0].strip_prefix("agentfs:")?.to_string(); + if parts.len() >= 2 && parts[0].starts_with("vfs:") { + let agent_id = parts[0].strip_prefix("vfs:")?.to_string(); // Skip the internal "fuse" mount used by the daemon if agent_id == "fuse" { return None; @@ -39,7 +39,7 @@ pub fn get_mounts() -> Vec { .collect() } -/// Get all currently mounted agentfs filesystems (non-Linux stub) +/// Get all currently mounted vfs filesystems (non-Linux stub) #[cfg(not(target_os = "linux"))] pub fn get_mounts() -> Vec { // On macOS, we could parse the output of `mount` command diff --git a/crates/agentfs-core/src/options.rs b/crates/vfs-core/src/options.rs similarity index 87% rename from crates/agentfs-core/src/options.rs rename to crates/vfs-core/src/options.rs index c7d99d12..245098a8 100644 --- a/crates/agentfs-core/src/options.rs +++ b/crates/vfs-core/src/options.rs @@ -5,9 +5,9 @@ use turso::sync::PartialSyncOpts; use crate::config::CoreConfig; use crate::error::{Error, Result}; -/// Directory containing agentfs databases -pub fn agentfs_dir() -> &'static std::path::Path { - std::path::Path::new(".agentfs") +/// Directory containing vfs databases +pub fn vfs_dir() -> &'static std::path::Path { + std::path::Path::new(".vfs") } /// Configuration options for sync @@ -30,11 +30,11 @@ pub struct EncryptionConfig { pub cipher: String, } -/// Configuration options for opening an AgentFS instance +/// Configuration options for opening a Vfs instance #[derive(Debug, Clone, Default)] -pub struct AgentFSOptions { +pub struct VfsOptions { /// Optional unique identifier for the agent. - /// - If Some(id): Creates persistent storage at `.agentfs/{id}.db` + /// - If Some(id): Creates persistent storage at `.vfs/{id}.db` /// - If None: Uses ephemeral in-memory database pub(crate) id: Option, /// Optional custom path to the database file. @@ -48,11 +48,11 @@ pub struct AgentFSOptions { /// Encryption configuration for database at rest pub(crate) encryption: Option, /// Typed core runtime configuration. When omitted, [`CoreConfig::from_env`] - /// is evaluated once by [`AgentFS::open`](crate::AgentFS::open). + /// is evaluated once by [`Vfs::open`](crate::Vfs::open). pub core_config: Option, } -impl AgentFSOptions { +impl VfsOptions { /// Validates an agent ID to prevent path traversal and ensure safe filesystem operations. /// Returns true if the ID contains only alphanumeric characters, hyphens, and underscores. pub fn validate_agent_id(id: &str) -> bool { @@ -75,12 +75,12 @@ impl AgentFSOptions { return Err(Error::InvalidAgentId(id.clone())); } - // Ensure .agentfs directory exists - let agentfs_dir = agentfs_dir(); - if !agentfs_dir.exists() { - std::fs::create_dir_all(agentfs_dir)?; + // Ensure .vfs directory exists + let vfs_dir = vfs_dir(); + if !vfs_dir.exists() { + std::fs::create_dir_all(vfs_dir)?; } - agentfs_dir.join(format!("{}.db", id)) + vfs_dir.join(format!("{}.db", id)) } else { // No id or path = ephemeral in-memory database return Ok(":memory:".to_string()); @@ -166,11 +166,11 @@ impl AgentFSOptions { self } - /// Resolve an id-or-path string to AgentFSOptions + /// Resolve an id-or-path string to VfsOptions /// /// Resolution order (first match wins): /// 1. `:memory:` -> ephemeral in-memory database - /// 2. Valid agent ID with existing `.agentfs/{id}.db` -> uses that agent + /// 2. Valid agent ID with existing `.vfs/{id}.db` -> uses that agent /// 3. Existing file path -> uses that path directly /// /// When nothing matches: ID-shaped arguments report `AgentNotFound`, @@ -183,9 +183,9 @@ impl AgentFSOptions { return Ok(Self::ephemeral()); } - // First, check if it's a valid agent ID with an existing database in .agentfs/ - if AgentFSOptions::validate_agent_id(&id_or_path) { - let db_path = agentfs_dir().join(format!("{}.db", id_or_path)); + // First, check if it's a valid agent ID with an existing database in .vfs/ + if VfsOptions::validate_agent_id(&id_or_path) { + let db_path = vfs_dir().join(format!("{}.db", id_or_path)); if db_path.exists() { return Ok(Self::with_path(db_path.to_str().ok_or_else(|| { Error::InvalidUtf8Path(db_path.display().to_string()) @@ -197,11 +197,11 @@ impl AgentFSOptions { let path = Path::new(&id_or_path); if path.is_file() { Ok(Self::with_path(id_or_path)) - } else if AgentFSOptions::validate_agent_id(&id_or_path) { + } else if VfsOptions::validate_agent_id(&id_or_path) { // Not a valid agent and not an existing file Err(Error::AgentNotFound { id: id_or_path.clone(), - path: agentfs_dir() + path: vfs_dir() .join(format!("{}.db", id_or_path)) .display() .to_string(), diff --git a/crates/agentfs-core/src/pool.rs b/crates/vfs-core/src/pool.rs similarity index 99% rename from crates/agentfs-core/src/pool.rs rename to crates/vfs-core/src/pool.rs index 47e8864b..c1716cef 100644 --- a/crates/agentfs-core/src/pool.rs +++ b/crates/vfs-core/src/pool.rs @@ -417,7 +417,7 @@ mod tests { turso::Error::Corrupt("bad page".to_string()) ))); assert!(is_fatal_connection_error(&Error::Database( - turso::Error::NotAdb("not an agentfs db".to_string()) + turso::Error::NotAdb("not an vfs db".to_string()) ))); assert!(is_fatal_connection_error(&Error::Database( turso::Error::IoError(std::io::ErrorKind::UnexpectedEof, "read") diff --git a/crates/agentfs-core/src/schema/integrity.rs b/crates/vfs-core/src/schema/integrity.rs similarity index 99% rename from crates/agentfs-core/src/schema/integrity.rs rename to crates/vfs-core/src/schema/integrity.rs index 90a6d83b..ada5990d 100644 --- a/crates/agentfs-core/src/schema/integrity.rs +++ b/crates/vfs-core/src/schema/integrity.rs @@ -1,4 +1,4 @@ -//! Read-only integrity checks for AgentFS databases. +//! Read-only integrity checks for Vfs databases. use crate::error::{Error, Result}; use serde::Serialize; @@ -162,12 +162,12 @@ async fn check_config(conn: &Connection, report: &mut Report) -> Result<()> { let schema_version = config_string(conn, "schema_version").await?; report.push_check( "config.schema_version", - schema_version.as_deref() == Some(super::AGENTFS_SCHEMA_VERSION), + schema_version.as_deref() == Some(super::VFS_SCHEMA_VERSION), schema_version .as_deref() .map(|value| format!("found {value}")) .unwrap_or_else(|| "missing".to_string()), - if schema_version.as_deref() == Some(super::AGENTFS_SCHEMA_VERSION) { + if schema_version.as_deref() == Some(super::VFS_SCHEMA_VERSION) { Some(0) } else { Some(1) diff --git a/crates/agentfs-core/src/schema/mod.rs b/crates/vfs-core/src/schema/mod.rs similarity index 97% rename from crates/agentfs-core/src/schema/mod.rs rename to crates/vfs-core/src/schema/mod.rs index cc4c83bc..5cef5634 100644 --- a/crates/agentfs-core/src/schema/mod.rs +++ b/crates/vfs-core/src/schema/mod.rs @@ -1,4 +1,4 @@ -//! Schema authority for AgentFS databases. +//! Schema authority for Vfs databases. //! //! This module owns all production Rust DDL, schema-version detection, and //! user_version keyed migrations for the pre-crate-split SDK core. @@ -14,7 +14,7 @@ use turso::{Connection, Value}; pub const CURRENT: SchemaVersion = SchemaVersion::V0_5; /// Compatibility string for callers that still surface the historical version. -pub const AGENTFS_SCHEMA_VERSION: &str = CURRENT.as_str(); +pub const VFS_SCHEMA_VERSION: &str = CURRENT.as_str(); pub const CONFIG_SCHEMA_VERSION_KEY: &str = "schema_version"; pub const CONFIG_CHUNK_SIZE_KEY: &str = "chunk_size"; pub const CONFIG_INLINE_THRESHOLD_KEY: &str = "inline_threshold"; @@ -390,7 +390,7 @@ pub async fn check_schema_version(conn: &Connection) -> Result<()> { /// ones (compat columns, missing indexes, `user_version` stamp), but never run /// version upgrades. An older supported schema returns /// [`Error::SchemaVersionMismatch`] so callers can direct the user to -/// `agentfs migrate`, which owns explicit upgrades via [`ensure_current`]. +/// `vfs migrate`, which owns explicit upgrades via [`ensure_current`]. pub async fn require_current(conn: &Connection) -> Result<()> { if let Some(version) = detect_schema_version(conn).await? { if version != CURRENT { @@ -869,7 +869,7 @@ fn is_duplicate_column_error(err: &turso::Error) -> bool { #[cfg(test)] mod tests { use super::*; - use crate::{AgentFS, AgentFSOptions, KvStore, ToolCalls, DEFAULT_FILE_MODE}; + use crate::{KvStore, ToolCalls, Vfs, VfsOptions, DEFAULT_FILE_MODE}; use std::path::{Path, PathBuf}; use tempfile::tempdir; use turso::Builder; @@ -913,7 +913,7 @@ mod tests { drop(conn); drop(db); - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())).await?; + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())).await?; assert_eq!(agent.fs.read_file("/file.txt").await?.unwrap(), b"abcdef"); let conn = agent.get_connection().await?; let report = @@ -977,11 +977,10 @@ mod tests { create_legacy_fixture(&conn, version).await?; } - let err = - match AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())).await { - Ok(_) => panic!("{version}: AgentFS::open must not upgrade an old schema"), - Err(err) => err, - }; + let err = match Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())).await { + Ok(_) => panic!("{version}: Vfs::open must not upgrade an old schema"), + Err(err) => err, + }; assert!( matches!(err, Error::SchemaVersionMismatch { .. }), "{version}: unexpected open error {err}" @@ -1018,7 +1017,7 @@ mod tests { ensure_current(&conn).await?; drop(conn); drop(db); - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())).await?; + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())).await?; assert_eq!(agent.fs.read_file("/file.txt").await?.unwrap(), b"abcdef"); } Ok(()) @@ -1031,10 +1030,10 @@ mod tests { let agent_path = create_legacy_whiteout_fixture_file(dir.path(), "agent-open", SchemaVersion::V0_5) .await?; - let agent = AgentFS::open(AgentFSOptions::with_path(agent_path.to_string_lossy())).await?; + let agent = Vfs::open(VfsOptions::with_path(agent_path.to_string_lossy())).await?; assert_eq!(agent.fs.read_file("/file.txt").await?.unwrap(), b"abcdef"); drop(agent); - assert_legacy_whiteout_parent_path(&agent_path, "AgentFS::open").await?; + assert_legacy_whiteout_parent_path(&agent_path, "Vfs::open").await?; let kv_path = create_legacy_whiteout_fixture_file(dir.path(), "kv-open", SchemaVersion::V0_5).await?; @@ -1092,7 +1091,7 @@ mod tests { .await?; ensure_current(&conn).await?; assert_eq!(detect_schema_version(&conn).await?, Some(CURRENT)); - let agent = AgentFS::open(AgentFSOptions::with_path(hybrid_path.to_string_lossy())).await?; + let agent = Vfs::open(VfsOptions::with_path(hybrid_path.to_string_lossy())).await?; assert_eq!(agent.fs.read_file("/file.txt").await?.unwrap(), b"abcdef"); let corrupt_current_path = dir.path().join("current-missing-table.db"); diff --git a/crates/agentfs-core/src/semantics/access.rs b/crates/vfs-core/src/semantics/access.rs similarity index 100% rename from crates/agentfs-core/src/semantics/access.rs rename to crates/vfs-core/src/semantics/access.rs diff --git a/crates/agentfs-core/src/semantics/durability.rs b/crates/vfs-core/src/semantics/durability.rs similarity index 98% rename from crates/agentfs-core/src/semantics/durability.rs rename to crates/vfs-core/src/semantics/durability.rs index 992e0b15..6e993839 100644 --- a/crates/agentfs-core/src/semantics/durability.rs +++ b/crates/vfs-core/src/semantics/durability.rs @@ -46,7 +46,7 @@ impl Semantics { /// Return coherent attributes for `ino`. /// /// Filesystem implementations own their pending-write visibility primitive. - /// AgentFS merges batcher pending state into attributes before returning; + /// Vfs merges batcher pending state into attributes before returning; /// adapters should use this method for NFS GETATTR and WCC attributes /// instead of bypassing the shared coherence contract. pub async fn stat_coherent(&self, ino: i64) -> Result> { diff --git a/crates/agentfs-core/src/semantics/handles.rs b/crates/vfs-core/src/semantics/handles.rs similarity index 96% rename from crates/agentfs-core/src/semantics/handles.rs rename to crates/vfs-core/src/semantics/handles.rs index c4b47b95..e625a10c 100644 --- a/crates/agentfs-core/src/semantics/handles.rs +++ b/crates/vfs-core/src/semantics/handles.rs @@ -1,6 +1,6 @@ //! Handle and authority state shared by protocol adapters. //! -//! NFS is stateless, but AgentFS deliberately preserves write authority +//! NFS is stateless, but Vfs deliberately preserves write authority //! captured by a successful CREATE response so later mode changes do not make //! already-open client writeback fail. This table is scoped to one //! [`Semantics`](super::Semantics) facade. @@ -10,7 +10,7 @@ //! (`clippy::await_holding_lock` is deny-by-workspace). use crate::error::Result; -use crate::fs::{agentfs::ReapHook, BoxedFile, FileSystem}; +use crate::fs::{vfs::ReapHook, BoxedFile, FileSystem}; use async_trait::async_trait; use lru::LruCache; use parking_lot::Mutex; @@ -275,7 +275,7 @@ impl ReapHook for HandleTable { #[cfg(test)] mod tests { use super::*; - use crate::{AgentFS, AgentFSOptions, FileSystem, Semantics, DEFAULT_FILE_MODE}; + use crate::{FileSystem, Semantics, Vfs, VfsOptions, DEFAULT_FILE_MODE}; #[test] fn write_authority_tokens_evict_least_recently_used_token() { @@ -319,7 +319,7 @@ mod tests { #[tokio::test] async fn open_cached_reuses_and_invalidates_handles_by_inode() -> Result<()> { - let agent = AgentFS::open(AgentFSOptions::ephemeral()).await?; + let agent = Vfs::open(VfsOptions::ephemeral()).await?; let fs: Arc = Arc::new(agent.fs); let (stats, _file) = fs .create_file(1, "cached.txt", DEFAULT_FILE_MODE, 0, 0) @@ -338,7 +338,7 @@ mod tests { #[tokio::test] async fn open_cached_upgrades_read_handle_on_first_write_open() -> Result<()> { - let agent = AgentFS::open(AgentFSOptions::ephemeral()).await?; + let agent = Vfs::open(VfsOptions::ephemeral()).await?; let fs: Arc = Arc::new(agent.fs); let (stats, _file) = fs .create_file(1, "upgrade.txt", DEFAULT_FILE_MODE, 0, 0) @@ -363,7 +363,7 @@ mod tests { #[tokio::test] async fn reap_hook_invalidates_authority_tokens() -> Result<()> { - let agent = AgentFS::open(AgentFSOptions::ephemeral()).await?; + let agent = Vfs::open(VfsOptions::ephemeral()).await?; let fs: Arc = Arc::new(agent.fs); let semantics = Semantics::new(fs.clone()); let (stats, file) = fs diff --git a/crates/agentfs-core/src/semantics/mod.rs b/crates/vfs-core/src/semantics/mod.rs similarity index 93% rename from crates/agentfs-core/src/semantics/mod.rs rename to crates/vfs-core/src/semantics/mod.rs index 492919f2..db46e083 100644 --- a/crates/agentfs-core/src/semantics/mod.rs +++ b/crates/vfs-core/src/semantics/mod.rs @@ -15,7 +15,7 @@ pub use handles::{Authority, Handle, HandleTable}; mod tests { use super::{AckDurability, Semantics}; use crate::config::{BatcherConfig, CoreConfig}; - use crate::{AgentFS, AgentFSOptions, FileSystem, DEFAULT_FILE_MODE, S_IFREG}; + use crate::{FileSystem, Vfs, VfsOptions, DEFAULT_FILE_MODE, S_IFREG}; use std::sync::Arc; use std::time::Duration; @@ -39,8 +39,8 @@ mod tests { ) -> crate::error::Result<()> { let dir = tempfile::tempdir()?; let db_path = dir.path().join("semantics-durability.db"); - let agent = AgentFS::open( - AgentFSOptions::with_path(db_path.to_str().expect("test DB path is UTF-8")) + let agent = Vfs::open( + VfsOptions::with_path(db_path.to_str().expect("test DB path is UTF-8")) .with_core_config(long_batch_window_config()), ) .await?; @@ -81,7 +81,7 @@ mod tests { drop(semantics); drop(fs); - let reopened = AgentFS::open(AgentFSOptions::with_path( + let reopened = Vfs::open(VfsOptions::with_path( db_path.to_str().expect("test DB path is UTF-8"), )) .await?; diff --git a/crates/agentfs-core/src/telemetry/mod.rs b/crates/vfs-core/src/telemetry/mod.rs similarity index 91% rename from crates/agentfs-core/src/telemetry/mod.rs rename to crates/vfs-core/src/telemetry/mod.rs index 2167f4ca..4bff80aa 100644 --- a/crates/agentfs-core/src/telemetry/mod.rs +++ b/crates/vfs-core/src/telemetry/mod.rs @@ -1,6 +1,6 @@ -//! Lightweight env-gated telemetry counters for AgentFS hot paths. +//! Lightweight env-gated telemetry counters for Vfs hot paths. //! -//! Recording remains a single cached `AGENTFS_PROFILE` branch followed by +//! Recording remains a single cached `VFS_PROFILE` branch followed by //! relaxed atomic updates. Counter sections are declared from compact tables so //! core and adapter-specific vocabularies can live in their owning crates while //! still being reported by one process-wide registry. @@ -327,16 +327,16 @@ crate::telemetry::define_counters! { chunk_read_queries: Counter, chunk_read_chunks: Sum, chunk_write_chunks: Sum, - agentfs_batcher_enqueues: Counter, - agentfs_batcher_drains_timer: Counter, - agentfs_batcher_drains_bytes: Counter, - agentfs_batcher_drains_explicit: Counter, - agentfs_batcher_pending_max_bytes: Max, - agentfs_batcher_coalesced_ranges: Sum, - agentfs_batcher_commit_latency_ns_total: Sum, - agentfs_batcher_commit_txns: Counter, - agentfs_batcher_txn_inodes_total: Sum, - agentfs_batcher_txn_inodes_max: Max, + vfs_batcher_enqueues: Counter, + vfs_batcher_drains_timer: Counter, + vfs_batcher_drains_bytes: Counter, + vfs_batcher_drains_explicit: Counter, + vfs_batcher_pending_max_bytes: Max, + vfs_batcher_coalesced_ranges: Sum, + vfs_batcher_commit_latency_ns_total: Sum, + vfs_batcher_commit_txns: Counter, + vfs_batcher_txn_inodes_total: Sum, + vfs_batcher_txn_inodes_max: Max, wal_checkpoint: Timer, } } @@ -355,7 +355,7 @@ crate::telemetry::define_counters! { } } -/// Returns true when profiling is enabled with `AGENTFS_PROFILE=1`. +/// Returns true when profiling is enabled with `VFS_PROFILE=1`. /// Always-on under `#[cfg(test)]` so unit tests can assert on counters without /// racing the global `OnceLock` init. pub fn is_enabled() -> bool { @@ -366,7 +366,7 @@ pub fn is_enabled() -> bool { #[cfg(not(test))] { *ENABLED.get_or_init(|| { - crate::config::EnvReader::new().bool("AGENTFS_PROFILE", DEFAULT_PROFILE_ENABLED) + crate::config::EnvReader::new().bool("VFS_PROFILE", DEFAULT_PROFILE_ENABLED) }) } } @@ -522,58 +522,58 @@ pub(crate) fn record_chunk_write_chunks(chunks: u64) { } } -pub(crate) fn record_agentfs_batcher_enqueue() { +pub(crate) fn record_vfs_batcher_enqueue() { if is_enabled() { - CORE_COUNTERS.agentfs_batcher_enqueues.increment(); + CORE_COUNTERS.vfs_batcher_enqueues.increment(); } } -pub(crate) fn record_agentfs_batcher_drain_timer() { +pub(crate) fn record_vfs_batcher_drain_timer() { if is_enabled() { - CORE_COUNTERS.agentfs_batcher_drains_timer.increment(); + CORE_COUNTERS.vfs_batcher_drains_timer.increment(); } } -pub(crate) fn record_agentfs_batcher_drain_bytes() { +pub(crate) fn record_vfs_batcher_drain_bytes() { if is_enabled() { - CORE_COUNTERS.agentfs_batcher_drains_bytes.increment(); + CORE_COUNTERS.vfs_batcher_drains_bytes.increment(); } } -pub(crate) fn record_agentfs_batcher_drain_explicit() { +pub(crate) fn record_vfs_batcher_drain_explicit() { if is_enabled() { - CORE_COUNTERS.agentfs_batcher_drains_explicit.increment(); + CORE_COUNTERS.vfs_batcher_drains_explicit.increment(); } } -pub(crate) fn record_agentfs_batcher_pending_bytes(pending_bytes: u64) { +pub(crate) fn record_vfs_batcher_pending_bytes(pending_bytes: u64) { if is_enabled() { CORE_COUNTERS - .agentfs_batcher_pending_max_bytes + .vfs_batcher_pending_max_bytes .update(pending_bytes); } } -pub(crate) fn record_agentfs_batcher_coalesced_ranges(ranges: u64) { +pub(crate) fn record_vfs_batcher_coalesced_ranges(ranges: u64) { if is_enabled() && ranges > 0 { - CORE_COUNTERS.agentfs_batcher_coalesced_ranges.add(ranges); + CORE_COUNTERS.vfs_batcher_coalesced_ranges.add(ranges); } } -pub(crate) fn record_agentfs_batcher_commit_latency(duration: Duration) { +pub(crate) fn record_vfs_batcher_commit_latency(duration: Duration) { if is_enabled() { CORE_COUNTERS - .agentfs_batcher_commit_latency_ns_total + .vfs_batcher_commit_latency_ns_total .add(duration.as_nanos() as u64); } } /// Record one batcher SQLite commit transaction that covered `inodes` inodes. -pub(crate) fn record_agentfs_batcher_commit_txn(inodes: u64) { +pub(crate) fn record_vfs_batcher_commit_txn(inodes: u64) { if is_enabled() { - CORE_COUNTERS.agentfs_batcher_commit_txns.increment(); - CORE_COUNTERS.agentfs_batcher_txn_inodes_total.add(inodes); - CORE_COUNTERS.agentfs_batcher_txn_inodes_max.update(inodes); + CORE_COUNTERS.vfs_batcher_commit_txns.increment(); + CORE_COUNTERS.vfs_batcher_txn_inodes_total.add(inodes); + CORE_COUNTERS.vfs_batcher_txn_inodes_max.update(inodes); } } diff --git a/crates/agentfs-core/src/toolcalls.rs b/crates/vfs-core/src/toolcalls.rs similarity index 99% rename from crates/agentfs-core/src/toolcalls.rs rename to crates/vfs-core/src/toolcalls.rs index 7d3bfcce..1159da5e 100644 --- a/crates/agentfs-core/src/toolcalls.rs +++ b/crates/vfs-core/src/toolcalls.rs @@ -86,9 +86,9 @@ impl ToolCalls { pub(crate) async fn new(db_path: &str) -> Result { let db = Builder::new_local(db_path).build().await?; let options = if db_path == ":memory:" { - crate::fs::agentfs::memory_connection_pool_options() + crate::fs::vfs::memory_connection_pool_options() } else { - crate::fs::agentfs::file_backed_connection_pool_options() + crate::fs::vfs::file_backed_connection_pool_options() }; let pool = ConnectionPool::with_options(DatabaseType::Local(db), options); let tc = Self { pool }; diff --git a/crates/agentfs-core/tests/concurrency_integrity.rs b/crates/vfs-core/tests/concurrency_integrity.rs similarity index 95% rename from crates/agentfs-core/tests/concurrency_integrity.rs rename to crates/vfs-core/tests/concurrency_integrity.rs index 2f06849b..0cc76189 100644 --- a/crates/agentfs-core/tests/concurrency_integrity.rs +++ b/crates/vfs-core/tests/concurrency_integrity.rs @@ -1,5 +1,3 @@ -use agentfs_core::error::Result; -use agentfs_core::{AgentFS, AgentFSOptions, DEFAULT_FILE_MODE}; use serde_json::{json, Value}; use std::sync::{ atomic::{AtomicUsize, Ordering}, @@ -7,6 +5,8 @@ use std::sync::{ }; use tokio::sync::Barrier; use tokio::time::{sleep, Duration}; +use vfs_core::error::Result; +use vfs_core::{Vfs, VfsOptions, DEFAULT_FILE_MODE}; const WORKERS: usize = 6; const ITERATIONS: usize = 4; @@ -15,7 +15,7 @@ const ITERATIONS: usize = 4; async fn concurrent_sdk_operations_preserve_database_integrity() -> Result<()> { let temp_dir = tempfile::tempdir()?; let db_path = temp_dir.path().join("concurrent.db"); - let agent = AgentFS::open(AgentFSOptions::with_path(db_path.to_string_lossy())).await?; + let agent = Vfs::open(VfsOptions::with_path(db_path.to_string_lossy())).await?; let start_barrier = Arc::new(Barrier::new(WORKERS + 1)); let active_workers = Arc::new(AtomicUsize::new(0)); @@ -141,7 +141,7 @@ async fn concurrent_sdk_operations_preserve_database_integrity() -> Result<()> { Ok(()) } -async fn assert_final_state(agent: &AgentFS) -> Result<()> { +async fn assert_final_state(agent: &Vfs) -> Result<()> { for worker in 0..WORKERS { let worker_dir = format!("/worker-{worker}"); let worker_stat = agent.fs.stat(&worker_dir).await?.unwrap(); @@ -207,7 +207,7 @@ async fn assert_final_state(agent: &AgentFS) -> Result<()> { Ok(()) } -async fn assert_integrity_check_ok(agent: &AgentFS) -> Result<()> { +async fn assert_integrity_check_ok(agent: &Vfs) -> Result<()> { let conn = agent.get_connection().await?; let mut rows = conn.query("PRAGMA integrity_check", ()).await?; let mut results = Vec::new(); @@ -218,11 +218,7 @@ async fn assert_integrity_check_ok(agent: &AgentFS) -> Result<()> { Ok(()) } -async fn assert_inline_inode_has_no_chunks( - agent: &AgentFS, - ino: i64, - expected: &[u8], -) -> Result<()> { +async fn assert_inline_inode_has_no_chunks(agent: &Vfs, ino: i64, expected: &[u8]) -> Result<()> { let conn = agent.get_connection().await?; let mut rows = conn .query( diff --git a/crates/agentfs-core/tests/host_layout.rs b/crates/vfs-core/tests/host_layout.rs similarity index 100% rename from crates/agentfs-core/tests/host_layout.rs rename to crates/vfs-core/tests/host_layout.rs diff --git a/crates/agentfs-core/tests/snapshot_restore.rs b/crates/vfs-core/tests/snapshot_restore.rs similarity index 92% rename from crates/agentfs-core/tests/snapshot_restore.rs rename to crates/vfs-core/tests/snapshot_restore.rs index e79aba66..54482215 100644 --- a/crates/agentfs-core/tests/snapshot_restore.rs +++ b/crates/vfs-core/tests/snapshot_restore.rs @@ -1,8 +1,8 @@ -use agentfs_core::error::Result; -use agentfs_core::fs::{AgentFS as AgentFsCore, FileSystem, FsError}; -use agentfs_core::{AgentFS, AgentFSOptions, ToolCallStatus, DEFAULT_FILE_MODE}; use serde_json::{json, Value}; use std::path::{Path, PathBuf}; +use vfs_core::error::Result; +use vfs_core::fs::{FileSystem, FsError, Vfs as VfsCore}; +use vfs_core::{ToolCallStatus, Vfs, VfsOptions, DEFAULT_FILE_MODE}; const ROOT_INO: i64 = 1; @@ -32,7 +32,7 @@ async fn snapshot_restore_preserves_one_file_agent_state_after_checkpoint() -> R let source_db = temp_dir.path().join("source.db"); let restored_db = temp_dir.path().join("restored.db"); - let agent = AgentFS::open(AgentFSOptions::with_path(source_db.to_string_lossy())).await?; + let agent = Vfs::open(VfsOptions::with_path(source_db.to_string_lossy())).await?; let chunk_size = agent.fs.chunk_size(); agent.fs.mkdir("/workspace", 0, 0).await?; @@ -53,7 +53,7 @@ async fn snapshot_restore_preserves_one_file_agent_state_after_checkpoint() -> R std::fs::copy(&source_db, &restored_db)?; - let restored = AgentFS::open(AgentFSOptions::with_path(restored_db.to_string_lossy())).await?; + let restored = Vfs::open(VfsOptions::with_path(restored_db.to_string_lossy())).await?; assert_eq!(restored.fs.chunk_size(), chunk_size); assert_generated_state(&restored, &cases, &tool_ids).await?; assert_integrity_check_ok(&restored).await?; @@ -61,11 +61,7 @@ async fn snapshot_restore_preserves_one_file_agent_state_after_checkpoint() -> R Ok(()) } -async fn create_snapshot_case( - agent: &AgentFS, - chunk_size: usize, - seed: usize, -) -> Result { +async fn create_snapshot_case(agent: &Vfs, chunk_size: usize, seed: usize) -> Result { let dir = format!("/workspace/seed-{seed}"); let nested_dir = format!("{dir}/nested"); let crossing_path = format!("{nested_dir}/crossing.bin"); @@ -155,7 +151,7 @@ async fn create_snapshot_case( }) } -async fn record_tool_calls(agent: &AgentFS, seed: usize) -> Result { +async fn record_tool_calls(agent: &Vfs, seed: usize) -> Result { let started_at = 1_700_000_000 + seed as i64 * 10; let success = agent .tools @@ -185,7 +181,7 @@ async fn record_tool_calls(agent: &AgentFS, seed: usize) -> Result { } async fn assert_generated_state( - agent: &AgentFS, + agent: &Vfs, cases: &[SnapshotCase], tool_ids: &[ToolIds], ) -> Result<()> { @@ -321,7 +317,7 @@ async fn assert_generated_state( Ok(()) } -async fn parent_and_name(fs: &AgentFsCore, path: &str) -> Result<(i64, String)> { +async fn parent_and_name(fs: &VfsCore, path: &str) -> Result<(i64, String)> { let normalized = normalize_test_path(path); let components = normalized .split('/') @@ -355,7 +351,7 @@ fn normalize_test_path(path: &str) -> String { } } -async fn link_path(fs: &AgentFsCore, oldpath: &str, newpath: &str) -> Result<()> { +async fn link_path(fs: &VfsCore, oldpath: &str, newpath: &str) -> Result<()> { let source = fs.stat(oldpath).await?.ok_or(FsError::NotFound)?; let (newparent_ino, newname) = parent_and_name(fs, newpath).await?; FileSystem::link(fs, source.ino, newparent_ino, &newname) @@ -364,7 +360,7 @@ async fn link_path(fs: &AgentFsCore, oldpath: &str, newpath: &str) -> Result<()> } async fn create_symlink_path( - fs: &AgentFsCore, + fs: &VfsCore, target: &str, linkpath: &str, uid: u32, @@ -376,7 +372,7 @@ async fn create_symlink_path( .map(|_| ()) } -async fn lstat_path(fs: &AgentFsCore, path: &str) -> Result> { +async fn lstat_path(fs: &VfsCore, path: &str) -> Result> { let normalized = normalize_test_path(path); if normalized == "/" { return FileSystem::getattr(fs, ROOT_INO).await; @@ -385,7 +381,7 @@ async fn lstat_path(fs: &AgentFsCore, path: &str) -> Result Result<()> { +async fn assert_integrity_check_ok(agent: &Vfs) -> Result<()> { let conn = agent.get_connection().await?; let mut rows = conn.query("PRAGMA integrity_check", ()).await?; let mut results = Vec::new(); @@ -396,7 +392,7 @@ async fn assert_integrity_check_ok(agent: &AgentFS) -> Result<()> { Ok(()) } -async fn assert_journal_mode_is_wal(agent: &AgentFS) -> Result<()> { +async fn assert_journal_mode_is_wal(agent: &Vfs) -> Result<()> { let conn = agent.get_connection().await?; let mut rows = conn.query("PRAGMA journal_mode", ()).await?; let row = rows.next().await?.unwrap(); @@ -404,11 +400,7 @@ async fn assert_journal_mode_is_wal(agent: &AgentFS) -> Result<()> { Ok(()) } -async fn assert_inline_inode_has_no_chunks( - agent: &AgentFS, - ino: i64, - expected: &[u8], -) -> Result<()> { +async fn assert_inline_inode_has_no_chunks(agent: &Vfs, ino: i64, expected: &[u8]) -> Result<()> { agent.fs.drain_all().await?; let conn = agent.get_connection().await?; let mut rows = conn diff --git a/crates/agentfs-fuse/Cargo.toml b/crates/vfs-fuse/Cargo.toml similarity index 83% rename from crates/agentfs-fuse/Cargo.toml rename to crates/vfs-fuse/Cargo.toml index 76c9bd92..c60495ef 100644 --- a/crates/agentfs-fuse/Cargo.toml +++ b/crates/vfs-fuse/Cargo.toml @@ -1,15 +1,15 @@ [package] -name = "agentfs-fuse" +name = "vfs-fuse" version.workspace = true edition.workspace = true -description = "Linux FUSE adapter for AgentFS" +description = "Linux FUSE adapter for Vfs" license.workspace = true [lints] workspace = true [dependencies] -agentfs-core = { workspace = true } +vfs-core = { workspace = true } anyhow = { workspace = true } libc = { workspace = true } memchr = { workspace = true } diff --git a/crates/agentfs-fuse/src/adapter/cache.rs b/crates/vfs-fuse/src/adapter/cache.rs similarity index 99% rename from crates/agentfs-fuse/src/adapter/cache.rs rename to crates/vfs-fuse/src/adapter/cache.rs index cbe9f6ec..723d1efe 100644 --- a/crates/agentfs-fuse/src/adapter/cache.rs +++ b/crates/vfs-fuse/src/adapter/cache.rs @@ -9,13 +9,13 @@ //! (`clippy::await_holding_lock` is deny-by-workspace). use crate::transport::FileAttr; -use agentfs_core::Stats; use parking_lot::{Mutex, MutexGuard}; use std::collections::{HashMap, HashSet}; use std::sync::{ atomic::{AtomicU64, Ordering}, Arc, }; +use vfs_core::Stats; /// Directory entry materialized for readdir/readdirplus cache replies. pub(super) struct CachedDirEntry { diff --git a/crates/agentfs-fuse/src/adapter/config.rs b/crates/vfs-fuse/src/adapter/config.rs similarity index 83% rename from crates/agentfs-fuse/src/adapter/config.rs rename to crates/vfs-fuse/src/adapter/config.rs index 8309c1a1..794266e0 100644 --- a/crates/agentfs-fuse/src/adapter/config.rs +++ b/crates/vfs-fuse/src/adapter/config.rs @@ -7,7 +7,7 @@ use std::env::VarError; use std::time::Duration; -use agentfs_core::EnvReader; +use vfs_core::EnvReader; /// The max size of write requests plus header slack used by the FUSE session /// request buffer. Dispatch auto-sizing must account for one buffer per @@ -33,8 +33,8 @@ impl FuseWorkersDefault { fn resolve(self) -> usize { match self { Self::Auto => workers_from_resource_percent( - env_percent("AGENTFS_FUSE_CPU_PERCENT", DEFAULT_AUTO_PERCENT), - env_percent("AGENTFS_FUSE_MEMORY_PERCENT", DEFAULT_AUTO_PERCENT), + env_percent("VFS_FUSE_CPU_PERCENT", DEFAULT_AUTO_PERCENT), + env_percent("VFS_FUSE_MEMORY_PERCENT", DEFAULT_AUTO_PERCENT), ), } } @@ -69,14 +69,14 @@ impl FuseQueueDefault { /// (warm steady-state reads measured 12.7x native at the old 1s default). /// Within one mount the kernel is coherent for its own operations regardless /// of TTL; the TTL only bounds staleness ACROSS concurrent mounts of the same -/// session DB (`agentfs run --session` from another terminal), which now see +/// session DB (`vfs run --session` from another terminal), which now see /// attribute/namespace changes within 10s. Override with -/// `AGENTFS_FUSE_ENTRY_TTL_MS` / `AGENTFS_FUSE_ATTR_TTL_MS`. +/// `VFS_FUSE_ENTRY_TTL_MS` / `VFS_FUSE_ATTR_TTL_MS`. const DEFAULT_FUSE_POSITIVE_TTL_MS: u64 = 10_000; /// Default kernel TTL for negative dentries. Kept at 1s: a file created by a /// second mount stays invisible to this mount for the negative TTL, and /// lookup-miss caching is the most surprising staleness to debug. Override -/// with `AGENTFS_FUSE_NEG_TTL_MS`. +/// with `VFS_FUSE_NEG_TTL_MS`. const DEFAULT_FUSE_NEG_TTL_MS: u64 = 1000; const DEFAULT_AUTO_PERCENT: u8 = 50; const DEFAULT_QUEUE_MEMORY_PERCENT: u8 = 25; @@ -114,13 +114,13 @@ pub enum DispatchMode { impl DispatchMode { fn from_env() -> Self { - let workers = match std::env::var("AGENTFS_FUSE_WORKERS") { + let workers = match std::env::var("VFS_FUSE_WORKERS") { Ok(value) if value.eq_ignore_ascii_case("serial") => return Self::Serial, Ok(value) if DEFAULT_FUSE_WORKERS.matches(&value) => DEFAULT_FUSE_WORKERS.resolve(), Ok(value) => parse_workers(&value).unwrap_or_else(|| { tracing::warn!( value, - "invalid AGENTFS_FUSE_WORKERS; using serial FUSE dispatch" + "invalid VFS_FUSE_WORKERS; using serial FUSE dispatch" ); 0 }), @@ -128,7 +128,7 @@ impl DispatchMode { Err(VarError::NotUnicode(value)) => { tracing::warn!( ?value, - "invalid AGENTFS_FUSE_WORKERS; using serial FUSE dispatch" + "invalid VFS_FUSE_WORKERS; using serial FUSE dispatch" ); 0 } @@ -139,13 +139,13 @@ impl DispatchMode { } let default_queue_capacity = default_queue_capacity(workers); - let queue_capacity = match std::env::var("AGENTFS_FUSE_QUEUE") { + let queue_capacity = match std::env::var("VFS_FUSE_QUEUE") { Ok(value) if DEFAULT_FUSE_QUEUE.matches(&value) => default_queue_capacity, Ok(value) => parse_queue_capacity(&value, workers).unwrap_or_else(|| { tracing::warn!( value, default_queue_capacity, - "invalid AGENTFS_FUSE_QUEUE; using default queue capacity" + "invalid VFS_FUSE_QUEUE; using default queue capacity" ); default_queue_capacity }), @@ -154,7 +154,7 @@ impl DispatchMode { tracing::warn!( ?value, default_queue_capacity, - "invalid AGENTFS_FUSE_QUEUE; using default queue capacity" + "invalid VFS_FUSE_QUEUE; using default queue capacity" ); default_queue_capacity } @@ -234,15 +234,10 @@ impl UringConfig { fn from_env(reader: EnvReader) -> Self { let default = Self::default(); Self { - enabled: reader.bool("AGENTFS_FUSE_URING", default.enabled), - depth: env_usize_in_range( - "AGENTFS_FUSE_URING_DEPTH", - default.depth, - 1, - MAX_URING_DEPTH, - ), + enabled: reader.bool("VFS_FUSE_URING", default.enabled), + depth: env_usize_in_range("VFS_FUSE_URING_DEPTH", default.depth, 1, MAX_URING_DEPTH), spin_us: env_u64_in_range( - "AGENTFS_FUSE_URING_SPIN_US", + "VFS_FUSE_URING_SPIN_US", default.spin_us, 0, MAX_URING_SPIN_US, @@ -280,45 +275,41 @@ impl FuseConfig { pub(crate) fn from_env() -> Self { let reader = EnvReader::new(); let dispatch_mode = DispatchMode::from_env(); - let drain_on_release = reader.bool("AGENTFS_DRAIN_ON_RELEASE", DEFAULT_DRAIN_ON_RELEASE); - let noflush_requested = reader.bool("AGENTFS_FUSE_NOFLUSH", DEFAULT_FUSE_NOFLUSH); + let drain_on_release = reader.bool("VFS_DRAIN_ON_RELEASE", DEFAULT_DRAIN_ON_RELEASE); + let noflush_requested = reader.bool("VFS_FUSE_NOFLUSH", DEFAULT_FUSE_NOFLUSH); let noflush = noflush_requested && !drain_on_release; if noflush_requested && !noflush { tracing::warn!( - "AGENTFS_FUSE_NOFLUSH disabled: AGENTFS_DRAIN_ON_RELEASE needs the close-time FLUSH" + "VFS_FUSE_NOFLUSH disabled: VFS_DRAIN_ON_RELEASE needs the close-time FLUSH" ); } - let noopen_requested = reader.bool("AGENTFS_FUSE_NOOPEN", DEFAULT_FUSE_NOOPEN); + let noopen_requested = reader.bool("VFS_FUSE_NOOPEN", DEFAULT_FUSE_NOOPEN); let noopen = noopen_requested && !drain_on_release; if noopen_requested && !noopen { tracing::warn!( - "AGENTFS_FUSE_NOOPEN disabled: AGENTFS_DRAIN_ON_RELEASE needs per-handle releases" + "VFS_FUSE_NOOPEN disabled: VFS_DRAIN_ON_RELEASE needs per-handle releases" ); } let config = Self { dispatch_mode, - entry_ttl_ms: env_duration_ms( - "AGENTFS_FUSE_ENTRY_TTL_MS", - DEFAULT_FUSE_POSITIVE_TTL_MS, - ), - attr_ttl_ms: env_duration_ms("AGENTFS_FUSE_ATTR_TTL_MS", DEFAULT_FUSE_POSITIVE_TTL_MS), - neg_ttl_ms: env_duration_ms("AGENTFS_FUSE_NEG_TTL_MS", DEFAULT_FUSE_NEG_TTL_MS), - writeback_cache_requested: reader - .bool("AGENTFS_FUSE_WRITEBACK", DEFAULT_FUSE_WRITEBACK), - keepcache_requested: reader.bool("AGENTFS_FUSE_KEEPCACHE", DEFAULT_FUSE_KEEPCACHE), + entry_ttl_ms: env_duration_ms("VFS_FUSE_ENTRY_TTL_MS", DEFAULT_FUSE_POSITIVE_TTL_MS), + attr_ttl_ms: env_duration_ms("VFS_FUSE_ATTR_TTL_MS", DEFAULT_FUSE_POSITIVE_TTL_MS), + neg_ttl_ms: env_duration_ms("VFS_FUSE_NEG_TTL_MS", DEFAULT_FUSE_NEG_TTL_MS), + writeback_cache_requested: reader.bool("VFS_FUSE_WRITEBACK", DEFAULT_FUSE_WRITEBACK), + keepcache_requested: reader.bool("VFS_FUSE_KEEPCACHE", DEFAULT_FUSE_KEEPCACHE), readdirplus_requested: readdirplus_mode_from_env(), sync_inval: sync_inval_from_env(reader, dispatch_mode), - self_inval: reader.bool("AGENTFS_FUSE_SELF_INVAL", DEFAULT_FUSE_SELF_INVAL), + self_inval: reader.bool("VFS_FUSE_SELF_INVAL", DEFAULT_FUSE_SELF_INVAL), drain_on_release, - drain_on_forget: reader.bool("AGENTFS_DRAIN_ON_FORGET", DEFAULT_DRAIN_ON_FORGET), - flush_inval_always: reader.bool("AGENTFS_FUSE_FLUSH_INVAL", DEFAULT_FUSE_FLUSH_INVAL), + drain_on_forget: reader.bool("VFS_DRAIN_ON_FORGET", DEFAULT_DRAIN_ON_FORGET), + flush_inval_always: reader.bool("VFS_FUSE_FLUSH_INVAL", DEFAULT_FUSE_FLUSH_INVAL), noflush, noopen, - ino_files_cap: env_usize_min("AGENTFS_FUSE_INO_FILES_CAP", DEFAULT_INO_FILES_CAP, 16), - cache_dir_requested: reader.bool("AGENTFS_FUSE_CACHE_DIR", DEFAULT_FUSE_CACHE_DIR), + ino_files_cap: env_usize_min("VFS_FUSE_INO_FILES_CAP", DEFAULT_INO_FILES_CAP, 16), + cache_dir_requested: reader.bool("VFS_FUSE_CACHE_DIR", DEFAULT_FUSE_CACHE_DIR), keepcache_sticky_drop: reader.bool( - "AGENTFS_FUSE_STICKY_KEEPCACHE_DROP", + "VFS_FUSE_STICKY_KEEPCACHE_DROP", DEFAULT_FUSE_STICKY_KEEPCACHE_DROP, ), uring: UringConfig::from_env(reader), @@ -374,29 +365,29 @@ fn emit_kernel_cache_interlock_warnings(config: &FuseConfig) { if config.entry_ttl_ms != 0 || config.attr_ttl_ms != 0 || config.neg_ttl_ms != 0 { tracing::warn!( - "Refusing nonzero FUSE TTLs: kernel entry/attr/negative TTLs require non-serial AGENTFS_FUSE_WORKERS" + "Refusing nonzero FUSE TTLs: kernel entry/attr/negative TTLs require non-serial VFS_FUSE_WORKERS" ); } if config.writeback_cache_requested { tracing::warn!( - "Refusing FUSE writeback cache: AGENTFS_FUSE_WRITEBACK requires non-serial AGENTFS_FUSE_WORKERS" + "Refusing FUSE writeback cache: VFS_FUSE_WRITEBACK requires non-serial VFS_FUSE_WORKERS" ); } if config.keepcache_requested { tracing::warn!( - "Refusing FOPEN_KEEP_CACHE: AGENTFS_FUSE_KEEPCACHE requires non-serial AGENTFS_FUSE_WORKERS" + "Refusing FOPEN_KEEP_CACHE: VFS_FUSE_KEEPCACHE requires non-serial VFS_FUSE_WORKERS" ); } if config.readdirplus_requested != ReaddirPlusMode::Off { tracing::warn!( - "Refusing FUSE readdirplus: readdirplus requires non-serial AGENTFS_FUSE_WORKERS" + "Refusing FUSE readdirplus: readdirplus requires non-serial VFS_FUSE_WORKERS" ); } } fn readdirplus_mode_from_env() -> ReaddirPlusMode { let default = ReaddirPlusMode::default(); - match std::env::var("AGENTFS_FUSE_READDIRPLUS") { + match std::env::var("VFS_FUSE_READDIRPLUS") { Ok(value) if value.eq_ignore_ascii_case("off") || value.eq_ignore_ascii_case("false") @@ -417,7 +408,7 @@ fn readdirplus_mode_from_env() -> ReaddirPlusMode { } Ok(value) => { tracing::warn!( - "Ignoring invalid AGENTFS_FUSE_READDIRPLUS={:?}; using default {:?}", + "Ignoring invalid VFS_FUSE_READDIRPLUS={:?}; using default {:?}", value, default ); @@ -427,7 +418,7 @@ fn readdirplus_mode_from_env() -> ReaddirPlusMode { Err(VarError::NotUnicode(value)) => { tracing::warn!( ?value, - "Ignoring non-Unicode AGENTFS_FUSE_READDIRPLUS; using default" + "Ignoring non-Unicode VFS_FUSE_READDIRPLUS; using default" ); default } @@ -435,10 +426,10 @@ fn readdirplus_mode_from_env() -> ReaddirPlusMode { } fn sync_inval_from_env(reader: EnvReader, dispatch_mode: DispatchMode) -> bool { - let sync_requested = reader.bool("AGENTFS_FUSE_SYNC_INVAL", DEFAULT_FUSE_SYNC_INVAL); + let sync_requested = reader.bool("VFS_FUSE_SYNC_INVAL", DEFAULT_FUSE_SYNC_INVAL); if dispatch_mode.is_serial() && sync_requested { tracing::info!( - "AGENTFS_FUSE_SYNC_INVAL requested with AGENTFS_FUSE_WORKERS=serial; using deferred invalidation to avoid notify/reply deadlock" + "VFS_FUSE_SYNC_INVAL requested with VFS_FUSE_WORKERS=serial; using deferred invalidation to avoid notify/reply deadlock" ); false } else { @@ -579,7 +570,7 @@ fn parse_workers(value: &str) -> Option { if let Some(percent) = parse_percent_suffix(value) { return Some(workers_from_resource_percent( percent, - env_percent("AGENTFS_FUSE_MEMORY_PERCENT", percent), + env_percent("VFS_FUSE_MEMORY_PERCENT", percent), )); } value.parse::().ok().filter(|workers| *workers > 0) @@ -651,7 +642,7 @@ fn default_queue_capacity(workers: usize) -> usize { fn derived_queue_capacity(workers: usize) -> usize { let memory_percent = env_percent( - "AGENTFS_FUSE_QUEUE_MEMORY_PERCENT", + "VFS_FUSE_QUEUE_MEMORY_PERCENT", DEFAULT_QUEUE_MEMORY_PERCENT, ); workers @@ -701,30 +692,30 @@ mod tests { static ENV_LOCK: Mutex<()> = Mutex::new(()); const FUSE_ENV_KEYS: &[&str] = &[ - "AGENTFS_FUSE_WORKERS", - "AGENTFS_FUSE_CPU_PERCENT", - "AGENTFS_FUSE_MEMORY_PERCENT", - "AGENTFS_FUSE_QUEUE", - "AGENTFS_FUSE_QUEUE_MEMORY_PERCENT", - "AGENTFS_FUSE_ENTRY_TTL_MS", - "AGENTFS_FUSE_ATTR_TTL_MS", - "AGENTFS_FUSE_NEG_TTL_MS", - "AGENTFS_FUSE_WRITEBACK", - "AGENTFS_FUSE_KEEPCACHE", - "AGENTFS_FUSE_READDIRPLUS", - "AGENTFS_FUSE_SYNC_INVAL", - "AGENTFS_FUSE_SELF_INVAL", - "AGENTFS_DRAIN_ON_RELEASE", - "AGENTFS_DRAIN_ON_FORGET", - "AGENTFS_FUSE_FLUSH_INVAL", - "AGENTFS_FUSE_NOFLUSH", - "AGENTFS_FUSE_NOOPEN", - "AGENTFS_FUSE_INO_FILES_CAP", - "AGENTFS_FUSE_CACHE_DIR", - "AGENTFS_FUSE_STICKY_KEEPCACHE_DROP", - "AGENTFS_FUSE_URING", - "AGENTFS_FUSE_URING_DEPTH", - "AGENTFS_FUSE_URING_SPIN_US", + "VFS_FUSE_WORKERS", + "VFS_FUSE_CPU_PERCENT", + "VFS_FUSE_MEMORY_PERCENT", + "VFS_FUSE_QUEUE", + "VFS_FUSE_QUEUE_MEMORY_PERCENT", + "VFS_FUSE_ENTRY_TTL_MS", + "VFS_FUSE_ATTR_TTL_MS", + "VFS_FUSE_NEG_TTL_MS", + "VFS_FUSE_WRITEBACK", + "VFS_FUSE_KEEPCACHE", + "VFS_FUSE_READDIRPLUS", + "VFS_FUSE_SYNC_INVAL", + "VFS_FUSE_SELF_INVAL", + "VFS_DRAIN_ON_RELEASE", + "VFS_DRAIN_ON_FORGET", + "VFS_FUSE_FLUSH_INVAL", + "VFS_FUSE_NOFLUSH", + "VFS_FUSE_NOOPEN", + "VFS_FUSE_INO_FILES_CAP", + "VFS_FUSE_CACHE_DIR", + "VFS_FUSE_STICKY_KEEPCACHE_DROP", + "VFS_FUSE_URING", + "VFS_FUSE_URING_DEPTH", + "VFS_FUSE_URING_SPIN_US", ]; struct EnvSnapshot { @@ -789,7 +780,7 @@ mod tests { DispatchMode::Serial => 0, }; - std::env::set_var("AGENTFS_FUSE_WORKERS", DEFAULT_FUSE_WORKERS.as_str()); + std::env::set_var("VFS_FUSE_WORKERS", DEFAULT_FUSE_WORKERS.as_str()); let explicit_config = FuseConfig::from_env(); let explicit_workers = match explicit_config.dispatch_mode { DispatchMode::Parallel { workers, .. } => workers, @@ -800,11 +791,11 @@ mod tests { "documented worker default must use the same parser path as an unset env var" ); - std::env::set_var("AGENTFS_FUSE_WORKERS", "2"); - std::env::remove_var("AGENTFS_FUSE_QUEUE"); + std::env::set_var("VFS_FUSE_WORKERS", "2"); + std::env::remove_var("VFS_FUSE_QUEUE"); let unset_queue_config = FuseConfig::from_env(); - std::env::set_var("AGENTFS_FUSE_QUEUE", DEFAULT_FUSE_QUEUE.as_str()); + std::env::set_var("VFS_FUSE_QUEUE", DEFAULT_FUSE_QUEUE.as_str()); let explicit_queue_config = FuseConfig::from_env(); assert_eq!( @@ -817,13 +808,13 @@ mod tests { fn invalid_readdirplus_warns_and_defaults_on() { let _lock = ENV_LOCK.lock().unwrap(); let _snapshot = EnvSnapshot::capture(FUSE_ENV_KEYS); - std::env::set_var("AGENTFS_FUSE_READDIRPLUS", "bogus"); + std::env::set_var("VFS_FUSE_READDIRPLUS", "bogus"); let config = FuseConfig::from_env(); let cache = config.kernel_cache(); println!( - "invalid AGENTFS_FUSE_READDIRPLUS kept default {:?}", + "invalid VFS_FUSE_READDIRPLUS kept default {:?}", cache.readdirplus_mode ); assert_eq!(cache.readdirplus_mode, ReaddirPlusMode::Always); @@ -833,7 +824,7 @@ mod tests { fn workers_config_feeds_dispatch_and_cache_policy() { let _lock = ENV_LOCK.lock().unwrap(); let _snapshot = EnvSnapshot::capture(FUSE_ENV_KEYS); - std::env::set_var("AGENTFS_FUSE_WORKERS", "25%"); + std::env::set_var("VFS_FUSE_WORKERS", "25%"); let config = FuseConfig::from_env(); let cache = config.kernel_cache(); @@ -868,7 +859,7 @@ mod tests { fn drain_on_release_disables_noopen_and_noflush() { let _lock = ENV_LOCK.lock().unwrap(); let _snapshot = EnvSnapshot::capture(FUSE_ENV_KEYS); - std::env::set_var("AGENTFS_DRAIN_ON_RELEASE", "1"); + std::env::set_var("VFS_DRAIN_ON_RELEASE", "1"); let config = FuseConfig::from_env(); @@ -881,9 +872,9 @@ mod tests { fn combined_kill_switches_are_honored() { let _lock = ENV_LOCK.lock().unwrap(); let _snapshot = EnvSnapshot::capture(FUSE_ENV_KEYS); - std::env::set_var("AGENTFS_FUSE_NOOPEN", "0"); - std::env::set_var("AGENTFS_FUSE_NOFLUSH", "0"); - std::env::set_var("AGENTFS_FUSE_URING", "0"); + std::env::set_var("VFS_FUSE_NOOPEN", "0"); + std::env::set_var("VFS_FUSE_NOFLUSH", "0"); + std::env::set_var("VFS_FUSE_URING", "0"); let config = FuseConfig::from_env(); diff --git a/crates/agentfs-fuse/src/adapter/ino_files.rs b/crates/vfs-fuse/src/adapter/ino_files.rs similarity index 98% rename from crates/agentfs-fuse/src/adapter/ino_files.rs rename to crates/vfs-fuse/src/adapter/ino_files.rs index 4704e985..640ce158 100644 --- a/crates/agentfs-fuse/src/adapter/ino_files.rs +++ b/crates/vfs-fuse/src/adapter/ino_files.rs @@ -1,5 +1,5 @@ use super::write_buffer::{PendingDrain, WriteBuffer}; -use agentfs_core::BoxedFile; +use vfs_core::BoxedFile; /// Per-inode file state for the zero-message-open path. With the kernel's /// `no_open` latched (ENOSYS-OPEN), file ops arrive with `fh=0` and no diff --git a/crates/agentfs-fuse/src/adapter/mod.rs b/crates/vfs-fuse/src/adapter/mod.rs similarity index 98% rename from crates/agentfs-fuse/src/adapter/mod.rs rename to crates/vfs-fuse/src/adapter/mod.rs index dfb2e437..80043d78 100644 --- a/crates/agentfs-fuse/src/adapter/mod.rs +++ b/crates/vfs-fuse/src/adapter/mod.rs @@ -20,12 +20,6 @@ use crate::transport::{ ReplyCreate, ReplyData, ReplyDirectory, ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyOpen, ReplyStatfs, ReplyWrite, Request, }; -use agentfs_core::error::Error as SdkError; -use agentfs_core::fs::{ - KernelCachePolicy, WriteRange, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFSOCK, -}; -use agentfs_core::semantics::Semantics; -use agentfs_core::{BoxedFile, DirEntry, FileSystem, FsError, Stats, TimeChange}; use parking_lot::Mutex; use std::{ collections::{HashMap, HashSet}, @@ -38,6 +32,12 @@ use std::{ time::{Duration, Instant, SystemTime, UNIX_EPOCH}, }; use tokio::runtime::Runtime; +use vfs_core::error::Error as SdkError; +use vfs_core::fs::{ + KernelCachePolicy, WriteRange, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFSOCK, +}; +use vfs_core::semantics::Semantics; +use vfs_core::{BoxedFile, DirEntry, FileSystem, FsError, Stats, TimeChange}; /// Convert an SDK error to an errno code for FUSE replies. /// @@ -55,7 +55,7 @@ fn error_to_errno(e: &SdkError) -> i32 { }; // Debug capture: SdkError Display no longer carries the source cause // (source-only chaining), and this trace needs the full detail. - tracing::debug!(target: "agentfs_errno", error = ?e, errno, "FUSE error reply"); + tracing::debug!(target: "vfs_errno", error = ?e, errno, "FUSE error reply"); errno } @@ -163,7 +163,7 @@ impl MutationAudit { } } -struct AgentFSFuse { +struct VfsFuse { fs: Arc, semantics: Semantics, runtime: Runtime, @@ -194,7 +194,7 @@ struct AgentFSFuse { /// page cache, forcing re-LOOKUP/GETATTR/READ storms (~19.9k notifications /// per codex clone). Server-initiated divergence (deferred commits) stays /// bounded by the entry/attr TTLs exactly as deferred notification latency - /// already was. Set `AGENTFS_FUSE_SELF_INVAL=1` to restore the old + /// already was. Set `VFS_FUSE_SELF_INVAL=1` to restore the old /// notify-on-every-mutation behaviour. Adapter-internal caches (epoch, /// attr/entry/dir maps) are invalidated regardless. self_inval: bool, @@ -203,25 +203,25 @@ struct AgentFSFuse { /// writes, so close-time commits are unnecessary work that serialise the /// clone critical path. Durability is preserved by fsync, the batcher /// timer/bytes/global triggers, and finalize-on-unmount. Set - /// `AGENTFS_DRAIN_ON_RELEASE=1` to restore the legacy commit-on-close. + /// `VFS_DRAIN_ON_RELEASE=1` to restore the legacy commit-on-close. drain_on_release: bool, /// When true, FLUSH invalidates the inode even when the handle had no /// buffered writes. Default false: a read-only close mutates nothing, and /// the unconditional invalidation permanently destroyed keep-cache /// eligibility (the drift guard's `dropped` set is sticky), forcing every /// re-open of an unchanged base file back into FUSE READ round trips. - /// Set `AGENTFS_FUSE_FLUSH_INVAL=1` to restore the old behaviour. + /// Set `VFS_FUSE_FLUSH_INVAL=1` to restore the old behaviour. flush_inval_always: bool, /// When true, `opendir` grants `FOPEN_CACHE_DIR | FOPEN_KEEP_CACHE` so /// warm getdents are served from the kernel page cache. Requires the same /// kernel-cache safety as keep-cache (non-serial workers for notify). - /// Set `AGENTFS_FUSE_CACHE_DIR=0` to disable. + /// Set `VFS_FUSE_CACHE_DIR=0` to disable. cache_dir_enabled: bool, /// When true, force a synchronous SDK drain (SQLite commit) when the /// kernel FORGETs an inode. Default false: a FORGET only drops the /// kernel's reference — pending writes stay readable through the Tier-4 /// overlay and are committed by the batcher timer/bytes triggers, fsync, - /// and finalize-on-unmount. Set `AGENTFS_DRAIN_ON_FORGET=1` to restore + /// and finalize-on-unmount. Set `VFS_DRAIN_ON_FORGET=1` to restore /// the legacy commit-on-forget. drain_on_forget: bool, /// When true (default), the first FLUSH performs its normal drain work @@ -233,13 +233,13 @@ struct AgentFSFuse { /// attr-bearing paths). Halves the per-open/close round trips: measured /// 61.7us -> 31.2us per open/read/close cycle. Forced off when /// `drain_on_release` is set: legacy commit-on-close needs the FLUSH. - /// Set `AGENTFS_FUSE_NOFLUSH=0` to restore close-time FLUSH replies. + /// Set `VFS_FUSE_NOFLUSH=0` to restore close-time FLUSH replies. noflush: bool, - /// Mirror of the SDK's `AGENTFS_KEEPCACHE_DELTA` gate: when off, the + /// Mirror of the SDK's `VFS_KEEPCACHE_DELTA` gate: when off, the /// adapter's cached-stats keep-cache fast path must defer to the SDK /// (only the SDK knows whether an inode is base- or delta-backed). keepcache_delta_enabled: bool, - /// ENOSYS-OPEN, default on; set `AGENTFS_FUSE_NOOPEN=0` to restore + /// ENOSYS-OPEN, default on; set `VFS_FUSE_NOOPEN=0` to restore /// per-handle opens. The open handler only replies ENOSYS once /// `noopen_active` is also latched, which requires the kernel to have /// offered `FUSE_NO_OPEN_SUPPORT` in INIT. @@ -268,7 +268,7 @@ struct AgentFSFuse { writeback_enabled: bool, } -impl Filesystem for AgentFSFuse { +impl Filesystem for VfsFuse { /// Initialize the filesystem and enable performance optimizations. /// /// - Async read: allows the kernel to issue multiple read requests in parallel, @@ -294,7 +294,7 @@ impl Filesystem for AgentFSFuse { capabilities |= FUSE_NO_OPENDIR_SUPPORT; } let _ = config.add_capabilities(capabilities); - // FUSE-over-io_uring, default on (kill switch AGENTFS_FUSE_URING=0): + // FUSE-over-io_uring, default on (kill switch VFS_FUSE_URING=0): // only advertised when the kernel offered it and ring setup works, // since the kernel stalls requests after INIT until the ring queues // register; without the root sysctl fuse.enable_uring=1 the probe @@ -321,7 +321,7 @@ impl Filesystem for AgentFSFuse { self.noopen_active.store(true, Ordering::Release); } else { tracing::warn!( - "AGENTFS_FUSE_NOOPEN=1 but kernel lacks FUSE_NO_OPEN_SUPPORT; opens stay per-handle" + "VFS_FUSE_NOOPEN=1 but kernel lacks FUSE_NO_OPEN_SUPPORT; opens stay per-handle" ); } } @@ -1389,7 +1389,7 @@ impl Filesystem for AgentFSFuse { /// mount invalidate the parent via `invalidate_inode_cache` (kernel /// notification drops the dir pages), and cross-mount divergence is /// bounded by the entry/attr TTLs exactly like file attributes. - /// `AGENTFS_FUSE_CACHE_DIR=0` is the kill switch. + /// `VFS_FUSE_CACHE_DIR=0` is the kill switch. fn opendir(&self, _req: &Request, _ino: u64, _flags: i32, reply: ReplyOpen) { let open_flags = if self.cache_dir_enabled { FOPEN_CACHE_DIR | FOPEN_KEEP_CACHE @@ -1732,7 +1732,7 @@ impl Filesystem for AgentFSFuse { // SDK batcher overlay, but do NOT force a SQLite commit on close. The // overlay keeps reads consistent and the batcher's timer/bytes/global // triggers + finalize-on-unmount provide durability. Set - // AGENTFS_DRAIN_ON_RELEASE=1 to restore the legacy commit-on-close. + // VFS_DRAIN_ON_RELEASE=1 to restore the legacy commit-on-close. let (drain, file) = { let mut open_files = self.open_files.lock(); let Some(open_file) = open_files.get_mut(&fh) else { @@ -1865,7 +1865,7 @@ impl Filesystem for AgentFSFuse { } } -impl Drop for AgentFSFuse { +impl Drop for VfsFuse { fn drop(&mut self) { if let Err(e) = self.flush_all_pending() { tracing::warn!(error = ?e, "FUSE drop failed to flush pending writes"); @@ -1879,7 +1879,7 @@ impl Drop for AgentFSFuse { } } -impl AgentFSFuse { +impl VfsFuse { fn flush_pending_inode(&self, ino: u64) -> Result<(), SdkError> { // Tier Four: only flush per-fh FUSE WriteBuffer state into the SDK // batcher. Do NOT call drain_inode_writes here — the SDK now serves @@ -2263,7 +2263,7 @@ impl AgentFSFuse { /// Invalidation for a kernel-initiated mutation whose FUSE reply already /// carries the fresh attributes for `ino` (setattr's attr reply, link's /// entry reply): adapter-internal caches are always invalidated, but the - /// kernel notification is skipped unless `AGENTFS_FUSE_SELF_INVAL=1` — + /// kernel notification is skipped unless `VFS_FUSE_SELF_INVAL=1` — /// the kernel's own caches are coherent for its own mutations, and the /// notification would purge the attrs and page cache the reply just /// established (see the `self_inval` field). @@ -2302,7 +2302,7 @@ impl AgentFSFuse { /// Entry-cache counterpart of `invalidate_inode_cache_self` for a name the /// kernel just created via this reply (create/mknod/mkdir/symlink/link): /// the entry reply establishes the dentry, so the kernel notification is - /// skipped unless `AGENTFS_FUSE_SELF_INVAL=1`. Adapter entry/negative + /// skipped unless `VFS_FUSE_SELF_INVAL=1`. Adapter entry/negative /// caches are always invalidated. fn invalidate_entry_cache_self(&self, req: &Request, parent: u64, name: &OsStr) { if self.self_inval { @@ -2742,7 +2742,7 @@ fn cached_dir_entry( // Attribute Conversion // ───────────────────────────────────────────────────────────── -/// Fill a `FileAttr` from AgentFS stats. +/// Fill a `FileAttr` from Vfs stats. /// /// Similar to the Linux kernel's `generic_fillattr()`, this converts /// filesystem-specific stat information into the VFS attribute structure. @@ -2857,7 +2857,7 @@ pub fn mount( let fuse_config = FuseConfig::from_env(); let dispatch_mode = fuse_config.dispatch_mode; let uring_config = fuse_config.uring; - let fs = AgentFSFuse::new(fs, runtime, fuse_config, opts.uid, opts.gid); + let fs = VfsFuse::new(fs, runtime, fuse_config, opts.uid, opts.gid); let mount_opts = build_mount_options(&opts)?; let mut session = crate::transport::Session::new( @@ -2913,10 +2913,10 @@ mod tests { build_cached_readdir_entries, build_mount_options, fuse_write_open, readdir_start, FuseMountOptions, }; - use agentfs_core::fs::{DirEntry, Stats, S_IFDIR, S_IFLNK, S_IFREG}; - use agentfs_core::semantics::access; use smallvec::smallvec; use std::path::PathBuf; + use vfs_core::fs::{DirEntry, Stats, S_IFDIR, S_IFLNK, S_IFREG}; + use vfs_core::semantics::access; fn stats(ino: i64, mode: u32) -> Stats { Stats { @@ -2947,11 +2947,11 @@ mod tests { #[test] fn fuse_mount_keeps_kernel_default_permissions_enabled() { let opts = FuseMountOptions { - mountpoint: PathBuf::from("/tmp/agentfs-test"), + mountpoint: PathBuf::from("/tmp/vfs-test"), auto_unmount: false, allow_root: false, allow_other: false, - fsname: "agentfs-test".to_string(), + fsname: "vfs-test".to_string(), uid: None, gid: None, }; diff --git a/crates/agentfs-fuse/src/adapter/write_buffer.rs b/crates/vfs-fuse/src/adapter/write_buffer.rs similarity index 94% rename from crates/agentfs-fuse/src/adapter/write_buffer.rs rename to crates/vfs-fuse/src/adapter/write_buffer.rs index fad56883..ce29a5ba 100644 --- a/crates/agentfs-fuse/src/adapter/write_buffer.rs +++ b/crates/vfs-fuse/src/adapter/write_buffer.rs @@ -1,7 +1,7 @@ -use agentfs_core::fs::WriteRange; -use agentfs_core::{error::Error as SdkError, BoxedFile}; use std::collections::BTreeMap; use tokio::runtime::Runtime; +use vfs_core::fs::WriteRange; +use vfs_core::{error::Error as SdkError, BoxedFile}; /// Threshold at which the FUSE-layer per-fh write coalescer flushes its /// accumulated ranges down to the SDK. Picked at 4x the chunk size so a single @@ -208,13 +208,13 @@ impl WriteBuffer { #[cfg(test)] mod tests { use super::{OpenFile, WriteBuffer}; - use agentfs_core::fs::{Stats, WriteRange, S_IFREG}; - use agentfs_core::{BoxedFile, File}; use std::sync::{ atomic::{AtomicUsize, Ordering}, Arc, Mutex, }; use tokio::runtime::Runtime; + use vfs_core::fs::{Stats, WriteRange, S_IFREG}; + use vfs_core::{BoxedFile, File}; fn ranges(buffer: &WriteBuffer) -> Vec<(u64, Vec)> { buffer @@ -233,30 +233,30 @@ mod tests { #[async_trait::async_trait] impl File for RecordingFile { - async fn pread(&self, _offset: u64, _size: u64) -> agentfs_core::error::Result> { + async fn pread(&self, _offset: u64, _size: u64) -> vfs_core::error::Result> { Ok(Vec::new()) } - async fn pwrite(&self, _offset: u64, _data: &[u8]) -> agentfs_core::error::Result<()> { + async fn pwrite(&self, _offset: u64, _data: &[u8]) -> vfs_core::error::Result<()> { self.pwrite_calls.fetch_add(1, Ordering::SeqCst); Ok(()) } - async fn pwrite_ranges(&self, ranges: Vec) -> agentfs_core::error::Result<()> { + async fn pwrite_ranges(&self, ranges: Vec) -> vfs_core::error::Result<()> { self.pwrite_ranges_calls.fetch_add(1, Ordering::SeqCst); *self.ranges.lock().unwrap() = ranges; Ok(()) } - async fn truncate(&self, _size: u64) -> agentfs_core::error::Result<()> { + async fn truncate(&self, _size: u64) -> vfs_core::error::Result<()> { Ok(()) } - async fn fsync(&self) -> agentfs_core::error::Result<()> { + async fn fsync(&self) -> vfs_core::error::Result<()> { Ok(()) } - async fn fstat(&self) -> agentfs_core::error::Result { + async fn fstat(&self) -> vfs_core::error::Result { Ok(stats(1, S_IFREG | 0o644)) } } diff --git a/crates/agentfs-fuse/src/lib.rs b/crates/vfs-fuse/src/lib.rs similarity index 91% rename from crates/agentfs-fuse/src/lib.rs rename to crates/vfs-fuse/src/lib.rs index e72f0df3..2d1d3375 100644 --- a/crates/agentfs-fuse/src/lib.rs +++ b/crates/vfs-fuse/src/lib.rs @@ -1,6 +1,6 @@ -//! Sealed Linux FUSE mount surface for AgentFS. +//! Sealed Linux FUSE mount surface for Vfs. //! -//! The adopted FUSE transport and AgentFS adapter are private implementation +//! The adopted FUSE transport and Vfs adapter are private implementation //! details. Callers receive only the mount entry point, mount options, and //! session handle needed for lifecycle management (`mount`, //! `FuseMountOptions`, `SessionHandle`). diff --git a/crates/agentfs-fuse/src/telemetry.rs b/crates/vfs-fuse/src/telemetry.rs similarity index 97% rename from crates/agentfs-fuse/src/telemetry.rs rename to crates/vfs-fuse/src/telemetry.rs index 2e26c37a..96c4b0fe 100644 --- a/crates/agentfs-fuse/src/telemetry.rs +++ b/crates/vfs-fuse/src/telemetry.rs @@ -7,14 +7,14 @@ use std::sync::OnceLock; use std::time::Duration; -pub(crate) use agentfs_core::telemetry::{ +pub(crate) use vfs_core::telemetry::{ is_enabled, record_base_fast_inode_invalidation, record_base_fast_open_eligible, record_base_fast_open_keep_cache, record_base_fast_open_rejected, record_base_fast_stale_rejection, record_negative_cache_hit, record_negative_cache_invalidation, record_negative_cache_miss, timer, TimerGuard, }; -agentfs_core::telemetry::define_counters! { +vfs_core::telemetry::define_counters! { pub(crate) static FUSE_COUNTERS: FuseCounters = "fuse" { fuse_callback_count: Counter, fuse_lookup_count: Counter, @@ -79,7 +79,7 @@ agentfs_core::telemetry::define_counters! { } } -agentfs_core::telemetry::define_counters! { +vfs_core::telemetry::define_counters! { pub(crate) static CONFIG_COUNTERS: ConfigCounters = "config" { fuse_workers_configured: Gauge, fuse_readdirplus_mode: Gauge, @@ -95,8 +95,8 @@ static REGISTERED: OnceLock<()> = OnceLock::new(); pub(crate) fn register_sections() { REGISTERED.get_or_init(|| { - agentfs_core::telemetry::Registry::register(&FUSE_COUNTERS); - agentfs_core::telemetry::Registry::register(&CONFIG_COUNTERS); + vfs_core::telemetry::Registry::register(&FUSE_COUNTERS); + vfs_core::telemetry::Registry::register(&CONFIG_COUNTERS); }); } diff --git a/crates/agentfs-fuse/src/transport/channel.rs b/crates/vfs-fuse/src/transport/channel.rs similarity index 100% rename from crates/agentfs-fuse/src/transport/channel.rs rename to crates/vfs-fuse/src/transport/channel.rs diff --git a/crates/agentfs-fuse/src/transport/deferred_notify.rs b/crates/vfs-fuse/src/transport/deferred_notify.rs similarity index 100% rename from crates/agentfs-fuse/src/transport/deferred_notify.rs rename to crates/vfs-fuse/src/transport/deferred_notify.rs diff --git a/crates/agentfs-fuse/src/transport/ll/argument.rs b/crates/vfs-fuse/src/transport/ll/argument.rs similarity index 100% rename from crates/agentfs-fuse/src/transport/ll/argument.rs rename to crates/vfs-fuse/src/transport/ll/argument.rs diff --git a/crates/agentfs-fuse/src/transport/ll/fuse_abi.rs b/crates/vfs-fuse/src/transport/ll/fuse_abi.rs similarity index 100% rename from crates/agentfs-fuse/src/transport/ll/fuse_abi.rs rename to crates/vfs-fuse/src/transport/ll/fuse_abi.rs diff --git a/crates/agentfs-fuse/src/transport/ll/mod.rs b/crates/vfs-fuse/src/transport/ll/mod.rs similarity index 100% rename from crates/agentfs-fuse/src/transport/ll/mod.rs rename to crates/vfs-fuse/src/transport/ll/mod.rs diff --git a/crates/agentfs-fuse/src/transport/ll/notify.rs b/crates/vfs-fuse/src/transport/ll/notify.rs similarity index 100% rename from crates/agentfs-fuse/src/transport/ll/notify.rs rename to crates/vfs-fuse/src/transport/ll/notify.rs diff --git a/crates/agentfs-fuse/src/transport/ll/reply.rs b/crates/vfs-fuse/src/transport/ll/reply.rs similarity index 100% rename from crates/agentfs-fuse/src/transport/ll/reply.rs rename to crates/vfs-fuse/src/transport/ll/reply.rs diff --git a/crates/agentfs-fuse/src/transport/ll/request.rs b/crates/vfs-fuse/src/transport/ll/request.rs similarity index 100% rename from crates/agentfs-fuse/src/transport/ll/request.rs rename to crates/vfs-fuse/src/transport/ll/request.rs diff --git a/crates/agentfs-fuse/src/transport/mnt/fuse_pure.rs b/crates/vfs-fuse/src/transport/mnt/fuse_pure.rs similarity index 99% rename from crates/agentfs-fuse/src/transport/mnt/fuse_pure.rs rename to crates/vfs-fuse/src/transport/mnt/fuse_pure.rs index 83c414fe..21f9cfa5 100644 --- a/crates/agentfs-fuse/src/transport/mnt/fuse_pure.rs +++ b/crates/vfs-fuse/src/transport/mnt/fuse_pure.rs @@ -304,7 +304,7 @@ fn fuse_mount_fusermount( {URING_DRAIN_MOUNT_ATTEMPTS} bounded attempts: a just-closed \ fuse-over-io_uring connection wedges new mounts while the kernel \ drains it (~2s). Retry shortly, avoid rapid unmount-then-mount \ - cycles, or set AGENTFS_FUSE_URING=0 on the mount-owning processes." + cycles, or set VFS_FUSE_URING=0 on the mount-owning processes." ), )); } @@ -460,7 +460,7 @@ fn fuse_mount_sys(mountpoint: &OsStr, options: &[MountOption]) -> Result Session { let active_dispatches = active_dispatches.clone(); worker_handles.push( thread::Builder::new() - .name(format!("agentfs-fuse-worker-{worker_id}")) + .name(format!("vfs-fuse-worker-{worker_id}")) .spawn(move || { while let Ok(queued) = rx.recv() { queue_depth.fetch_sub(1, Ordering::AcqRel); diff --git a/crates/agentfs-fuse/src/transport/uring.rs b/crates/vfs-fuse/src/transport/uring.rs similarity index 99% rename from crates/agentfs-fuse/src/transport/uring.rs rename to crates/vfs-fuse/src/transport/uring.rs index 7d5a8a01..08f578fb 100644 --- a/crates/agentfs-fuse/src/transport/uring.rs +++ b/crates/vfs-fuse/src/transport/uring.rs @@ -636,7 +636,7 @@ pub(crate) fn start_uring_queues( return; } match std::thread::Builder::new() - .name(format!("agentfs-fuse-uring-{qid}")) + .name(format!("vfs-fuse-uring-{qid}")) .spawn(move || { let queue_config = QueueThreadConfig { qid: qid as u16, @@ -661,7 +661,7 @@ pub(crate) fn start_uring_queues( } }; match std::thread::Builder::new() - .name("agentfs-fuse-uring-start".into()) + .name("vfs-fuse-uring-start".into()) .spawn(starter) { Ok(handle) => control.set_starter(handle), diff --git a/crates/agentfs-mount/Cargo.toml b/crates/vfs-mount/Cargo.toml similarity index 68% rename from crates/agentfs-mount/Cargo.toml rename to crates/vfs-mount/Cargo.toml index 13750aa4..4af47d32 100644 --- a/crates/agentfs-mount/Cargo.toml +++ b/crates/vfs-mount/Cargo.toml @@ -1,16 +1,16 @@ [package] -name = "agentfs-mount" +name = "vfs-mount" version.workspace = true edition.workspace = true -description = "Mount lifecycle and supervision for AgentFS" +description = "Mount lifecycle and supervision for Vfs" license.workspace = true [lints] workspace = true [dependencies] -agentfs-core = { workspace = true } -agentfs-nfs = { workspace = true } +vfs-core = { workspace = true } +vfs-nfs = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } libc = { workspace = true } @@ -19,7 +19,7 @@ tokio-util = { workspace = true } tracing = { workspace = true } [target.'cfg(target_os = "linux")'.dependencies] -agentfs-fuse = { workspace = true } +vfs-fuse = { workspace = true } [dev-dependencies] tempfile = { workspace = true } diff --git a/crates/agentfs-mount/src/daemon.rs b/crates/vfs-mount/src/daemon.rs similarity index 99% rename from crates/agentfs-mount/src/daemon.rs rename to crates/vfs-mount/src/daemon.rs index 10c24594..b93cd313 100644 --- a/crates/agentfs-mount/src/daemon.rs +++ b/crates/vfs-mount/src/daemon.rs @@ -33,7 +33,7 @@ const MAX_ERROR_MSG_LEN: usize = 4096; /// constructed on this thread but not entered, evades it, and tokio's /// public API offers no process-wide probe. Callers must therefore fork /// first and build their runtime inside `daemon_fn`, dropping any -/// short-lived pre-check runtimes beforehand (see `agentfs mount`, whose +/// short-lived pre-check runtimes beforehand (see `vfs mount`, whose /// two call sites both follow this shape). pub fn daemonize(daemon_fn: F, ready_check: R, timeout: Duration) -> Result<()> where diff --git a/crates/agentfs-mount/src/fuse.rs b/crates/vfs-mount/src/fuse.rs similarity index 81% rename from crates/agentfs-mount/src/fuse.rs rename to crates/vfs-mount/src/fuse.rs index e448ce99..b269f27d 100644 --- a/crates/agentfs-mount/src/fuse.rs +++ b/crates/vfs-mount/src/fuse.rs @@ -45,10 +45,10 @@ pub(super) fn unmount_fuse(mountpoint: &Path, lazy: bool) -> Result<()> { /// Internal FUSE mount implementation. pub(super) fn mount_fuse( - fs: Arc, + fs: Arc, opts: MountOpts, ) -> Result { - use agentfs_fuse::FuseMountOptions; + use vfs_fuse::FuseMountOptions; let fuse_opts = FuseMountOptions { mountpoint: opts.mountpoint.clone(), @@ -64,10 +64,10 @@ pub(super) fn mount_fuse( let timeout = opts.timeout; let lazy_unmount = opts.lazy_unmount; - let fs_arc: Arc = Arc::new(ReadWriteLaneFsAdapter::new(fs)); + let fs_arc: Arc = Arc::new(ReadWriteLaneFsAdapter::new(fs)); let rt = crate::get_runtime(); - let mut fuse_session = agentfs_fuse::mount(fs_arc, fuse_opts, rt)?; + let mut fuse_session = vfs_fuse::mount(fs_arc, fuse_opts, rt)?; // The readiness probe stats the mountpoint, which can block indefinitely // when a fresh fuse-over-io_uring mount races the kernel-side drain of a @@ -77,7 +77,7 @@ pub(super) fn mount_fuse( let (ready_tx, ready_rx) = std::sync::mpsc::channel(); let probe_mountpoint = mountpoint.clone(); let _ = std::thread::Builder::new() - .name("agentfs-mount-ready".into()) + .name("vfs-mount-ready".into()) .spawn(move || { let _ = ready_tx.send(wait_for_mount(&probe_mountpoint, timeout)); }); @@ -90,7 +90,7 @@ pub(super) fn mount_fuse( // block, and the caller is owed a bounded, clear failure. let (teardown_tx, teardown_rx) = std::sync::mpsc::channel(); let _ = std::thread::Builder::new() - .name("agentfs-mount-teardown".into()) + .name("vfs-mount-teardown".into()) .spawn(move || { if let Err(error) = fuse_session.unmount() { tracing::warn!(%error, "failed to unmount FUSE session after readiness timeout"); @@ -105,7 +105,7 @@ pub(super) fn mount_fuse( "FUSE mount at {} did not become ready within {:?}. On kernels with \ fuse-over-io_uring enabled, a mount racing the drain of a just-closed \ FUSE connection can stall; retry shortly, avoid rapid \ - unmount-then-mount cycles, or set AGENTFS_FUSE_URING=0.", + unmount-then-mount cycles, or set VFS_FUSE_URING=0.", mountpoint.display(), timeout ); @@ -124,7 +124,7 @@ pub(super) fn mount_fuse( /// Adapter that admits read operations concurrently while serializing /// mutations before they reach the backend filesystem. struct ReadWriteLaneFsAdapter { - inner: Arc, + inner: Arc, lanes: RwLock<()>, active_reads: AtomicU64, } @@ -147,7 +147,7 @@ impl Drop for ReadLaneGuard<'_> { } impl ReadWriteLaneFsAdapter { - fn new(inner: Arc) -> Self { + fn new(inner: Arc) -> Self { Self { inner, lanes: RwLock::new(()), @@ -180,12 +180,12 @@ impl ReadWriteLaneFsAdapter { } #[async_trait::async_trait] -impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { +impl vfs_core::FileSystem for ReadWriteLaneFsAdapter { async fn lookup( &self, parent_ino: i64, name: &str, - ) -> std::result::Result, agentfs_core::error::Error> { + ) -> std::result::Result, vfs_core::error::Error> { let _lane = self.lock_read_fs().await; self.inner.lookup(parent_ino, name).await } @@ -193,7 +193,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { async fn getattr( &self, ino: i64, - ) -> std::result::Result, agentfs_core::error::Error> { + ) -> std::result::Result, vfs_core::error::Error> { let _lane = self.lock_read_fs().await; self.inner.getattr(ino).await } @@ -201,7 +201,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { async fn readlink( &self, ino: i64, - ) -> std::result::Result, agentfs_core::error::Error> { + ) -> std::result::Result, vfs_core::error::Error> { let _lane = self.lock_read_fs().await; self.inner.readlink(ino).await } @@ -209,7 +209,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { async fn readdir( &self, ino: i64, - ) -> std::result::Result>, agentfs_core::error::Error> { + ) -> std::result::Result>, vfs_core::error::Error> { let _lane = self.lock_read_fs().await; self.inner.readdir(ino).await } @@ -217,16 +217,12 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { async fn readdir_plus( &self, ino: i64, - ) -> std::result::Result>, agentfs_core::error::Error> { + ) -> std::result::Result>, vfs_core::error::Error> { let _lane = self.lock_read_fs().await; self.inner.readdir_plus(ino).await } - async fn chmod( - &self, - ino: i64, - mode: u32, - ) -> std::result::Result<(), agentfs_core::error::Error> { + async fn chmod(&self, ino: i64, mode: u32) -> std::result::Result<(), vfs_core::error::Error> { let _lane = self.lock_write_fs().await; self.inner.chmod(ino, mode).await } @@ -236,7 +232,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { ino: i64, uid: Option, gid: Option, - ) -> std::result::Result<(), agentfs_core::error::Error> { + ) -> std::result::Result<(), vfs_core::error::Error> { let _lane = self.lock_write_fs().await; self.inner.chown(ino, uid, gid).await } @@ -244,9 +240,9 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { async fn utimens( &self, ino: i64, - atime: agentfs_core::TimeChange, - mtime: agentfs_core::TimeChange, - ) -> std::result::Result<(), agentfs_core::error::Error> { + atime: vfs_core::TimeChange, + mtime: vfs_core::TimeChange, + ) -> std::result::Result<(), vfs_core::error::Error> { let _lane = self.lock_write_fs().await; self.inner.utimens(ino, atime, mtime).await } @@ -255,7 +251,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { &self, ino: i64, flags: i32, - ) -> std::result::Result { + ) -> std::result::Result { match classify_open(flags) { FuseFsOperationClass::PureRead => { let _lane = self.lock_read_fs().await; @@ -272,7 +268,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { &self, ino: i64, flags: i32, - ) -> std::result::Result, agentfs_core::error::Error> { + ) -> std::result::Result, vfs_core::error::Error> { match classify_open(flags) { FuseFsOperationClass::PureRead => { let _lane = self.lock_read_fs().await; @@ -289,7 +285,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { self.inner.delta_keep_cache_fast_path() } - fn kernel_cache_policy(&self, ino: i64) -> agentfs_core::fs::KernelCachePolicy { + fn kernel_cache_policy(&self, ino: i64) -> vfs_core::fs::KernelCachePolicy { self.inner.kernel_cache_policy(ino) } @@ -300,7 +296,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { mode: u32, uid: u32, gid: u32, - ) -> std::result::Result { + ) -> std::result::Result { let _lane = self.lock_write_fs().await; self.inner.mkdir(parent_ino, name, mode, uid, gid).await } @@ -312,10 +308,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { mode: u32, uid: u32, gid: u32, - ) -> std::result::Result< - (agentfs_core::Stats, agentfs_core::BoxedFile), - agentfs_core::error::Error, - > { + ) -> std::result::Result<(vfs_core::Stats, vfs_core::BoxedFile), vfs_core::error::Error> { let _lane = self.lock_write_fs().await; self.inner .create_file(parent_ino, name, mode, uid, gid) @@ -330,7 +323,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { rdev: u64, uid: u32, gid: u32, - ) -> std::result::Result { + ) -> std::result::Result { let _lane = self.lock_write_fs().await; self.inner .mknod(parent_ino, name, mode, rdev, uid, gid) @@ -344,7 +337,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { target: &str, uid: u32, gid: u32, - ) -> std::result::Result { + ) -> std::result::Result { let _lane = self.lock_write_fs().await; self.inner.symlink(parent_ino, name, target, uid, gid).await } @@ -353,7 +346,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { &self, parent_ino: i64, name: &str, - ) -> std::result::Result<(), agentfs_core::error::Error> { + ) -> std::result::Result<(), vfs_core::error::Error> { let _lane = self.lock_write_fs().await; self.inner.unlink(parent_ino, name).await } @@ -362,7 +355,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { &self, parent_ino: i64, name: &str, - ) -> std::result::Result<(), agentfs_core::error::Error> { + ) -> std::result::Result<(), vfs_core::error::Error> { let _lane = self.lock_write_fs().await; self.inner.rmdir(parent_ino, name).await } @@ -372,7 +365,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { ino: i64, newparent_ino: i64, newname: &str, - ) -> std::result::Result { + ) -> std::result::Result { let _lane = self.lock_write_fs().await; self.inner.link(ino, newparent_ino, newname).await } @@ -383,7 +376,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { oldname: &str, newparent_ino: i64, newname: &str, - ) -> std::result::Result<(), agentfs_core::error::Error> { + ) -> std::result::Result<(), vfs_core::error::Error> { let _lane = self.lock_write_fs().await; self.inner .rename(oldparent_ino, oldname, newparent_ino, newname) @@ -392,7 +385,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { async fn statfs( &self, - ) -> std::result::Result { + ) -> std::result::Result { let _lane = self.lock_read_fs().await; self.inner.statfs().await } @@ -400,17 +393,17 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { async fn drain_inode_writes( &self, ino: i64, - ) -> std::result::Result<(), agentfs_core::error::Error> { + ) -> std::result::Result<(), vfs_core::error::Error> { let _lane = self.lock_write_fs().await; self.inner.drain_inode_writes(ino).await } - async fn drain_all(&self) -> std::result::Result<(), agentfs_core::error::Error> { + async fn drain_all(&self) -> std::result::Result<(), vfs_core::error::Error> { let _lane = self.lock_write_fs().await; self.inner.drain_all().await } - async fn finalize(&self) -> std::result::Result<(), agentfs_core::error::Error> { + async fn finalize(&self) -> std::result::Result<(), vfs_core::error::Error> { let _lane = self.lock_write_fs().await; self.inner.finalize().await } @@ -419,7 +412,7 @@ impl agentfs_core::FileSystem for ReadWriteLaneFsAdapter { &self, ino: i64, nlookup: u64, - ) -> std::result::Result<(), agentfs_core::error::Error> { + ) -> std::result::Result<(), vfs_core::error::Error> { let _lane = self.lock_read_fs().await; self.inner.retain_lookup(ino, nlookup).await } diff --git a/crates/agentfs-mount/src/lib.rs b/crates/vfs-mount/src/lib.rs similarity index 95% rename from crates/agentfs-mount/src/lib.rs rename to crates/vfs-mount/src/lib.rs index 3187a8ac..ac56f835 100644 --- a/crates/agentfs-mount/src/lib.rs +++ b/crates/vfs-mount/src/lib.rs @@ -1,4 +1,4 @@ -//! Mount lifecycle infrastructure for AgentFS. +//! Mount lifecycle infrastructure for Vfs. //! //! This module provides a unified mount API that abstracts over FUSE and NFS backends. //! The `mount_fs()` function returns a `MountHandle` whose explicit `unmount` @@ -23,7 +23,7 @@ //! # Example //! //! ```ignore -//! use agentfs_mount::{mount_fs, Backend, MountOpts}; +//! use vfs_mount::{mount_fs, Backend, MountOpts}; //! //! let opts = MountOpts::new(PathBuf::from("/mnt/agent"), Backend::Fuse); //! let handle = mount_fs(Arc::new(my_fs), opts).await?; @@ -118,7 +118,7 @@ impl MountOpts { Self { mountpoint, backend, - fsname: "agentfs".to_string(), + fsname: "vfs".to_string(), uid: None, gid: None, allow_other: false, @@ -136,7 +136,7 @@ impl Default for MountOpts { } } -/// Options for serving AgentFS over NFS without mounting it locally. +/// Options for serving Vfs over NFS without mounting it locally. #[derive(Debug, Clone)] pub struct NfsServerOptions { /// IP address or hostname to bind. @@ -163,7 +163,7 @@ impl Default for NfsServerOptions { /// Handle for a standalone NFS server owned by the mount lifecycle crate. pub struct NfsServerHandle { - inner: agentfs_nfs::ServerHandle, + inner: vfs_nfs::ServerHandle, } impl NfsServerHandle { @@ -190,13 +190,13 @@ impl NfsServerHandle { /// Serve a filesystem over NFS through the mount lifecycle crate's sealed edge. pub async fn serve_nfs( - fs: Arc, + fs: Arc, opts: NfsServerOptions, ) -> Result { let shutdown = tokio_util::sync::CancellationToken::new(); - let inner = agentfs_nfs::serve( + let inner = vfs_nfs::serve( fs, - agentfs_nfs::NfsServeOptions::new(opts.bind, opts.port), + vfs_nfs::NfsServeOptions::new(opts.bind, opts.port), shutdown, ) .await?; @@ -218,10 +218,10 @@ pub struct MountHandle { pub(crate) enum MountHandleInner { #[cfg(target_os = "linux")] Fuse { - session: Option, + session: Option, }, Nfs { - server_handle: Option, + server_handle: Option, }, } @@ -237,11 +237,11 @@ impl MountHandle { #[cfg(target_os = "linux")] MountHandleInner::Fuse { session } => session .as_ref() - .map(agentfs_fuse::SessionHandle::is_finished) + .map(vfs_fuse::SessionHandle::is_finished) .unwrap_or(true), MountHandleInner::Nfs { server_handle } => server_handle .as_ref() - .map(agentfs_nfs::ServerHandle::is_finished) + .map(vfs_nfs::ServerHandle::is_finished) .unwrap_or(true), } } @@ -455,10 +455,7 @@ fn unmount(mountpoint: &Path, backend: Backend, lazy: bool) -> Result<()> { /// Returns a handle that automatically unmounts when dropped. /// The filesystem must be wrapped in `Arc`. #[cfg(target_os = "linux")] -pub async fn mount_fs( - fs: Arc, - opts: MountOpts, -) -> Result { +pub async fn mount_fs(fs: Arc, opts: MountOpts) -> Result { match opts.backend { Backend::Fuse => fuse::mount_fuse(fs, opts), Backend::Nfs => nfs::mount_nfs(fs, opts).await, @@ -467,10 +464,7 @@ pub async fn mount_fs( /// Mount a filesystem with the given options (macOS version). #[cfg(target_os = "macos")] -pub async fn mount_fs( - fs: Arc, - opts: MountOpts, -) -> Result { +pub async fn mount_fs(fs: Arc, opts: MountOpts) -> Result { match opts.backend { Backend::Fuse => { anyhow::bail!( diff --git a/crates/agentfs-mount/src/nfs.rs b/crates/vfs-mount/src/nfs.rs similarity index 98% rename from crates/agentfs-mount/src/nfs.rs rename to crates/vfs-mount/src/nfs.rs index cc34a839..b5b56b04 100644 --- a/crates/agentfs-mount/src/nfs.rs +++ b/crates/vfs-mount/src/nfs.rs @@ -5,7 +5,7 @@ use std::path::Path; use std::process::Command; use std::sync::Arc; -use agentfs_nfs::{serve, NfsServeOptions}; +use vfs_nfs::{serve, NfsServeOptions}; use super::{Backend, MountHandle, MountHandleInner, MountOpts}; @@ -76,7 +76,7 @@ pub(super) fn unmount_nfs(mountpoint: &Path, lazy: bool) -> Result<()> { /// Internal NFS mount implementation. pub(super) async fn mount_nfs( - fs: Arc, + fs: Arc, opts: MountOpts, ) -> Result { use tokio_util::sync::CancellationToken; diff --git a/crates/agentfs-mount/src/supervise.rs b/crates/vfs-mount/src/supervise.rs similarity index 99% rename from crates/agentfs-mount/src/supervise.rs rename to crates/vfs-mount/src/supervise.rs index b6614212..744cb465 100644 --- a/crates/agentfs-mount/src/supervise.rs +++ b/crates/vfs-mount/src/supervise.rs @@ -77,7 +77,7 @@ async fn run_supervised_with_opts( /// Run arbitrary async work with a mounted filesystem and always unmount afterward. /// /// This covers mount-owning flows whose lifetime is not represented by one -/// child process, such as `agentfs clone`'s mount plus bulk-import pipeline. +/// child process, such as `vfs clone`'s mount plus bulk-import pipeline. pub async fn run_supervised_task( handle: MountHandle, task: F, @@ -505,7 +505,7 @@ mod tests { "printf '%s\\n' \"$$\" > \"$1\"; : > \"$2\"; \ while :; do sleep 1; done", ) - .arg("agentfs-darwin-supervision-child") + .arg("vfs-darwin-supervision-child") .arg(&child_pid_path) .arg(&child_ready_path) .current_dir(&mountpoint); @@ -593,7 +593,7 @@ mod tests { : > \"$3\"; \ wait", ) - .arg("agentfs-supervise-group") + .arg("vfs-supervise-group") .arg(&child_pid_path) .arg(&grandchild_pid_path) .arg(&ready_path); diff --git a/crates/agentfs-nfs/Cargo.toml b/crates/vfs-nfs/Cargo.toml similarity index 85% rename from crates/agentfs-nfs/Cargo.toml rename to crates/vfs-nfs/Cargo.toml index 8c2187e2..b2a21b44 100644 --- a/crates/agentfs-nfs/Cargo.toml +++ b/crates/vfs-nfs/Cargo.toml @@ -1,15 +1,15 @@ [package] -name = "agentfs-nfs" +name = "vfs-nfs" version.workspace = true edition.workspace = true -description = "NFS adapter for AgentFS" +description = "NFS adapter for Vfs" license.workspace = true [lints] workspace = true [dependencies] -agentfs-core = { workspace = true } +vfs-core = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } byteorder = { workspace = true } diff --git a/crates/agentfs-nfs/src/adapter.rs b/crates/vfs-nfs/src/adapter.rs similarity index 97% rename from crates/agentfs-nfs/src/adapter.rs rename to crates/vfs-nfs/src/adapter.rs index c3bbb1ce..b64e2624 100644 --- a/crates/agentfs-nfs/src/adapter.rs +++ b/crates/vfs-nfs/src/adapter.rs @@ -1,7 +1,7 @@ -//! NFS server adapter for AgentFS. +//! NFS server adapter for Vfs. //! -//! This module implements nfsserve's NFSFileSystem trait on top of AgentFS's -//! FileSystem trait, enabling systems to mount AgentFS via NFS without requiring +//! This module implements nfsserve's NFSFileSystem trait on top of Vfs's +//! FileSystem trait, enabling systems to mount Vfs via NFS without requiring //! FUSE or other system extensions. use std::sync::Arc; @@ -12,15 +12,15 @@ use crate::server::nfs::{ set_gid3, set_mode3, set_mtime, set_size3, set_uid3, specdata3, }; use crate::server::vfs::{auth_unix, DirEntry, NFSFileSystem, ReadDirResult}; -use agentfs_core::error::Error as SdkError; -use agentfs_core::fs::FsError; -use agentfs_core::semantics::{AckDurability, Authority, Semantics}; -use agentfs_core::{ +use async_trait::async_trait; +use uuid::Uuid; +use vfs_core::error::Error as SdkError; +use vfs_core::fs::FsError; +use vfs_core::semantics::{AckDurability, Authority, Semantics}; +use vfs_core::{ FileSystem, Stats, TimeChange, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, }; -use async_trait::async_trait; -use uuid::Uuid; /// Root directory inode number const ROOT_INO: fileid3 = 1; @@ -60,7 +60,7 @@ fn error_to_nfsstat(e: SdkError) -> nfsstat3 { } } -/// NFS adapter that wraps an AgentFS FileSystem. +/// NFS adapter that wraps a Vfs FileSystem. pub(crate) struct AgentNFS { /// The underlying concurrency-safe filesystem. fs: Arc, @@ -126,7 +126,7 @@ impl AgentNFS { Ok((id, Some(token))) } - /// Convert AgentFS Stats to NFS fattr3. + /// Convert Vfs Stats to NFS fattr3. fn stats_to_fattr(&self, stats: &Stats) -> fattr3 { let ftype = match stats.mode & S_IFMT { S_IFREG => ftype3::NF3REG, @@ -687,15 +687,15 @@ impl NFSFileSystem for AgentNFS { mod tests { use super::*; use crate::server::vfs::NFSFileSystem; - use agentfs_core::fs::AgentFS as CoreAgentFS; - use agentfs_core::{AgentFS, AgentFSOptions, HostFS, OverlayFS}; use std::sync::Arc; use tempfile::TempDir; + use vfs_core::fs::Vfs as CoreVfs; + use vfs_core::{HostFS, OverlayFS, Vfs, VfsOptions}; async fn test_nfs() -> AgentNFS { - let agent = AgentFS::open(AgentFSOptions::ephemeral()) + let agent = Vfs::open(VfsOptions::ephemeral()) .await - .expect("ephemeral AgentFS opens"); + .expect("ephemeral Vfs opens"); let fs: Arc = Arc::new(agent.fs); AgentNFS::new(fs) } @@ -708,9 +708,9 @@ mod tests { let delta_dir = tempfile::tempdir().expect("delta tempdir is created"); let db_path = delta_dir.path().join("delta.db"); - let delta = CoreAgentFS::new(db_path.to_str().expect("delta DB path is UTF-8")) + let delta = CoreVfs::new(db_path.to_str().expect("delta DB path is UTF-8")) .await - .expect("delta AgentFS opens"); + .expect("delta Vfs opens"); let overlay = OverlayFS::new(base, delta); overlay .init(base_dir.path().to_str().expect("base path is UTF-8")) diff --git a/crates/agentfs-nfs/src/lib.rs b/crates/vfs-nfs/src/lib.rs similarity index 97% rename from crates/agentfs-nfs/src/lib.rs rename to crates/vfs-nfs/src/lib.rs index 8ef0c112..f2fdd9ba 100644 --- a/crates/agentfs-nfs/src/lib.rs +++ b/crates/vfs-nfs/src/lib.rs @@ -1,6 +1,6 @@ -//! Sealed NFS serve surface for AgentFS. +//! Sealed NFS serve surface for Vfs. //! -//! The adopted NFS protocol server and AgentFS adapter are private +//! The adopted NFS protocol server and Vfs adapter are private //! implementation details. Callers receive only the serve entry point, serve //! options, and server handle needed for lifecycle management (`serve`, //! `NfsServeOptions`, `ServerHandle`). @@ -8,7 +8,7 @@ //! Owned invariants: //! //! - Server-side enforcement: permission checks go through the shared -//! `agentfs_core::semantics` access implementation, and write authority is +//! `vfs_core::semantics` access implementation, and write authority is //! granted only via handle-table tokens (LRU-bounded, invalidated on //! unlink/rename) — local processes reaching the TCP port are inside the //! threat model. @@ -26,9 +26,9 @@ mod server; use std::net::SocketAddr; use std::sync::Arc; -use agentfs_core::FileSystem; use tokio::task::JoinHandle; use tokio_util::sync::CancellationToken; +use vfs_core::FileSystem; use adapter::AgentNFS; use server::tcp::{NFSTcp, NFSTcpListener}; diff --git a/crates/agentfs-nfs/src/server/context.rs b/crates/vfs-nfs/src/server/context.rs similarity index 100% rename from crates/agentfs-nfs/src/server/context.rs rename to crates/vfs-nfs/src/server/context.rs diff --git a/crates/agentfs-nfs/src/server/mod.rs b/crates/vfs-nfs/src/server/mod.rs similarity index 100% rename from crates/agentfs-nfs/src/server/mod.rs rename to crates/vfs-nfs/src/server/mod.rs diff --git a/crates/agentfs-nfs/src/server/mount.rs b/crates/vfs-nfs/src/server/mount.rs similarity index 100% rename from crates/agentfs-nfs/src/server/mount.rs rename to crates/vfs-nfs/src/server/mount.rs diff --git a/crates/agentfs-nfs/src/server/mount_handlers.rs b/crates/vfs-nfs/src/server/mount_handlers.rs similarity index 100% rename from crates/agentfs-nfs/src/server/mount_handlers.rs rename to crates/vfs-nfs/src/server/mount_handlers.rs diff --git a/crates/agentfs-nfs/src/server/nfs.rs b/crates/vfs-nfs/src/server/nfs.rs similarity index 100% rename from crates/agentfs-nfs/src/server/nfs.rs rename to crates/vfs-nfs/src/server/nfs.rs diff --git a/crates/agentfs-nfs/src/server/nfs_handlers.rs b/crates/vfs-nfs/src/server/nfs_handlers.rs similarity index 99% rename from crates/agentfs-nfs/src/server/nfs_handlers.rs rename to crates/vfs-nfs/src/server/nfs_handlers.rs index 9cbc2b4f..4b8fc458 100644 --- a/crates/agentfs-nfs/src/server/nfs_handlers.rs +++ b/crates/vfs-nfs/src/server/nfs_handlers.rs @@ -4,13 +4,13 @@ use super::nfs; use super::permissions; use super::rpc::*; use super::xdr::*; -use agentfs_core::fs::MAX_NAME_LEN; -use agentfs_core::semantics::{access, AckDurability}; use byteorder::{ReadBytesExt, WriteBytesExt}; use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::cast::FromPrimitive; use std::io::{Read, Write}; use tracing::{debug, error, trace, warn}; +use vfs_core::fs::MAX_NAME_LEN; +use vfs_core::semantics::{access, AckDurability}; /* program NFS_PROGRAM { version NFS_V3 { @@ -2834,11 +2834,11 @@ mod tests { use crate::server::rpc::{accept_body, accepted_reply, reply_body, rpc_body, rpc_msg}; use crate::server::transaction_tracker::{TransactionTracker, DEFAULT_REPLY_CACHE_CAPACITY}; use crate::server::vfs::NFSFileSystem; - use agentfs_core::fs::MAX_NAME_LEN; - use agentfs_core::{AgentFS as AgentSdk, AgentFSOptions, FileSystem}; use std::io::Cursor; use std::path::Path; use std::sync::Arc; + use vfs_core::fs::MAX_NAME_LEN; + use vfs_core::{FileSystem, Vfs as VfsSdk, VfsOptions}; const TEST_UID: u32 = 1000; const TEST_GID: u32 = 1000; @@ -2871,23 +2871,23 @@ mod tests { } } - async fn test_context() -> (RPCContext, agentfs_core::fs::AgentFS) { - let agent = AgentSdk::open(AgentFSOptions::ephemeral()) + async fn test_context() -> (RPCContext, vfs_core::fs::Vfs) { + let agent = VfsSdk::open(VfsOptions::ephemeral()) .await - .expect("open ephemeral AgentFS"); + .expect("open ephemeral Vfs"); test_context_from_agent(agent).await } - async fn test_context_with_db_path(db_path: &Path) -> (RPCContext, agentfs_core::fs::AgentFS) { - let agent = AgentSdk::open(AgentFSOptions::with_path( + async fn test_context_with_db_path(db_path: &Path) -> (RPCContext, vfs_core::fs::Vfs) { + let agent = VfsSdk::open(VfsOptions::with_path( db_path.to_str().expect("test DB path is UTF-8"), )) .await - .expect("open file-backed AgentFS"); + .expect("open file-backed Vfs"); test_context_from_agent(agent).await } - async fn test_context_from_agent(agent: AgentSdk) -> (RPCContext, agentfs_core::fs::AgentFS) { + async fn test_context_from_agent(agent: VfsSdk) -> (RPCContext, vfs_core::fs::Vfs) { agent .fs .chmod(1, 0o777) @@ -2914,11 +2914,11 @@ mod tests { } fn force_long_write_batcher_window() { - std::env::set_var("AGENTFS_FUSE_WRITEBACK", "1"); - std::env::set_var("AGENTFS_OVERLAY_READS", "1"); - std::env::set_var("AGENTFS_BATCH_MS", "60000"); - std::env::set_var("AGENTFS_BATCH_BYTES", "1048576"); - std::env::set_var("AGENTFS_BATCH_GLOBAL_BYTES", "10485760"); + std::env::set_var("VFS_FUSE_WRITEBACK", "1"); + std::env::set_var("VFS_OVERLAY_READS", "1"); + std::env::set_var("VFS_BATCH_MS", "60000"); + std::env::set_var("VFS_BATCH_BYTES", "1048576"); + std::env::set_var("VFS_BATCH_GLOBAL_BYTES", "10485760"); } fn parse_rpc_success(cursor: &mut Cursor>) { @@ -3617,7 +3617,7 @@ mod tests { ); } - async fn read_file(fs: &agentfs_core::fs::AgentFS, name: &str, len: u64) -> Vec { + async fn read_file(fs: &vfs_core::fs::Vfs, name: &str, len: u64) -> Vec { let stats = fs .lookup(1, name) .await @@ -3651,11 +3651,11 @@ mod tests { drop(context); drop(fs); - let reopened = AgentSdk::open(AgentFSOptions::with_path( + let reopened = VfsSdk::open(VfsOptions::with_path( db_path.to_str().expect("test DB path is UTF-8"), )) .await - .expect("reopen AgentFS after simulated server abort"); + .expect("reopen Vfs after simulated server abort"); let persisted = read_file(&reopened.fs, "loose-object", payload.len() as u64).await; println!( "reopened DB persisted {} bytes after FILE_SYNC abort", diff --git a/crates/agentfs-nfs/src/server/permissions.rs b/crates/vfs-nfs/src/server/permissions.rs similarity index 94% rename from crates/agentfs-nfs/src/server/permissions.rs rename to crates/vfs-nfs/src/server/permissions.rs index 93bd2e97..00f90b34 100644 --- a/crates/agentfs-nfs/src/server/permissions.rs +++ b/crates/vfs-nfs/src/server/permissions.rs @@ -1,6 +1,6 @@ -//! NFS-facing shim over the shared AgentFS access semantics. +//! NFS-facing shim over the shared Vfs access semantics. //! -//! POSIX permission logic lives in `agentfs_core::semantics::access`. This +//! POSIX permission logic lives in `vfs_core::semantics::access`. This //! module only converts NFS wire types to core credentials/stats and maps the //! shared answers back to RFC 1813 bitmasks/status codes. @@ -9,10 +9,10 @@ use super::nfs::{ set_uid3, }; use super::rpc::auth_unix; -use agentfs_core::fs::FsError; -use agentfs_core::semantics::access::{self, AttrChange, Credentials, TimeUpdate}; -use agentfs_core::{Stats, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFREG, S_IFSOCK}; use smallvec::SmallVec; +use vfs_core::fs::FsError; +use vfs_core::semantics::access::{self, AttrChange, Credentials, TimeUpdate}; +use vfs_core::{Stats, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFREG, S_IFSOCK}; /// NFS ACCESS procedure permission bits (from RFC 1813) pub const ACCESS3_READ: u32 = 0x0001; diff --git a/crates/agentfs-nfs/src/server/portmap.rs b/crates/vfs-nfs/src/server/portmap.rs similarity index 100% rename from crates/agentfs-nfs/src/server/portmap.rs rename to crates/vfs-nfs/src/server/portmap.rs diff --git a/crates/agentfs-nfs/src/server/portmap_handlers.rs b/crates/vfs-nfs/src/server/portmap_handlers.rs similarity index 100% rename from crates/agentfs-nfs/src/server/portmap_handlers.rs rename to crates/vfs-nfs/src/server/portmap_handlers.rs diff --git a/crates/agentfs-nfs/src/server/rpc.rs b/crates/vfs-nfs/src/server/rpc.rs similarity index 100% rename from crates/agentfs-nfs/src/server/rpc.rs rename to crates/vfs-nfs/src/server/rpc.rs diff --git a/crates/agentfs-nfs/src/server/rpcwire.rs b/crates/vfs-nfs/src/server/rpcwire.rs similarity index 98% rename from crates/agentfs-nfs/src/server/rpcwire.rs rename to crates/vfs-nfs/src/server/rpcwire.rs index da563c8d..390aef37 100644 --- a/crates/agentfs-nfs/src/server/rpcwire.rs +++ b/crates/vfs-nfs/src/server/rpcwire.rs @@ -267,13 +267,13 @@ mod tests { use crate::server::rpc::{auth_flavor, call_body, opaque_auth, rpc_body, rpc_msg}; use crate::server::transaction_tracker::{TransactionTracker, DEFAULT_REPLY_CACHE_CAPACITY}; use crate::server::vfs::NFSFileSystem; - use agentfs_core::{AgentFS as AgentSdk, AgentFSOptions, FileSystem}; use std::sync::Arc; + use vfs_core::{FileSystem, Vfs as VfsSdk, VfsOptions}; - async fn test_context() -> (RPCContext, agentfs_core::fs::AgentFS) { - let agent = AgentSdk::open(AgentFSOptions::ephemeral()) + async fn test_context() -> (RPCContext, vfs_core::fs::Vfs) { + let agent = VfsSdk::open(VfsOptions::ephemeral()) .await - .expect("open ephemeral AgentFS"); + .expect("open ephemeral Vfs"); agent .fs .chmod(1, 0o777) diff --git a/crates/agentfs-nfs/src/server/tcp.rs b/crates/vfs-nfs/src/server/tcp.rs similarity index 96% rename from crates/agentfs-nfs/src/server/tcp.rs rename to crates/vfs-nfs/src/server/tcp.rs index 614b2511..41cbf566 100644 --- a/crates/agentfs-nfs/src/server/tcp.rs +++ b/crates/vfs-nfs/src/server/tcp.rs @@ -262,10 +262,10 @@ mod tests { use crate::server::rpc::{self, accept_body, accepted_reply, reply_body, rpc_body, rpc_msg}; use crate::server::vfs::NFSFileSystem; use crate::server::xdr::XDR; - use agentfs_core::{AgentFS as AgentSdk, AgentFSOptions, FileSystem, DEFAULT_FILE_MODE}; use std::io::Cursor; use std::time::Duration; use tokio::io::AsyncReadExt; + use vfs_core::{FileSystem, Vfs as VfsSdk, VfsOptions, DEFAULT_FILE_MODE}; fn serialize_rpc_call(xid: u32, proc: u32) -> Vec { let mut msg = Vec::new(); @@ -362,22 +362,22 @@ mod tests { #[tokio::test] async fn cancellation_drains_finalizes_and_releases_port_across_cycles() { - std::env::set_var("AGENTFS_FUSE_WRITEBACK", "1"); - std::env::set_var("AGENTFS_OVERLAY_READS", "1"); - std::env::set_var("AGENTFS_BATCH_MS", "60000"); - std::env::set_var("AGENTFS_BATCH_BYTES", "1048576"); - std::env::set_var("AGENTFS_BATCH_GLOBAL_BYTES", "10485760"); + std::env::set_var("VFS_FUSE_WRITEBACK", "1"); + std::env::set_var("VFS_OVERLAY_READS", "1"); + std::env::set_var("VFS_BATCH_MS", "60000"); + std::env::set_var("VFS_BATCH_BYTES", "1048576"); + std::env::set_var("VFS_BATCH_GLOBAL_BYTES", "10485760"); let port = bind_available_local_port().await; for cycle in 0..3 { let dir = tempfile::tempdir().expect("tempdir"); let db_path = dir.path().join(format!("cycle-{cycle}.db")); - let agent = AgentSdk::open(AgentFSOptions::with_path( + let agent = VfsSdk::open(VfsOptions::with_path( db_path.to_str().expect("test DB path is UTF-8"), )) .await - .expect("open file-backed AgentFS"); + .expect("open file-backed Vfs"); let (_stats, file) = FileSystem::create_file( &agent.fs, 1, @@ -419,7 +419,7 @@ mod tests { ); drop(rebind); - let reopened = AgentSdk::open(AgentFSOptions::with_path( + let reopened = VfsSdk::open(VfsOptions::with_path( db_path.to_str().expect("test DB path is UTF-8"), )) .await diff --git a/crates/agentfs-nfs/src/server/transaction_tracker.rs b/crates/vfs-nfs/src/server/transaction_tracker.rs similarity index 100% rename from crates/agentfs-nfs/src/server/transaction_tracker.rs rename to crates/vfs-nfs/src/server/transaction_tracker.rs diff --git a/crates/agentfs-nfs/src/server/vfs.rs b/crates/vfs-nfs/src/server/vfs.rs similarity index 99% rename from crates/agentfs-nfs/src/server/vfs.rs rename to crates/vfs-nfs/src/server/vfs.rs index 1c13efc0..953577e7 100644 --- a/crates/agentfs-nfs/src/server/vfs.rs +++ b/crates/vfs-nfs/src/server/vfs.rs @@ -1,11 +1,11 @@ use super::nfs; use super::nfs::*; pub(crate) use super::rpc::auth_unix; -use agentfs_core::semantics::AckDurability; use async_trait::async_trait; use std::cmp::Ordering; use std::sync::OnceLock; use std::time::SystemTime; +use vfs_core::semantics::AckDurability; #[derive(Default, Debug)] pub struct DirEntrySimple { pub fileid: fileid3, diff --git a/crates/agentfs-nfs/src/server/write_counter.rs b/crates/vfs-nfs/src/server/write_counter.rs similarity index 100% rename from crates/agentfs-nfs/src/server/write_counter.rs rename to crates/vfs-nfs/src/server/write_counter.rs diff --git a/crates/agentfs-nfs/src/server/xdr.rs b/crates/vfs-nfs/src/server/xdr.rs similarity index 100% rename from crates/agentfs-nfs/src/server/xdr.rs rename to crates/vfs-nfs/src/server/xdr.rs diff --git a/scripts/validation/agentfs-clone-benchmark.py b/scripts/validation/vfs-clone-benchmark.py similarity index 100% rename from scripts/validation/agentfs-clone-benchmark.py rename to scripts/validation/vfs-clone-benchmark.py From 4cd1f0ef6dd483cf56758c7c81910d10425ef2c5 Mon Sep 17 00:00:00 2001 From: factory-ain3sh Date: Fri, 24 Jul 2026 01:06:34 -0700 Subject: [PATCH 2/8] docs: rename product surfaces to vfs Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .devcontainer/devcontainer.json | 4 +- .devcontainer/docker-compose.yml | 2 +- .github/assets/vfs-arch.svg | 2 +- .gitignore | 2 +- README.md | 80 +++++----- docs/KNOBS.md | 82 +++++----- docs/MANUAL.md | 248 +++++++++++++++---------------- docs/SPEC.md | 32 ++-- docs/TESTING.md | 74 ++++----- 9 files changed, 263 insertions(+), 263 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 45a44ca1..abdfaacb 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,7 @@ { - "name": "agentfs sandbox", + "name": "vfs sandbox", "dockerComposeFile": ["docker-compose.yml"], - "service": "agentfs", + "service": "vfs", "remoteUser": "node", "workspaceFolder": "/workspace", "waitFor": "postStartCommand", diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 390bf127..c668dafc 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -14,7 +14,7 @@ services: timeout: 5s retries: 10 start_period: 1s - agentfs: + vfs: depends_on: proxy: condition: service_healthy diff --git a/.github/assets/vfs-arch.svg b/.github/assets/vfs-arch.svg index b49bdc1d..d41b4774 100644 --- a/.github/assets/vfs-arch.svg +++ b/.github/assets/vfs-arch.svg @@ -1,4 +1,4 @@ SQLite Database FilePagePagePagePagePageFilesystemKey-ValueToolcallsB-TreeAgentFS SDK \ No newline at end of file + @font-face { font-family: Excalifont; src: url(data:font/woff2;base64,d09GMgABAAAAABLAAA4AAAAAICQAABJrAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbh2wcNAZgAIEsEQgKrgiiCQs8AAE2AiQDdAQgBYMYByAb+BijoorVTvYXB7Yx68AfYEjTmIYYlh+dSFw0liENZXDxw0aYPMCZ17LKXUO0zft/oqVLxEIEo6aUgFiYhd2rcG7tOsNFhUT0h/6+e4loaWSZgMRrAQlIRGJhiVD5v//fnNW+c1nLqM4BK1ZY0qfUxM63MIGWA0sEmeRPXb12a02qPEmGhaOlww8MhkC1yaRM0f9r91oZD8hwQJP7gCBbkzLTp/8B/Pv73+/X6orHDc0QKwVCs1LJ+7jY4WL2dvEkrokIpVE3opK+JMRD2aGTIrEQI6FRSuMVbsvA/M0oxuHE6Nf+gQACAAnsAWAQhBC05HkItAxQo3FcKuCvOptqAH/XVFoN+OeiljrAhwIAeG4MOG2a6gCODxC2wdUCMzbghBuEM4P4deGvPEbDAPreBhntu1Cfvm53bvU230sw7SE79FZvsKD8wqwFxESA3nyuQ20tL4zqKzTPgsCvCNTFW1JNfccS57MPz9IrXOPbZdnxn+dtkHrfpuiybHkryHRiADBlsO9W3RA6+N13Chw8IhIyCrcbdAAmDq4mSJhgCSBklgsMk4m3+kAaNASKAWznIDQcKjI8Aq+BCrxiSaoEhEzWMQaiAkiYAaxy0xwXedgglxoEfxBFkN5ux4EsNQIArpATQQnaNWLqsWC8i0BHp+cpiQ0gT9dp3JLUMvkgJ2VsCgCIVbuGpnR+79hsONYysSWAQ6Q6a9ufhB4MKG0dABY24ms7JdJnCCMXnRG2hwlllpST1oBtbb5MLKziJEmVrlipcpXqtercRcLNRk8spxRrkbtG03e477HrjjjskH02bVg3a8a0KRMCjSAQD/oHYC4RhNkGVRLsHcA96MTNvgUAK4bGc4O8Xgnkkv2X1jLFLD5Gfj5euWjt39u9e/vVywe2Q53ubIGtl2oXjmuVnUd+P3/WuVmfou55heAkvcF0K8GTqrUH09vEwJs3/5aEh1u9eWLqrLR6vM9TJDHw4UPAjyDl8kj+hRhRYn1z0zCnyXSjbWxRzbCZAGozXCIEieu6mBjgHgTMJZoMA8A/4E52J8toW6+cSyOHGQ7otqUtCxKkS6TeCBGc/InKtlkUoqroqCI7Hc/zSCCnTZ/qvX7+Ks9b2qxxExnWV/WWBW5GTpi9yR6sG7EdvC7qcgfmWID+Te2EJKHnYv+2PVfsOrO1lY2HnQGV3UFjPh5rZKhVEl6LCjldFhVepq2kKNZ0FwFuSbD+z9JDuguDhWCB3WV34YTzHV9OOMJRT6iSO8vF40OxbRC2c1VbGtuqgGZribDM8ouX5fPrYve1Q9kdBBgeqRQAklaPxCbeVBCaJVEG/NVFBF0Jgpo/CcjB433yLhijzbycqvVhGz1QThz7s3cXsKsKE/W1XlNUG05+s6F2h7MqlqxS0VeOCe8709eTy9PUv6dN7nnLHj58Cg+Dv5OD9OwRpnu+vAHofHTclgVEp+JdwfNqfSRxKsIwRz1SoesItKhU5YDBJNBOH0EjOU1+4iaYEteVUlZf9r7ceFTDDsiU4TzxR2OqEfKRznY+K4VuADN7Nnjfyq6TPgQMQrU6RIwFsPN7KBoq87OMqlHCS2lK1oq6TBngBCDXmuD/Djmzne86K0bwESJF1vn+ezJVVMti4NlR1eXz6whq2tKmxVURrmJ9VYQM/o/RdQY8ASK/NeEpwx0IW/QMK3+cLtvxuTVFNvGm+804ZFgpOqj/jOyAbMnOMl1vUWvzJOSisa87BYnttaV+p9fdD3eQWs4eZB+o+vCBft/rhoE0U5epwnEMHoeoL2Xfy7G2FktFvuOi7Q2zIGZBzflRnUm8I8NgY8eVwUZyOnHUnYxCkjRAOCRetShkGBkEnT55UzEkDDPFwpqhMi+nsImMsbZ3W+eKU1TKhc7RGikb8zVy3z/E2qIcq02aOMlASMNb2FZRwoeQmeCqmJAQ0PKKfvYiLxqlFfFkv6L7CCxKXGhJMDHLZ7Gw9HYx4SzSGtkRt6Fxffi4ak5r9/t+XBmcGJpsn9ixHVfz46JX+T5VCMpFrkbW9O5zBGdX85K9sdCM7KTXfV4PFHkBGUb3hguRhU9/kjc7t0l8bM6pvDi8L3aG9xZbdqhaIGOPlAnfhpWvg2IixPZZP2/k3UVJ7Flx6uJ44rRl+DTvrvETniLPjQZSLT+9en4sOx1qqZ0dw3xXZMeOj8hNxsZQ+MRw8dM6dxea7X+9t4N0SS83pm/FR3y2WAMabgrqCuNEkae2ikpD7N9GcFSAUCThU8HdlSYCsq19JB+B+GIku5v8oRYyydXILG4MzvTh75i+J8GU4T6oke+6ijAdpAkoxP1ThIuCOIa7kzQxGc5iMZEB7QLrWXJXU9Gc3BDVuuhxEbSEi7OuVOVcsvPJSY0ArRWdIzm9QsUi7gi6j6vT+nbNH0pNC2O+sdpatKzJyawZYxI0GjXlzcmOggvQBM0HQJ3GwEsJy2mDITKxvbFWEvfwlB1cUuRshHPVqn+WXUFNnWVzGYNmS2JCUuvrVC0v0+ndKll1NS1lxaRkA8bW2e37a5W8N0q3nW0E+VG9JTLxZvzaASGqHEXeEokVCR7gAmyoGExKtIr935f3758+3zspCZhQZEqMleb4CmB3dFWjkAUMsGx6rOemEm2G21AJJlcSVt4fp6cH6dat5A2ChhonwV13IC9FpLz/l3AgGTvJ91dqypfsZhwDBngzb2lJmCfFvrX2Wj//M2XEvW7FIR7Im+Qm1szT9MOd023y9M5JjXI8kr3Jdr66VZoA7yYHuaP5WhshhihpKACl39KtKVfrmIGZhY9n8GZbpinhyDvVpVFmfKsl7zFx5iIhKznNvWqZqltoT09rXpGqQ5S4CAYSp2/63ng/pNwET5YEFG/k5VIT/+2QOTk977iiesAV2b9lO6PzwqggwJMqNquW/aimsuXx9Sv0Hxeocd+nIYLW5JEMCyTkUORbkLFAteReWXmNNmeNiGP/wmH+2ITtLVy0xHU063HZ0CnsQo/JpyyApydvf9MSQ7osJMxIg6ZZutmzmUTSE4xuT0nJSuoqBcN5NT/je+virwt8o82/mLnQcKi+YRNzL8BWuinEGAiCXfzZFJjec3S0O0SEVj/X6uNpMWhcKmPO6eWYb5gGqan55rYSrQZkaH7JzPe0WQ3zgRmYDHEWDDYabZC1NsSdORjBxo/vRI5QDhGRCcCKqiuRsK4REDruUc8xsy9aDz8wxkJOXRO5M5thk3yrScVYRsTkGsVUFc1E3xOSOndXg8WEdpKp+cOmqN8rdSyquYzZ2W5D5Pi5POqcECGzBBng+DEMnpqsPZbMixrNsrbmhP2OasET7fBtECP7hNvdVXJtC7zJ2Zi8dSsGTWv9nV1OsMIEDu6IDX8MAoo4jtNjS+h9AXHpT7O0W0XpTkqzJlsVht0Ft22PrtwuPN5rlJTQxrsp263vBiwklEXq6NY0LDrJ35FR3JVTJ8RagvZBxkgoG2VixNQ5VPiUfaCVuiSOickjHf7jwBxcLnO0JWOlV7a6cZdzBmSE3zi2ZdfKVGcX+YK1eCI8fPa5qPgSfCGjEA/XihQWLJEXxPN/+6KQ0ninS+aOFNKwz9Iw+WexgvWrwHyV88jNVfc7LtC2GxMjDB7dmFumhVeSF4pgwTp4GWE2Og6KsrSNcHZHcVJz7BvGvA8Y9t4vejUvNh378oiziqniNo94t+GJXHkWZc5MT4n9Q50sW+OzkNV2tYcKflLxXZTNpbgeO0rzLkarzA7sYDn8vfN9cJgC6NVMXyJjKdmx4m3mV888IQOfVfdaKYOyMRmLksclDwI0QdlgPFKtAWWMPF/IRMh2CCbGPYdT1Ls7Q0IC+eTtafPSMPMwtQyXcpK3NZ+1NRZkWH8+Oc0tqTcpJ+i5ceEcICVjomJ7YLC5m95Me2L4drSNMwoQgTUpYiNNOcVvKcr4OFDxCJD4LpggAG/HC1dodObDB58CBCX9ejmN4um+ctNJN+M7TYUvRlZWgYsJrtxS4p7F5oXuSNWNVKex7QP0lYXEFS53Y8pPhOGTdOwN/Yvz8md7Ga2xSImwZ3hs9KNbv09xBVacNS/Yrjipzg/Pf3maIgYYL13XKgNvdVplRimw5KQEAx7cDCfIV67JUVA1j1Uz/aE1X1UGyIF/PXlmSrR/Wq3HgAi3m/M3NTq10ABk16NxQ22gacpv0+Hpri4DET25C86FnIwK7YaBAdvjhQTM4bmxkubJgVEJo/K/GSNqS8FmnllfCiWgGajTGkqu1mmhJkKDG8V9w3tZbhGi3y7PmSmcXKwBXToKWdRARTcQ7NBjB8obY5VJcNBsasPiBtJyEgNteGJAv8ZmgEs4An7OEoQ7RL6XehwI+SWjt8d0jNN19mxRju4QvEkK1v4Y9rNw2z7rcUi+Rl5aJU6/cLqio+iQOJ7tiCE/hbcP+fx5D5zmExi7Zm/urnWIE1br/4EjmCdGrmpC1a3ENJYX5BW926dE3mN3S8TSKueEnMvNmchJ4MhzuBMoiag/AO3tWmU9qgxYzTZ6mBtTTXm0m9DAUjvSgQ1woPRTM7c2oCr5gEAssXk6NPhcKGUQJlXAek9mVd2jzSQIP6LdRgvdxiPOocqXYvscicYpD2kYMzMuwD8rnXqy/6mTqTTb19cLfJSJ+IPWB2lnadkgl3yIn/S5mHr3N8ZaN2k4u8DjpeGkrOxylPvJQ8iVb6FXpr/q+aQO2Aubwpd90f2Jh7ZUo5sCsNyOPOCRfkxZVhWZFMf9XHyoMSlKhUrKQtgsfc2D05SVrrgRjGEn75qb3OXM3i4nOykRZfK0KLJft6ObvxkJIjjlqX5iI4t5Vle+GDDbGNbg7Nq5Bl5CEYe+1zLlhxwBd4bj4/PpqCR4YpB/+OgwOyEws+JHQrANoIlaIrp9R7Tz3+dqq9wSHh6zpmwP7a5SamIWSHBt85GVXUogCJmXKKmktEtL5t7zeQkmHe79JZISVgwvYtCcydMN3br+ik4olJZBG809twyQRQKsE5uJ5YteqY5fZNXWX/XFyf0g2M+zCgxjcE4+gAZxyVgC8RQfJl7w6WQLJfAQNGIjkH3Ju7HTYyAkWrpxKbn/SdAfLCtULoC/hLFJPTkFgfP+PMn03PWNGdzM6Jfy+NbGaY5DuCwo46BororUfiS1r25mrO9AuBjvn2LpoM1PnK+PezcVvheVQWyG/UFQnL5gHNgKE3j0snK6MVe1wc6RY3AyY3BgMkOT0LjYsk8SQ5wuQscjeKr5o/RolTclGDJvua5Ir1fcMciWVk2u3iK2pdGTkMMQ/7W0AtxDeVUeiPmDqqzyqtxgVM+KcnRHndRb10fVDhJ+K23QSmPV6eyYECvRuKgckqdqfCTH99BSeyw4D28iRKj9uKUaTFJMpViY6M1yEkcvJ0NLF3GWj88ZXsBE4/kT7EzSKtugMQSG+1jQxCaPDw1f/vhyPwoSg7BhCzBGM667h+jeiJZtNhCFZ6YGtvkuiDel8Y2BVtcVAab2Ra4yBQ3LkE/+Wy1tdlbyKHTSx22tf25eG/Xjw9a0T5nBpvxeLK1RNngwYIZcznG/ssOdOEDFtKebj9zCiza77j4yhzIySYdCyndVsJdpPJCGMnf/O5uNl5evBcOKflipmzl5VrcQQuJG/v/SAO0X51bjyOj2yoE2VCtzw6HL95MMhQOC452N0sHgDgmDDTOOeTmklJsMRw7olEiXbekK1K/q4ULRxDQyRvOdIfcfZkOkGljpHuaXfNGlPooX7XOUCR3qkPPrsQiqcClF5uetCkH1I9s+/6VngQfN0unuMlJ+6MR1/aZcuWUNlfGKrwtyaoxFBDtnqJr2Y6gN4/0RKaT42C71n/3oZsbf0MRylCViQspArlAe3z+yMSDgPRw2bOBuipzVvQnDR/b5BGOPAbqV82v0QYFbxBOHkJcAAI8vXhZ1Ru/9zYC+kR+Aet0IBYxbOYxsc1Bpk4449nd+w8tdiKpLdJkMQDeMLAPgT8RKAuRM55lSmkjGyvYwVgAUqYOvWaA4sWOgirfA8tNtDo/AVuvtK6rHAjJTRCggNJm0ZR7/SKjjyYHSDtHpdsAnchOGjGUUVhvARVjZEFADDwCocWUcBWE6EAWj2BSF8NIVhaLkikKLpAQm0ABAtA7FitSoVKZenRa+kpmvjK0oCpE+JZs0o1nBQCl/GtqODGfDWzs1qHDEUqMeOgV1bdTtHMyz9WSkc1sL3yKFTYJw1txgYRRmJRt0IqgkcYUQ1AR4Kqmj7SEgly6d+RwuSsgBV7bTZB6jGjWz+Nw8hVJlLEVbgBJSEWIGTvM/NAAA); }SQLite Database FilePagePagePagePagePageFilesystemKey-ValueToolcallsB-TreeVfs SDK \ No newline at end of file diff --git a/.gitignore b/.gitignore index cd8a225e..1277e05e 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,7 @@ Network Trash Folder Temporary Items .apdisk -.agentfs +.vfs target/ # Python bytecode cache diff --git a/README.md b/README.md index a8cf8b48..fdfb1f40 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -# AgentFS +# Vfs The filesystem for agents: a SQLite-backed virtual filesystem with copy-on-write sandboxing, mountable over FUSE (Linux) or NFS. > **⚠️ Warning:** This software is in BETA. It may still contain bugs and unexpected behavior. Use caution with production data and ensure you have backups. -## What is AgentFS? +## What is Vfs? -AgentFS stores everything an agent does — every file it creates, every piece +Vfs stores everything an agent does — every file it creates, every piece of key-value state, every tool call it records — in a single SQLite database file. That gives agent state three properties ordinary filesystems don't have: @@ -18,9 +18,9 @@ have: * **Portability**: the entire agent runtime (files, state, history) moves between machines as a single `.db` file. -For sandboxed coding-agent workloads, AgentFS layers that database over a +For sandboxed coding-agent workloads, Vfs layers that database over a read-only view of the host filesystem: reads are scoped to the configured -base tree, writes go only to the AgentFS delta database, and the real +base tree, writes go only to the Vfs delta database, and the real filesystem is never modified. This holds even against prompt-injected agents: the isolation is enforced at the filesystem layer, not by convention. @@ -30,15 +30,15 @@ This repository is one Cargo workspace with five crates: | Crate | Role | |---|---| -| `crates/agentfs-core` | The engine: storage, overlay/copy-on-write, schema authority, typed config, telemetry, semantics (access/durability/handles). The only crate meant for external consumption. | -| `crates/agentfs-fuse` | Sealed Linux FUSE mount surface (transport + adapter). | -| `crates/agentfs-nfs` | Sealed NFSv3 serve surface (transport + adapter). | -| `crates/agentfs-mount` | One mount lifecycle: `mount_fs`, `MountHandle`, supervision, daemonize. | -| `crates/agentfs-cli` | The `agentfs` binary: thin CLI edge over the crates above. | +| `crates/vfs-core` | The engine: storage, overlay/copy-on-write, schema authority, typed config, telemetry, semantics (access/durability/handles). The only crate meant for external consumption. | +| `crates/vfs-fuse` | Sealed Linux FUSE mount surface (transport + adapter). | +| `crates/vfs-nfs` | Sealed NFSv3 serve surface (transport + adapter). | +| `crates/vfs-mount` | One mount lifecycle: `mount_fs`, `MountHandle`, supervision, daemonize. | +| `crates/vfs-cli` | The `vfs` binary: thin CLI edge over the crates above. | -Platform support: **Linux is first-tier** (FUSE and NFS backends, `agentfs +Platform support: **Linux is first-tier** (FUSE and NFS backends, `vfs run` sandbox, full validation gate). **macOS is second-tier**: NFS mount -plus a sandboxed `agentfs run` (Seatbelt/`sandbox-exec` with default-deny +plus a sandboxed `vfs run` (Seatbelt/`sandbox-exec` with default-deny read scoping), validated by a manual release gate (`scripts/validation/macos-nfs-git-validation.sh`) run on real hardware — see [docs/TESTING.md](docs/TESTING.md). No other platforms are supported. @@ -49,35 +49,35 @@ Build and install the CLI from source: ```bash cargo +nightly build --release --workspace --bins -install -m 0755 target/release/agentfs ~/.local/bin/ +install -m 0755 target/release/vfs ~/.local/bin/ ``` Initialize an agent filesystem: ```bash -$ agentfs init my-agent -Created agent filesystem: .agentfs/my-agent.db +$ vfs init my-agent +Created agent filesystem: .vfs/my-agent.db Agent ID: my-agent ``` Inspect it without mounting: ```bash -$ agentfs fs my-agent ls +$ vfs fs my-agent ls f hello.txt -$ agentfs fs my-agent cat hello.txt +$ vfs fs my-agent cat hello.txt hello from agent # Or address the database file directly -$ agentfs fs .agentfs/my-agent.db cat hello.txt +$ vfs fs .vfs/my-agent.db cat hello.txt hello from agent ``` View the agent's tool-call timeline: ```bash -$ agentfs timeline my-agent +$ vfs timeline my-agent ID TOOL STATUS DURATION STARTED 4 execute_code pending -- 2024-01-05 09:44:20 3 api_call error 300ms 2024-01-05 09:44:15 @@ -88,7 +88,7 @@ ID TOOL STATUS DURATION STARTED Mount it as a real filesystem (FUSE on Linux, NFS on macOS): ```bash -$ agentfs mount my-agent ./mnt +$ vfs mount my-agent ./mnt $ echo "hello" > ./mnt/hello.txt $ cat ./mnt/hello.txt hello @@ -98,42 +98,42 @@ Run a program in a copy-on-write sandbox over your current directory (FUSE + user/mount namespaces on Linux): ```bash -$ agentfs run --session my-session -- bash +$ vfs run --session my-session -- bash # ... work normally; every write lands in the delta database, # the host filesystem is untouched ... $ exit # List live sandbox sessions; inspect what a session changed -$ agentfs ps -$ agentfs diff my-session +$ vfs ps +$ vfs diff my-session ``` Beyond these basics, the CLI covers the rest of the lifecycle: -* `agentfs exec` — run a one-shot command over a temporary mount, then +* `vfs exec` — run a one-shot command over a temporary mount, then unmount automatically. -* `agentfs clone` — bulk-ingest a git repository straight into the database. -* `agentfs sync` — push/pull the database to a remote Turso (libSQL) server. -* `agentfs serve nfs` / `agentfs serve mcp` — export the filesystem over +* `vfs clone` — bulk-ingest a git repository straight into the database. +* `vfs sync` — push/pull the database to a remote Turso (libSQL) server. +* `vfs serve nfs` / `vfs serve mcp` — export the filesystem over NFS, or expose filesystem and KV-store tools to agents over MCP. * `--key` / `--cipher` — local at-rest encryption of the database. -* `agentfs backup`, `integrity`, `migrate`, `materialize`, `prune` — +* `vfs backup`, `integrity`, `migrate`, `materialize`, `prune` — portable backups, corruption checks, schema migration, partial-origin materialization, and mount cleanup. The **[User Manual](docs/MANUAL.md)** documents every command; its command reference is generated from the CLI's own argument definitions, so it cannot -drift from `agentfs --help`. +drift from `vfs --help`. -## Using AgentFS as a library +## Using Vfs as a library -The `agentfs-core` crate provides programmatic access to the same engine the +The `vfs-core` crate provides programmatic access to the same engine the CLI uses: filesystem, key-value store, and tool-call audit trail over a -single database. See the crate rustdoc (`cargo doc -p agentfs-core`). +single database. See the crate rustdoc (`cargo doc -p vfs-core`). ## How it works -At the heart of AgentFS is the [agent filesystem](docs/SPEC.md), a complete +At the heart of Vfs is the [agent filesystem](docs/SPEC.md), a complete SQLite-based storage system implemented on [Turso](https://github.com/tursodatabase/turso). The database schema separates namespace (dentries) from data (inodes + chunked/inline content), @@ -151,17 +151,17 @@ declared in the generated [docs/KNOBS.md](docs/KNOBS.md) ledger. ## FAQ -### How is AgentFS different from _X_? +### How is Vfs different from _X_? -[Bubblewrap](https://github.com/containers/bubblewrap) provides filesystem isolation using Linux namespaces and overlays. While you could achieve similar isolation with a `bwrap` call that mounts `/` read-only and uses `--tmp-overlay` on the working directory, the key difference is persistence and queryability: with AgentFS, the upper filesystem is stored in a single SQLite database file, which you can query, snapshot, and move to another machine. +[Bubblewrap](https://github.com/containers/bubblewrap) provides filesystem isolation using Linux namespaces and overlays. While you could achieve similar isolation with a `bwrap` call that mounts `/` read-only and uses `--tmp-overlay` on the working directory, the key difference is persistence and queryability: with Vfs, the upper filesystem is stored in a single SQLite database file, which you can query, snapshot, and move to another machine. -[Docker Sandbox](https://www.docker.com/blog/docker-sandboxes-a-new-approach-for-coding-agent-safety/) and AgentFS are complementary rather than competing. AgentFS answers "what happened and what's the state?" while Docker Sandboxes answer "how do I run this safely?" You could use both together: run an agent inside a Docker Sandbox for security, while using AgentFS inside that sandbox for structured state management and audit trails. +[Docker Sandbox](https://www.docker.com/blog/docker-sandboxes-a-new-approach-for-coding-agent-safety/) and Vfs are complementary rather than competing. Vfs answers "what happened and what's the state?" while Docker Sandboxes answer "how do I run this safely?" You could use both together: run an agent inside a Docker Sandbox for security, while using Vfs inside that sandbox for structured state management and audit trails. -[Git worktrees](https://git-scm.com/docs/git-worktree) let you check out multiple branches of a repository into separate directories, allowing agents to work on independent copies of the source code — similar to AgentFS. But AgentFS solves the problem at a lower level. With git worktrees, nothing prevents an agent from modifying files outside its worktree: another agent's worktree, system files, or anything else on the filesystem. The isolation is purely conventional, not enforced. AgentFS provides filesystem-level copy-on-write isolation that cannot be bypassed, and because it operates below git, it also handles untracked files. +[Git worktrees](https://git-scm.com/docs/git-worktree) let you check out multiple branches of a repository into separate directories, allowing agents to work on independent copies of the source code — similar to Vfs. But Vfs solves the problem at a lower level. With git worktrees, nothing prevents an agent from modifying files outside its worktree: another agent's worktree, system files, or anything else on the filesystem. The isolation is purely conventional, not enforced. Vfs provides filesystem-level copy-on-write isolation that cannot be bypassed, and because it operates below git, it also handles untracked files. -### Why implement AgentFS at the filesystem layer instead of using containers or VMs? +### Why implement Vfs at the filesystem layer instead of using containers or VMs? -The filesystem layer gives us capabilities that block devices can't. First, because everything is stored in structured SQLite tables, you can query the filesystem, which is essential for auditability and debugging agent behavior. Second, SQLite's write-ahead log enables snapshotting and time-travel forking by capturing every filesystem change. Third, the engine works in environments where mounting a block device is impossible. This approach also composes with containers and VMs: AgentFS is reachable over remote filesystem protocols like NFS or through mechanisms like virtio-fuse. +The filesystem layer gives us capabilities that block devices can't. First, because everything is stored in structured SQLite tables, you can query the filesystem, which is essential for auditability and debugging agent behavior. Second, SQLite's write-ahead log enables snapshotting and time-travel forking by capturing every filesystem change. Third, the engine works in environments where mounting a block device is impossible. This approach also composes with containers and VMs: Vfs is reachable over remote filesystem protocols like NFS or through mechanisms like virtio-fuse. ## Learn more @@ -170,7 +170,7 @@ The filesystem layer gives us capabilities that block devices can't. First, beca - **[Runtime Knobs](docs/KNOBS.md)** - generated ledger of every tunable - **[Testing](docs/TESTING.md)** - validation gates, benchmark policy, and the manual macOS release gate - **[CHANGELOG](CHANGELOG.md)** - including the fork-era restructure summary -- **[Turso database](https://github.com/tursodatabase/turso)** - the in-process SQL database AgentFS builds on +- **[Turso database](https://github.com/tursodatabase/turso)** - the in-process SQL database Vfs builds on ## License diff --git a/docs/KNOBS.md b/docs/KNOBS.md index ae87ccfa..5ecd77f4 100644 --- a/docs/KNOBS.md +++ b/docs/KNOBS.md @@ -1,6 +1,6 @@ -# AgentFS Runtime Knobs +# Vfs Runtime Knobs - + Every active runtime knob is declared here with an architecture class. Defaults are rendered from the typed config declarations used by the SDK/core and FUSE adapter config modules. @@ -8,47 +8,47 @@ Every active runtime knob is declared here with an architecture class. Defaults | Name | Surface | Class | Default | Owner | Description | Removal criteria | Gate | |---|---|---|---|---|---|---|---| -| `AGENTFS_KEY` | env or --key | product-config | `unset` | agentfs CLI edge | Hex-encoded local encryption key for CLI commands that open a database. | n/a | opts clap env binding | -| `AGENTFS_CIPHER` | env or --cipher | product-config | `unset` | agentfs CLI edge | Encryption cipher paired with AGENTFS_KEY or --key. | n/a | opts clap env binding | -| `AGENTFS_CLONE_TIMINGS` | env | sunset | `false` | agentfs CLI edge | Ad hoc clone timing printout for local performance investigations. | Remove after telemetry registry exposes clone timing through the single report sink. | config::clone_timings_enabled | -| `AGENTFS_PROFILE` | env | product-config | `false` | agentfs-core config | Enables profiling counters and summaries. | n/a | VAL-CONF-011 and VAL-CONF-014 | -| `AGENTFS_OVERLAY_READS` | env | sunset | `true` | agentfs-core config | Tier-4 pending-write read overlay rollback path. | Remove after PendingView/stat coherence and overlay read gates are green for N=2 consecutive milestones. | VAL-CORE-006 and phase8 smoke | -| `AGENTFS_KEEPCACHE_DELTA` | env | product-config | `true` | agentfs-core config | Allows DB-backed delta files to participate in keep-cache eligibility. | n/a | VAL-FUSE-014 | -| `AGENTFS_BATCH_MS` | env | product-config | `5` | agentfs-core config | Write batcher timer window in milliseconds. | n/a | sdk write-batcher tests | -| `AGENTFS_BATCH_BYTES` | env | product-config | `4194304` | agentfs-core config | Per-inode pending-byte drain trigger. | n/a | sdk write-batcher tests | -| `AGENTFS_BATCH_GLOBAL_BYTES` | env | product-config | `67108864` | agentfs-core config | Global pending-byte cap across inodes. | n/a | sdk write-batcher tests | -| `AGENTFS_BATCH_TXN_INODES` | env | product-config | `1024` | agentfs-core config | Maximum inodes drained or imported per transaction. | n/a | sdk write-batcher and import tests | -| `AGENTFS_BATCH_TXN_BYTES` | env | product-config | `33554432` | agentfs-core config | Maximum bytes drained or imported per transaction. | n/a | sdk write-batcher and import tests | -| `AGENTFS_DRAIN_ON_SETATTR` | env | sunset | `true` | agentfs-core config | Compatibility path that drains pending writes before setattr operations. | Remove after PendingView/stat coherence and setattr tests are green for N=2 consecutive milestones. | VAL-CORE-006 and VAL-NFS-016 | -| `--partial-origin` | cli flag | product-config | `off` | agentfs CLI edge | First-class partial-origin copy-up policy: off, on, or auto. | n/a | partial_origin::legacy_env_does_not_override_cli_off | -| `--partial-origin-threshold-bytes` | cli flag | product-config | `1048576` | agentfs CLI edge | Size threshold used by --partial-origin auto. | n/a | opts partial-origin parse tests | -| `AGENTFS_FUSE_WORKERS` | env | product-config | `auto` | agentfs FUSE config | FUSE request dispatch mode. auto sizes from CPU and memory budgets; serial disables kernel-cache acceleration for safety. | n/a | fuse_config::tests::workers_config_feeds_dispatch_and_cache_policy | -| `AGENTFS_FUSE_CPU_PERCENT` | env | product-config | `50` | agentfs FUSE config | CPU budget percentage used when AGENTFS_FUSE_WORKERS=auto. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | -| `AGENTFS_FUSE_MEMORY_PERCENT` | env | product-config | `50` | agentfs FUSE config | Memory budget percentage used when AGENTFS_FUSE_WORKERS=auto. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | -| `AGENTFS_FUSE_QUEUE` | env | product-config | `derived` | agentfs FUSE config | FUSE worker request queue capacity; unset derives from worker count and queue memory budget. | n/a | fuse_config::tests::workers_config_feeds_dispatch_and_cache_policy | -| `AGENTFS_FUSE_QUEUE_MEMORY_PERCENT` | env | product-config | `25` | agentfs FUSE config | Memory budget percentage for derived FUSE queue capacity. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | -| `AGENTFS_FUSE_ENTRY_TTL_MS` | env | product-config | `10000` | agentfs FUSE config | Positive dentry TTL in milliseconds. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | -| `AGENTFS_FUSE_ATTR_TTL_MS` | env | product-config | `10000` | agentfs FUSE config | Attribute TTL in milliseconds. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | -| `AGENTFS_FUSE_NEG_TTL_MS` | env | product-config | `1000` | agentfs FUSE config | Negative dentry TTL in milliseconds. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | -| `AGENTFS_FUSE_WRITEBACK` | env | product-config | `true` | agentfs FUSE config | Enables FUSE writeback cache and the SDK write batcher when the CLI opens core config. | n/a | fuse_config::tests::serial_dispatch_disables_kernel_cache_policy | -| `AGENTFS_FUSE_KEEPCACHE` | env | product-config | `true` | agentfs FUSE config | Allows eligible read-only base files to use FOPEN_KEEP_CACHE. | n/a | fuse_config::tests::serial_dispatch_disables_kernel_cache_policy | -| `AGENTFS_FUSE_READDIRPLUS` | env | product-config | `always` | agentfs FUSE config | Kernel READDIRPLUS policy. | n/a | fuse_config::tests::invalid_readdirplus_warns_and_defaults_on | -| `AGENTFS_FUSE_SYNC_INVAL` | env | product-config | `false` | agentfs FUSE config | Requests synchronous kernel invalidation; serial dispatch keeps deferred invalidation for deadlock safety. | n/a | fuse_config::tests::serial_dispatch_disables_kernel_cache_policy | -| `AGENTFS_FUSE_SELF_INVAL` | env | sunset | `false` | agentfs FUSE config | Compatibility path that restores notify-on-self-mutation behavior. | Remove after adapter cache extraction proves self-invalidation suppression through VAL-FUSE cache and coherence gates. | VAL-FUSE-011 and VAL-FUSE-018 | -| `AGENTFS_DRAIN_ON_RELEASE` | env | sunset | `false` | agentfs FUSE config | Compatibility path that restores commit-on-close and disables noopen/noflush. | Remove after N=2 consecutive milestones with VAL-FUSE-015, noopen, and noflush off-leg gates green. | VAL-FUSE-015 | -| `AGENTFS_DRAIN_ON_FORGET` | env | sunset | `false` | agentfs FUSE config | Compatibility path that restores drain-on-forget. | Remove after lifecycle and FORGET-driven cleanup gates are green for N=2 consecutive milestones. | VAL-FUSE-003 and VAL-FUSE-004 | -| `AGENTFS_FUSE_FLUSH_INVAL` | env | sunset | `false` | agentfs FUSE config | Compatibility path that restores invalidate-on-every-FLUSH. | Remove after noflush coherence gates are green for N=2 consecutive milestones. | VAL-FUSE-007 and VAL-FUSE-008 | -| `AGENTFS_FUSE_NOFLUSH` | env | kill-switch | `true` | agentfs FUSE config | Disables close-time FLUSH by returning ENOSYS after the kernel has written dirty pages. | Retire only after N=2 consecutive milestones with both FUSE transport legs green and the named off-leg gate still passing. | VAL-FUSE-007 default leg and VAL-FUSE-008 off leg | -| `AGENTFS_FUSE_NOOPEN` | env | kill-switch | `true` | agentfs FUSE config | Disables per-file OPEN/RELEASE by returning ENOSYS when the kernel supports no-open. | Retire only after N=2 consecutive milestones with both FUSE transport legs green and the named off-leg gate still passing. | VAL-FUSE-003 default leg and VAL-FUSE-004 off leg | -| `AGENTFS_FUSE_INO_FILES_CAP` | env | product-config | `65536` | agentfs FUSE config | Soft cap for inode-file cache backing no-open file resolution. | n/a | scripts/validation/noopen-coherence.py | -| `AGENTFS_FUSE_CACHE_DIR` | env | product-config | `true` | agentfs FUSE config | Directory-entry cache fast path, effective only when keepcache remains enabled. | n/a | VAL-FUSE-011 and VAL-FUSE-018 | -| `AGENTFS_FUSE_STICKY_KEEPCACHE_DROP` | env | sunset | `false` | agentfs FUSE config | Compatibility path that restores old sticky keepcache-drop behavior after mutation. | Remove after keep-cache fingerprint gates are green for N=2 consecutive milestones. | VAL-FUSE-012 and VAL-FUSE-014 | -| `AGENTFS_FUSE_URING` | env | kill-switch | `true` | agentfs FUSE config | FUSE-over-io_uring transport attempt. Set false to force the legacy /dev/fuse path. | Retire only after N=2 consecutive milestones with both FUSE transport legs green and the named off-leg gate still passing. | VAL-FUSE-009 uring leg and VAL-FUSE-010 off leg | -| `AGENTFS_FUSE_URING_DEPTH` | env | product-config | `4` | agentfs FUSE config | io_uring queue depth, bounded by the adapter parser. | n/a | VAL-FUSE-009 | -| `AGENTFS_FUSE_URING_SPIN_US` | env | sunset | `0` | agentfs FUSE config | Compatibility tuning for io_uring busy-poll spin before blocking. | Remove after uring teardown and performance gates are green for N=2 consecutive milestones. | VAL-FUSE-009 and VAL-GATE-004 | +| `VFS_KEY` | env or --key | product-config | `unset` | vfs CLI edge | Hex-encoded local encryption key for CLI commands that open a database. | n/a | opts clap env binding | +| `VFS_CIPHER` | env or --cipher | product-config | `unset` | vfs CLI edge | Encryption cipher paired with VFS_KEY or --key. | n/a | opts clap env binding | +| `VFS_CLONE_TIMINGS` | env | sunset | `false` | vfs CLI edge | Ad hoc clone timing printout for local performance investigations. | Remove after telemetry registry exposes clone timing through the single report sink. | config::clone_timings_enabled | +| `VFS_PROFILE` | env | product-config | `false` | vfs-core config | Enables profiling counters and summaries. | n/a | VAL-CONF-011 and VAL-CONF-014 | +| `VFS_OVERLAY_READS` | env | sunset | `true` | vfs-core config | Tier-4 pending-write read overlay rollback path. | Remove after PendingView/stat coherence and overlay read gates are green for N=2 consecutive milestones. | VAL-CORE-006 and phase8 smoke | +| `VFS_KEEPCACHE_DELTA` | env | product-config | `true` | vfs-core config | Allows DB-backed delta files to participate in keep-cache eligibility. | n/a | VAL-FUSE-014 | +| `VFS_BATCH_MS` | env | product-config | `5` | vfs-core config | Write batcher timer window in milliseconds. | n/a | sdk write-batcher tests | +| `VFS_BATCH_BYTES` | env | product-config | `4194304` | vfs-core config | Per-inode pending-byte drain trigger. | n/a | sdk write-batcher tests | +| `VFS_BATCH_GLOBAL_BYTES` | env | product-config | `67108864` | vfs-core config | Global pending-byte cap across inodes. | n/a | sdk write-batcher tests | +| `VFS_BATCH_TXN_INODES` | env | product-config | `1024` | vfs-core config | Maximum inodes drained or imported per transaction. | n/a | sdk write-batcher and import tests | +| `VFS_BATCH_TXN_BYTES` | env | product-config | `33554432` | vfs-core config | Maximum bytes drained or imported per transaction. | n/a | sdk write-batcher and import tests | +| `VFS_DRAIN_ON_SETATTR` | env | sunset | `true` | vfs-core config | Compatibility path that drains pending writes before setattr operations. | Remove after PendingView/stat coherence and setattr tests are green for N=2 consecutive milestones. | VAL-CORE-006 and VAL-NFS-016 | +| `--partial-origin` | cli flag | product-config | `off` | vfs CLI edge | First-class partial-origin copy-up policy: off, on, or auto. | n/a | partial_origin::legacy_env_does_not_override_cli_off | +| `--partial-origin-threshold-bytes` | cli flag | product-config | `1048576` | vfs CLI edge | Size threshold used by --partial-origin auto. | n/a | opts partial-origin parse tests | +| `VFS_FUSE_WORKERS` | env | product-config | `auto` | vfs FUSE config | FUSE request dispatch mode. auto sizes from CPU and memory budgets; serial disables kernel-cache acceleration for safety. | n/a | fuse_config::tests::workers_config_feeds_dispatch_and_cache_policy | +| `VFS_FUSE_CPU_PERCENT` | env | product-config | `50` | vfs FUSE config | CPU budget percentage used when VFS_FUSE_WORKERS=auto. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | +| `VFS_FUSE_MEMORY_PERCENT` | env | product-config | `50` | vfs FUSE config | Memory budget percentage used when VFS_FUSE_WORKERS=auto. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | +| `VFS_FUSE_QUEUE` | env | product-config | `derived` | vfs FUSE config | FUSE worker request queue capacity; unset derives from worker count and queue memory budget. | n/a | fuse_config::tests::workers_config_feeds_dispatch_and_cache_policy | +| `VFS_FUSE_QUEUE_MEMORY_PERCENT` | env | product-config | `25` | vfs FUSE config | Memory budget percentage for derived FUSE queue capacity. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | +| `VFS_FUSE_ENTRY_TTL_MS` | env | product-config | `10000` | vfs FUSE config | Positive dentry TTL in milliseconds. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | +| `VFS_FUSE_ATTR_TTL_MS` | env | product-config | `10000` | vfs FUSE config | Attribute TTL in milliseconds. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | +| `VFS_FUSE_NEG_TTL_MS` | env | product-config | `1000` | vfs FUSE config | Negative dentry TTL in milliseconds. | n/a | knobs::tests::knob_defaults_in_docs_match_runtime_defaults | +| `VFS_FUSE_WRITEBACK` | env | product-config | `true` | vfs FUSE config | Enables FUSE writeback cache and the SDK write batcher when the CLI opens core config. | n/a | fuse_config::tests::serial_dispatch_disables_kernel_cache_policy | +| `VFS_FUSE_KEEPCACHE` | env | product-config | `true` | vfs FUSE config | Allows eligible read-only base files to use FOPEN_KEEP_CACHE. | n/a | fuse_config::tests::serial_dispatch_disables_kernel_cache_policy | +| `VFS_FUSE_READDIRPLUS` | env | product-config | `always` | vfs FUSE config | Kernel READDIRPLUS policy. | n/a | fuse_config::tests::invalid_readdirplus_warns_and_defaults_on | +| `VFS_FUSE_SYNC_INVAL` | env | product-config | `false` | vfs FUSE config | Requests synchronous kernel invalidation; serial dispatch keeps deferred invalidation for deadlock safety. | n/a | fuse_config::tests::serial_dispatch_disables_kernel_cache_policy | +| `VFS_FUSE_SELF_INVAL` | env | sunset | `false` | vfs FUSE config | Compatibility path that restores notify-on-self-mutation behavior. | Remove after adapter cache extraction proves self-invalidation suppression through VAL-FUSE cache and coherence gates. | VAL-FUSE-011 and VAL-FUSE-018 | +| `VFS_DRAIN_ON_RELEASE` | env | sunset | `false` | vfs FUSE config | Compatibility path that restores commit-on-close and disables noopen/noflush. | Remove after N=2 consecutive milestones with VAL-FUSE-015, noopen, and noflush off-leg gates green. | VAL-FUSE-015 | +| `VFS_DRAIN_ON_FORGET` | env | sunset | `false` | vfs FUSE config | Compatibility path that restores drain-on-forget. | Remove after lifecycle and FORGET-driven cleanup gates are green for N=2 consecutive milestones. | VAL-FUSE-003 and VAL-FUSE-004 | +| `VFS_FUSE_FLUSH_INVAL` | env | sunset | `false` | vfs FUSE config | Compatibility path that restores invalidate-on-every-FLUSH. | Remove after noflush coherence gates are green for N=2 consecutive milestones. | VAL-FUSE-007 and VAL-FUSE-008 | +| `VFS_FUSE_NOFLUSH` | env | kill-switch | `true` | vfs FUSE config | Disables close-time FLUSH by returning ENOSYS after the kernel has written dirty pages. | Retire only after N=2 consecutive milestones with both FUSE transport legs green and the named off-leg gate still passing. | VAL-FUSE-007 default leg and VAL-FUSE-008 off leg | +| `VFS_FUSE_NOOPEN` | env | kill-switch | `true` | vfs FUSE config | Disables per-file OPEN/RELEASE by returning ENOSYS when the kernel supports no-open. | Retire only after N=2 consecutive milestones with both FUSE transport legs green and the named off-leg gate still passing. | VAL-FUSE-003 default leg and VAL-FUSE-004 off leg | +| `VFS_FUSE_INO_FILES_CAP` | env | product-config | `65536` | vfs FUSE config | Soft cap for inode-file cache backing no-open file resolution. | n/a | scripts/validation/noopen-coherence.py | +| `VFS_FUSE_CACHE_DIR` | env | product-config | `true` | vfs FUSE config | Directory-entry cache fast path, effective only when keepcache remains enabled. | n/a | VAL-FUSE-011 and VAL-FUSE-018 | +| `VFS_FUSE_STICKY_KEEPCACHE_DROP` | env | sunset | `false` | vfs FUSE config | Compatibility path that restores old sticky keepcache-drop behavior after mutation. | Remove after keep-cache fingerprint gates are green for N=2 consecutive milestones. | VAL-FUSE-012 and VAL-FUSE-014 | +| `VFS_FUSE_URING` | env | kill-switch | `true` | vfs FUSE config | FUSE-over-io_uring transport attempt. Set false to force the legacy /dev/fuse path. | Retire only after N=2 consecutive milestones with both FUSE transport legs green and the named off-leg gate still passing. | VAL-FUSE-009 uring leg and VAL-FUSE-010 off leg | +| `VFS_FUSE_URING_DEPTH` | env | product-config | `4` | vfs FUSE config | io_uring queue depth, bounded by the adapter parser. | n/a | VAL-FUSE-009 | +| `VFS_FUSE_URING_SPIN_US` | env | sunset | `0` | vfs FUSE config | Compatibility tuning for io_uring busy-poll spin before blocking. | Remove after uring teardown and performance gates are green for N=2 consecutive milestones. | VAL-FUSE-009 and VAL-GATE-004 | ## Deleted compatibility knobs | Name | Surface | Class | Default | Owner | Description | Removal criteria | Gate | |---|---|---|---|---|---|---|---| -| `AGENTFS_OVERLAY_PARTIAL_ORIGIN` | deleted env compat | sunset | `removed` | agentfs-core config | Removed legacy env compatibility path superseded by --partial-origin. | Already removed in M3. Do not reintroduce; use --partial-origin or --partial-origin-threshold-bytes. | partial_origin::legacy_env_does_not_override_cli_off | +| `VFS_OVERLAY_PARTIAL_ORIGIN` | deleted env compat | sunset | `removed` | vfs-core config | Removed legacy env compatibility path superseded by --partial-origin. | Already removed in M3. Do not reintroduce; use --partial-origin or --partial-origin-threshold-bytes. | partial_origin::legacy_env_does_not_override_cli_off | diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 6230c354..cf1dc58b 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -1,11 +1,11 @@ -# AgentFS Reference Guide +# Vfs Reference Guide -Command-line reference for the AgentFS CLI. +Command-line reference for the Vfs CLI. The command and option sections between the `GENERATED COMMAND REFERENCE` markers are rendered from the CLI's clap definitions and checked by `docs::tests::manual_help_parity`, so this manual always matches -`agentfs --help`. +`vfs --help`. ## Installation @@ -14,62 +14,62 @@ NFS mount only): ```bash cargo +nightly build --release --workspace --bins -install -m 0755 target/release/agentfs ~/.local/bin/ +install -m 0755 target/release/vfs ~/.local/bin/ ``` - + ## Commands -Every section below is generated from the clap definitions the binary actually parses; `agentfs --help` and this reference cannot disagree. +Every section below is generated from the clap definitions the binary actually parses; `vfs --help` and this reference cannot disagree. -### agentfs completions +### vfs completions Manage shell completions (supported shells: bash, zsh, fish, elvish, powershell) ``` -agentfs completions +vfs completions ``` -#### agentfs completions install +#### vfs completions install Install shell completions to your shell rc file ``` -agentfs completions install [SHELL] +vfs completions install [SHELL] ``` **Arguments:** - `[SHELL]` — Shell to install completions for (defaults to current shell) [possible values: bash, zsh, fish, elvish, power-shell] -#### agentfs completions uninstall +#### vfs completions uninstall Uninstall shell completions from your shell rc file ``` -agentfs completions uninstall [SHELL] +vfs completions uninstall [SHELL] ``` **Arguments:** - `[SHELL]` — Shell to uninstall completions for (defaults to current shell) [possible values: bash, zsh, fish, elvish, power-shell] -#### agentfs completions show +#### vfs completions show Print instructions for manual installation ``` -agentfs completions show +vfs completions show ``` -### agentfs init +### vfs init Initialize a new agent filesystem ``` -agentfs init [OPTIONS] [ID] +vfs init [OPTIONS] [ID] ``` **Arguments:** @@ -80,8 +80,8 @@ agentfs init [OPTIONS] [ID] - `--force` — Overwrite existing file if it exists - `--base ` — Base directory for overlay filesystem (copy-on-write) -- `--key ` — Hex-encoded encryption key. Enables local encryption when provided [env: AGENTFS_KEY] -- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: AGENTFS_CIPHER] +- `--key ` — Hex-encoded encryption key. Enables local encryption when provided [env: VFS_KEY] +- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: VFS_CIPHER] - `-c, --command ` — Command to execute after initialization (mounts the filesystem, runs command, unmounts) - `--backend ` — Backend to use for mounting when using -c (default: fuse on Linux, nfs on macOS) [possible values: fuse, nfs; default: fuse] - `--sync-remote-url ` @@ -90,56 +90,56 @@ agentfs init [OPTIONS] [ID] - `--sync-partial-bootstrap-query ` - `--sync-partial-bootstrap-length ` -### agentfs sync +### vfs sync Remote sync operations ``` -agentfs sync +vfs sync ``` **Arguments:** - `` — Agent ID or database path -#### agentfs sync pull +#### vfs sync pull -Pull remote changes (only of agentfs was initialized with remote sync) +Pull remote changes (only of vfs was initialized with remote sync) ``` -agentfs sync pull +vfs sync pull ``` -#### agentfs sync push +#### vfs sync push -Push remote changes (only of agentfs was initialized with remote sync) +Push remote changes (only of vfs was initialized with remote sync) ``` -agentfs sync push +vfs sync push ``` -#### agentfs sync stats +#### vfs sync stats Print synced database stats ``` -agentfs sync stats +vfs sync stats ``` -#### agentfs sync checkpoint +#### vfs sync checkpoint Checkpoint local synced db ``` -agentfs sync checkpoint +vfs sync checkpoint ``` -### agentfs fs +### vfs fs Filesystem operations ``` -agentfs fs [OPTIONS] +vfs fs [OPTIONS] ``` **Arguments:** @@ -148,39 +148,39 @@ agentfs fs [OPTIONS] **Options:** -- `--key ` — Hex-encoded encryption key for encrypted databases [env: AGENTFS_KEY] -- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: AGENTFS_CIPHER] +- `--key ` — Hex-encoded encryption key for encrypted databases [env: VFS_KEY] +- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: VFS_CIPHER] -#### agentfs fs ls +#### vfs fs ls List files in the filesystem ``` -agentfs fs ls [FS_PATH] +vfs fs ls [FS_PATH] ``` **Arguments:** - `[FS_PATH]` — Path to list (default: /) [default: /] -#### agentfs fs cat +#### vfs fs cat Display file contents ``` -agentfs fs cat +vfs fs cat ``` **Arguments:** - `` — Path to the file in the filesystem -#### agentfs fs write +#### vfs fs write Write file content ``` -agentfs fs write +vfs fs write ``` **Arguments:** @@ -188,14 +188,14 @@ agentfs fs write - `` — Path to the file in the filesystem - `` — Content of the file -### agentfs run +### vfs run Run a command in the sandboxed environment. -By default, uses FUSE+overlay with Linux user and mount namespaces for isolation. The overlay uses the host filesystem as a read-only base and stores all changes in an AgentFS-backed delta layer. On macOS the overlay is mounted over NFS and a generated Seatbelt profile scopes writes to the sandbox and reads to the allowed directories plus required platform paths (see the Sandboxing section of docs/MANUAL.md). +By default, uses FUSE+overlay with Linux user and mount namespaces for isolation. The overlay uses the host filesystem as a read-only base and stores all changes in a Vfs-backed delta layer. On macOS the overlay is mounted over NFS and a generated Seatbelt profile scopes writes to the sandbox and reads to the allowed directories plus required platform paths (see the Sandboxing section of docs/MANUAL.md). ``` -agentfs run [OPTIONS] [COMMAND] [ARGS]... +vfs run [OPTIONS] [COMMAND] [ARGS]... ``` **Arguments:** @@ -211,17 +211,17 @@ agentfs run [OPTIONS] [COMMAND] [ARGS]... - `--system` — Allow other system users to access this mount (requires /etc/fuse.conf user_allow_other; use cautiously) - `--partial-origin ` — Partial-origin policy for base-file writes: off, on, or auto [possible values: off, on, auto] - `--partial-origin-threshold-bytes ` — Size threshold for --partial-origin auto -- `--key ` — Hex-encoded encryption key for the delta layer. Enables local encryption when provided [env: AGENTFS_KEY] -- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: AGENTFS_CIPHER] +- `--key ` — Hex-encoded encryption key for the delta layer. Enables local encryption when provided [env: VFS_KEY] +- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: VFS_CIPHER] -### agentfs exec +### vfs exec -Execute a command with an AgentFS filesystem mounted. +Execute a command with a Vfs filesystem mounted. -Mounts the specified AgentFS to a temporary directory, runs the command with that directory as the working directory, then automatically unmounts. This is useful for running tools that need filesystem access without a persistent mount. +Mounts the specified Vfs to a temporary directory, runs the command with that directory as the working directory, then automatically unmounts. This is useful for running tools that need filesystem access without a persistent mount. ``` -agentfs exec [OPTIONS] [ARGS]... +vfs exec [OPTIONS] [ARGS]... ``` **Arguments:** @@ -233,17 +233,17 @@ agentfs exec [OPTIONS] [ARGS]... **Options:** - `--backend ` — Backend to use for mounting (default: fuse on Linux, nfs on macOS) [possible values: fuse, nfs; default: fuse] -- `--key ` — Hex-encoded encryption key for encrypted databases [env: AGENTFS_KEY] -- `--cipher ` — Cipher algorithm for encryption (required with --key) [env: AGENTFS_CIPHER] +- `--key ` — Hex-encoded encryption key for encrypted databases [env: VFS_KEY] +- `--cipher ` — Cipher algorithm for encryption (required with --key) [env: VFS_CIPHER] -### agentfs clone +### vfs clone -Clone a git repository into an AgentFS database (fast bulk ingest). +Clone a git repository into a Vfs database (fast bulk ingest). Runs `git clone --no-checkout` through a temporary mount (pack files are large sequential writes), then materializes the worktree by bulk-importing blobs straight into the database in large transactions and fabricating a matching git index, skipping the per-file FUSE round trips of a regular checkout. The resulting repository lives entirely inside the database; nothing is written to the host filesystem. Submodules and smudge/clean filters are not supported. ``` -agentfs clone [OPTIONS] [NAME] +vfs clone [OPTIONS] [NAME] ``` **Arguments:** @@ -257,12 +257,12 @@ agentfs clone [OPTIONS] [NAME] - `--backend ` — Backend to use for mounting (default: fuse on Linux, nfs on macOS) [possible values: fuse, nfs; default: fuse] - `--verify` — Verify `git status` is clean through the mount before finishing -### agentfs mount +### vfs mount Mount an agent filesystem using FUSE (or list mounts if no args) ``` -agentfs mount [OPTIONS] [ID_OR_PATH] [MOUNTPOINT] +vfs mount [OPTIONS] [ID_OR_PATH] [MOUNTPOINT] ``` **Arguments:** @@ -281,27 +281,27 @@ agentfs mount [OPTIONS] [ID_OR_PATH] [MOUNTPOINT] - `--backend ` — Backend to use for mounting [possible values: fuse, nfs; default: fuse] - `--partial-origin ` — Partial-origin policy for base-file writes: off, on, or auto [possible values: off, on, auto] - `--partial-origin-threshold-bytes ` — Size threshold for --partial-origin auto -- `--key ` — Hex-encoded encryption key for encrypted databases [env: AGENTFS_KEY] -- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: AGENTFS_CIPHER] +- `--key ` — Hex-encoded encryption key for encrypted databases [env: VFS_KEY] +- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: VFS_CIPHER] -### agentfs diff +### vfs diff Show differences between base filesystem and delta (overlay mode only) ``` -agentfs diff +vfs diff ``` **Arguments:** - `` — Agent ID or database path -### agentfs timeline +### vfs timeline Display agent action timeline from tool call audit log ``` -agentfs timeline [OPTIONS] +vfs timeline [OPTIONS] ``` **Arguments:** @@ -315,12 +315,12 @@ agentfs timeline [OPTIONS] - `--status ` — Filter by status (pending/success/error) [possible values: pending, success, error] - `--format ` — Output format [possible values: table, json; default: table] -### agentfs nfs +### vfs nfs -Start an NFS server to export an AgentFS filesystem over the network (deprecated: use `agentfs serve nfs` instead) +Start an NFS server to export a Vfs filesystem over the network (deprecated: use `vfs serve nfs` instead) ``` -agentfs nfs [OPTIONS] +vfs nfs [OPTIONS] ``` **Arguments:** @@ -331,15 +331,15 @@ agentfs nfs [OPTIONS] - `--bind ` — IP address to bind to [default: 127.0.0.1] - `--port ` — Port to listen on [default: 11111] -- `--key ` — Hex-encoded encryption key for encrypted databases [env: AGENTFS_KEY] -- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: AGENTFS_CIPHER] +- `--key ` — Hex-encoded encryption key for encrypted databases [env: VFS_KEY] +- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: VFS_CIPHER] -### agentfs mcp-server +### vfs mcp-server -Start an MCP server exposing filesystem and KV-store tools (deprecated: use `agentfs serve mcp` instead) +Start an MCP server exposing filesystem and KV-store tools (deprecated: use `vfs serve mcp` instead) ``` -agentfs mcp-server [OPTIONS] +vfs mcp-server [OPTIONS] ``` **Arguments:** @@ -350,20 +350,20 @@ agentfs mcp-server [OPTIONS] - `--tools ` — Tools to expose (comma-separated). If not provided, all tools are exposed. Available tools: read_file, write_file, readdir, mkdir, remove, rename, stat, access, kv_get, kv_set, kv_delete, kv_list -### agentfs serve +### vfs serve -Serve an AgentFS filesystem via different protocols +Serve a Vfs filesystem via different protocols ``` -agentfs serve +vfs serve ``` -#### agentfs serve nfs +#### vfs serve nfs -Start an NFS server to export an AgentFS filesystem over the network +Start an NFS server to export a Vfs filesystem over the network ``` -agentfs serve nfs [OPTIONS] +vfs serve nfs [OPTIONS] ``` **Arguments:** @@ -374,15 +374,15 @@ agentfs serve nfs [OPTIONS] - `--bind ` — IP address to bind to [default: 127.0.0.1] - `--port ` — Port to listen on [default: 11111] -- `--key ` — Hex-encoded encryption key for encrypted databases [env: AGENTFS_KEY] -- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: AGENTFS_CIPHER] +- `--key ` — Hex-encoded encryption key for encrypted databases [env: VFS_KEY] +- `--cipher ` — Cipher algorithm for encryption (required with --key). Options: aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4, aes128gcm, aes256gcm [env: VFS_CIPHER] -#### agentfs serve mcp +#### vfs serve mcp Start an MCP server exposing filesystem and KV-store tools ``` -agentfs serve mcp [OPTIONS] +vfs serve mcp [OPTIONS] ``` **Arguments:** @@ -393,40 +393,40 @@ agentfs serve mcp [OPTIONS] - `--tools ` — Tools to expose (comma-separated). If not provided, all tools are exposed. Available tools: read_file, write_file, readdir, mkdir, remove, rename, stat, access, kv_get, kv_set, kv_delete, kv_list -### agentfs ps +### vfs ps -List active agentfs run sessions +List active vfs run sessions ``` -agentfs ps +vfs ps ``` -### agentfs prune +### vfs prune Prune unused resources ``` -agentfs prune +vfs prune ``` -#### agentfs prune mounts +#### vfs prune mounts -Unmount unused agentfs mount points +Unmount unused vfs mount points ``` -agentfs prune mounts [OPTIONS] +vfs prune mounts [OPTIONS] ``` **Options:** - `--force` — Skip confirmation prompt and unmount immediately -### agentfs integrity +### vfs integrity -Check a local AgentFS database for SQLite and schema corruption +Check a local Vfs database for SQLite and schema corruption ``` -agentfs integrity [OPTIONS] +vfs integrity [OPTIONS] ``` **Arguments:** @@ -442,12 +442,12 @@ agentfs integrity [OPTIONS] - `--key ` — Hex-encoded encryption key for encrypted databases - `--cipher ` — Encryption cipher (required with --key) -### agentfs backup +### vfs backup -Create a portable local AgentFS database backup +Create a portable local Vfs database backup ``` -agentfs backup [OPTIONS] +vfs backup [OPTIONS] ``` **Arguments:** @@ -462,12 +462,12 @@ agentfs backup [OPTIONS] - `--key ` — Hex-encoded encryption key for encrypted databases - `--cipher ` — Encryption cipher (required with --key) -### agentfs materialize +### vfs materialize Create a portable database by materializing partial-origin files ``` -agentfs materialize [OPTIONS] +vfs materialize [OPTIONS] ``` **Arguments:** @@ -481,12 +481,12 @@ agentfs materialize [OPTIONS] - `--key ` — Hex-encoded encryption key for encrypted databases - `--cipher ` — Encryption cipher (required with --key) -### agentfs migrate +### vfs migrate Migrate database schema to the current version ``` -agentfs migrate [OPTIONS] +vfs migrate [OPTIONS] ``` **Arguments:** @@ -504,14 +504,14 @@ agentfs migrate [OPTIONS] -## MCP Server (`agentfs serve mcp`) +## MCP Server (`vfs serve mcp`) The `write_file` tool overwrites existing files in place and keeps their mode; files it creates get the default mode `0644` (`rw-r--r--`). -## Sandboxing (`agentfs run`) +## Sandboxing (`vfs run`) -`agentfs run` scopes both writes and reads at the OS level; the mechanism +`vfs run` scopes both writes and reads at the OS level; the mechanism differs by platform. **Linux (first-tier):** FUSE + overlay inside user and mount namespaces. @@ -527,7 +527,7 @@ the session is resumed with `--session`. **macOS (second-tier):** NFS mount + a generated `sandbox-exec` (Seatbelt) profile. Writes are restricted to the mountpoint, temp directories, `~/Library`, and the allowed paths. Reads are default-deny: only the session -directory (`~/.agentfs/run/`), the allowed directories (the defaults plus +directory (`~/.vfs/run/`), the allowed directories (the defaults plus `--allow`), and a curated set of platform roots are readable (system frameworks and libraries, the dyld shared cache cryptex, executable directories, `/private/etc`, terminfo/locale data under `/usr/share`, temp @@ -553,20 +553,20 @@ read-scoping leg: a secret outside the allow list must be unreadable, and Every runtime knob (env var or first-class flag) is declared in the generated [docs/KNOBS.md](KNOBS.md) ledger with its class, default, owner, and gate. -`AGENTFS_KEY` / `AGENTFS_CIPHER` provide default encryption credentials for +`VFS_KEY` / `VFS_CIPHER` provide default encryption credentials for the commands whose `--key` / `--cipher` options declare them (see the generated sections above); `TURSO_DB_AUTH_TOKEN` authenticates cloud sync. ### FUSE-over-io_uring and rapid remounts -On Linux kernels with `fuse.enable_uring=1` (the `AGENTFS_FUSE_URING` knob -controls whether AgentFS uses the transport), the kernel drains a just-closed +On Linux kernels with `fuse.enable_uring=1` (the `VFS_FUSE_URING` knob +controls whether Vfs uses the transport), the kernel drains a just-closed FUSE connection for roughly two seconds, and a new mount racing that drain can -block inside `mount(2)` indefinitely (observed on kernel 7.1.2). AgentFS +block inside `mount(2)` indefinitely (observed on kernel 7.1.2). Vfs bounds this: the mount is retried for a few seconds and then fails with a clear error instead of hanging. If rapid unmount-then-mount cycles keep hitting the error, wait a couple of seconds between cycles or set -`AGENTFS_FUSE_URING=0` on the mount-owning processes. A mount left wedged by +`VFS_FUSE_URING=0` on the mount-owning processes. A mount left wedged by other tooling can be recovered with `echo 1 > /sys/fs/fuse/connections//abort` (verify the connection id first). @@ -577,21 +577,21 @@ The `turso_core` dependency (0.5.3) leaks `tursodb-ephemeral-*` sort-spill files into the temp dir and never unlinks them (`vdbe/execute.rs:10096`). The CLI therefore points its own `TMPDIR` at a private per-process directory that is removed on exit, so hosts do not accumulate spill litter. This override is -process-internal: commands spawned by `agentfs run`, `agentfs exec`, and -`agentfs init -c` see the original `TMPDIR`. Stale spill directories from +process-internal: commands spawned by `vfs run`, `vfs exec`, and +`vfs init -c` see the original `TMPDIR`. Stale spill directories from `SIGKILL`ed processes are garbage-collected on the next CLI start. -Variables set inside an `agentfs run` sandbox: +Variables set inside an `vfs run` sandbox: | Variable | Description | |----------|-------------| -| `AGENTFS` | Set to `1` inside the AgentFS sandbox | -| `AGENTFS_SANDBOX` | Sandbox type: `linux-namespace` or `macos-sandbox` | -| `AGENTFS_SESSION` | Current session ID | +| `VFS` | Set to `1` inside the Vfs sandbox | +| `VFS_SANDBOX` | Sandbox type: `linux-namespace` or `macos-sandbox` | +| `VFS_SESSION` | Current session ID | ## Local Encryption -AgentFS supports encrypting the local SQLite database at rest. +Vfs supports encrypting the local SQLite database at rest. **Supported ciphers:** @@ -608,26 +608,26 @@ AgentFS supports encrypting the local SQLite database at rest. KEY=$(openssl rand -hex 32) # Initialize with encryption -agentfs init --key $KEY --cipher aes256gcm my-secure-agent +vfs init --key $KEY --cipher aes256gcm my-secure-agent # Access the filesystem -agentfs fs my-secure-agent --key $KEY --cipher aes256gcm ls / +vfs fs my-secure-agent --key $KEY --cipher aes256gcm ls / ``` **Example: Encrypted sandbox session** ```bash -agentfs run --key $KEY --cipher aes256gcm -- bash +vfs run --key $KEY --cipher aes256gcm -- bash ``` **Using environment variables:** ```bash -export AGENTFS_KEY=$(openssl rand -hex 32) -export AGENTFS_CIPHER=aes256gcm +export VFS_KEY=$(openssl rand -hex 32) +export VFS_CIPHER=aes256gcm -agentfs init my-secure-agent -agentfs fs my-secure-agent ls / +vfs init my-secure-agent +vfs fs my-secure-agent ls / ``` **Limitations:** @@ -636,9 +636,9 @@ agentfs fs my-secure-agent ls / ## Files -- `.agentfs/.db` - Agent filesystem database (relative to the working - directory where `agentfs init` ran) -- `~/.agentfs/run/` - `agentfs run` session state (listed by `agentfs ps`) +- `.vfs/.db` - Agent filesystem database (relative to the working + directory where `vfs init` ran) +- `~/.vfs/run/` - `vfs run` session state (listed by `vfs ps`) ## Unmounting diff --git a/docs/SPEC.md b/docs/SPEC.md index a806794e..a9cdd792 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -14,21 +14,21 @@ All timestamps in this specification use Unix epoch format (seconds since 1970-0 ## Runtime Architecture and Safety Invariants -The persistent AgentFS authority is the SQLite database described by this +The persistent Vfs authority is the SQLite database described by this specification. Runtime mounts, caches, file handles, FUSE lookup references, and overlay inode maps are acceleration structures only; they MUST be reconstructible from the database plus the configured read-only base path and MUST NOT become the only source of virtual filesystem state. -AgentFS sandboxing is built around two invariants: +Vfs sandboxing is built around two invariants: -1. A portable AgentFS database contains all writable virtual filesystem state. +1. A portable Vfs database contains all writable virtual filesystem state. Clean shutdown SHOULD checkpoint transient SQLite sidecars so backups and materialized copies can be represented as a single main database file. 2. Copy-on-write sandbox writes MUST NOT modify the real filesystem. Overlay backends MAY read from an explicitly scoped base directory, but file creates, writes, truncates, chmod/chown/utimens, links, renames, and deletes are - represented in the AgentFS delta database and overlay metadata. + represented in the Vfs delta database and overlay metadata. Implementations MAY use kernel caches, positive/negative lookup caches, attribute caches, read-dir caches, and parallel FUSE dispatch, provided they @@ -50,7 +50,7 @@ Writes MAY be acknowledged from an in-memory pending map before their bytes reach SQLite. The reference implementation batches FUSE writeback-cache writes and drains them on a short timer window, a per-inode pending-byte trigger, a global pending-byte cap, and bounded per-transaction inode/byte -budgets (`AGENTFS_BATCH_*` knobs). Buffered acknowledgement is only permitted +budgets (`VFS_BATCH_*` knobs). Buffered acknowledgement is only permitted for volatile-durability writes; any operation that promises durability — `fsync`, an NFSv3 `WRITE` acknowledged as `FILE_SYNC`, or unmount/shutdown finalization — MUST NOT return until the affected pending bytes are committed @@ -71,7 +71,7 @@ flush by default: it answers `OPEN`/`RELEASE` (and close-time `FLUSH`) with with `fh = 0`. Implementations using this mode MUST NOT key any state to open/release pairs. Per-inode resources (backing file handles for base reads, caches) are keyed by inode number, held in a bounded LRU -(`AGENTFS_FUSE_INO_FILES_CAP`), and reclaimed by kernel `FORGET` traffic and +(`VFS_FUSE_INO_FILES_CAP`), and reclaimed by kernel `FORGET` traffic and LRU eviction rather than by `RELEASE`. Consequences that MUST hold: @@ -82,8 +82,8 @@ Consequences that MUST hold: 2. Close does not imply commit. With no-flush enabled the kernel has already written back dirty pages before close; durability is promised only by `fsync` (see the durability contract above). -3. Both behaviors retain kill switches (`AGENTFS_FUSE_NOOPEN`, - `AGENTFS_FUSE_NOFLUSH`) and dedicated coherence gates +3. Both behaviors retain kill switches (`VFS_FUSE_NOOPEN`, + `VFS_FUSE_NOFLUSH`) and dedicated coherence gates (`scripts/validation/noopen-coherence.py`, `scripts/validation/flush-coherence.py`) that validate the default and disabled legs. @@ -92,7 +92,7 @@ Consequences that MUST hold: On kernels that expose `/sys/module/fuse/parameters/enable_uring = Y`, the FUSE session attempts the FUSE-over-io_uring transport by default -(`AGENTFS_FUSE_URING`, bounded queue depth via `AGENTFS_FUSE_URING_DEPTH`) +(`VFS_FUSE_URING`, bounded queue depth via `VFS_FUSE_URING_DEPTH`) and falls back to the classic `/dev/fuse` read/write loop when io_uring setup is unavailable or fails. The transport is a performance detail only: request semantics, reply contents, cache invalidation, and teardown bounds @@ -597,19 +597,19 @@ Migrations are keyed by `PRAGMA user_version` and land any supported old schema (v0.0, v0.2, v0.4) at the current version with one command: ```bash -agentfs migrate +vfs migrate ``` In-place migration requirements: 1. Every migration step MUST be an additive, idempotent DDL change applied inside a single transaction that stamps `PRAGMA user_version` before committing. 2. Existing file contents MUST keep their recorded `chunk_size`; the in-place path does not re-chunk data. A defaulted `inline_threshold` MUST NOT exceed the database's recorded `chunk_size`. -3. Open paths (mount, fs, exec, SDK open) MUST NOT run version upgrades implicitly; they reject old schemas and direct the user to `agentfs migrate`. +3. Open paths (mount, fs, exec, SDK open) MUST NOT run version upgrades implicitly; they reject old schemas and direct the user to `vfs migrate`. The copy-based mode rebuilds the database with the current chunk layout: ```bash -agentfs migrate --copy --verify +vfs migrate --copy --verify ``` Copy-migration requirements: @@ -711,7 +711,7 @@ CREATE TABLE fs_overlay_config ( |-----|-------------| | `base_path` | Canonical path to the read-only base directory | -v0.5 copy migration MUST preserve this table when migrating an overlay delta database. Without it, a migrated overlay database would mount as a plain AgentFS database and lose base-layer visibility. +v0.5 copy migration MUST preserve this table when migrating an overlay delta database. Without it, a migrated overlay database would mount as a plain Vfs database and lose base-layer visibility. ### Operations @@ -816,8 +816,8 @@ fail reads of partial-origin files if the recorded base size or modification metadata no longer matches the current base file. Snapshot/restore of the main delta database is supported only when the same unchanged base path is available. A database containing partial-origin rows is not portable on its own: -`agentfs backup` rejects it unless `--materialize` folds the base bytes in, -`agentfs materialize` produces a portable copy, and `agentfs integrity` +`vfs backup` rejects it unless `--materialize` folds the base bytes in, +`vfs materialize` produces a portable copy, and `vfs integrity` exposes the dependency via `--require-portable` and `--check-base`. #### Tables: `fs_partial_origin` and `fs_chunk_override` @@ -952,7 +952,7 @@ Such extensions SHOULD use separate tables to maintain referential integrity. - Added `inline_threshold` to the required `fs_config` keys - Added partial-origin overlay mode tables (`fs_partial_origin`, `fs_chunk_override`) behind the opt-in `--partial-origin` CLI policy - Whiteout schema requires `parent_path`; legacy `fs_whiteout(path, created_at)` rows are synthesized on migration -- Schema migrations are keyed by `PRAGMA user_version`; `agentfs migrate` lands any supported old schema (v0.0, v0.2, v0.4) at the current version in place, and `--copy` rebuilds with the current chunk layout +- Schema migrations are keyed by `PRAGMA user_version`; `vfs migrate` lands any supported old schema (v0.0, v0.2, v0.4) at the current version in place, and `--copy` rebuilds with the current chunk layout ### Version 0.4 diff --git a/docs/TESTING.md b/docs/TESTING.md index 67cf9d17..3c58aef2 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -1,4 +1,4 @@ -# Testing AgentFS +# Testing Vfs Linux is the first-tier platform: every gate below runs on Linux. macOS is second-tier (NFS mount only) and is covered by the manual release gate at the @@ -13,8 +13,8 @@ first failing command and runs, in order: 2. `cargo +nightly clippy --workspace --all-targets -- -D warnings` 3. `cargo +nightly test --workspace` 4. `cargo +nightly build --release --workspace --bins` -5. `crates/agentfs-cli/tests/all.sh` with `AGENTFS_GATE_STRICT=1` and - `AGENTFS_BIN` pointing at the release binary +5. `crates/vfs-cli/tests/all.sh` with `VFS_GATE_STRICT=1` and + `VFS_BIN` pointing at the release binary (a SKIP is a failure on the designated runner) 6. `scripts/validation/phase8-validation.py --smoke` — the top-level Python gate; it runs the noopen/flush/base-drift coherence harnesses internally @@ -23,9 +23,9 @@ first failing command and runs, in order: logging, env-reads-at-the-config-edge, `await_holding_lock`, lock-order headers, docs layout, changelog) -Knobs: `AGENTFS_BIN` (defaults to `target/release/agentfs`), -`AGENTFS_GATE_SHELL_TIMEOUT` (default 900 s), `AGENTFS_GATE_PHASE8_TIMEOUT` -(default 20 s), `AGENTFS_GATE_ALLOWED_SKIPS` (forwarded to the shell suite, +Knobs: `VFS_BIN` (defaults to `target/release/vfs`), +`VFS_GATE_SHELL_TIMEOUT` (default 900 s), `VFS_GATE_PHASE8_TIMEOUT` +(default 20 s), `VFS_GATE_ALLOWED_SKIPS` (forwarded to the shell suite, see below), and the `CORRUPTION_TORTURE_*` variables forwarded to the shell suite. The gate pins `TMPDIR` to a per-run scratch dir cleaned on exit so dependency temp-file litter cannot accumulate on the host. @@ -34,12 +34,12 @@ CI (`.github/workflows/rust.yml`) runs the workspace job (fmt/clippy/build/test on Linux and macOS, build+test on Linux arm64), the honest milestone gate (`scripts/gate.sh` plus pjdfstest `phase5-ci`), and the release workflow. The gate job first sets `kernel.apparmor_restrict_unprivileged_userns=0` so -the `agentfs run` suites exercise the sandbox instead of skipping on the +the `vfs run` suites exercise the sandbox instead of skipping on the Ubuntu 24.04 runner image. FUSE-over-io_uring coverage stays local-only: the CI kernel exposes `/sys/module/fuse/parameters/enable_uring` but ships it disabled (`N`), so the kernel refuses ring registration, the panic-census uring leg can never run there, and the gate job allowlists that one skip with -`AGENTFS_GATE_ALLOWED_SKIPS=fuse-sigint-panic-census`. The +`VFS_GATE_ALLOWED_SKIPS=fuse-sigint-panic-census`. The `corruption-torture-uring` leg needs no allowlist entry: with `enable_uring=N` the mount falls back to the legacy channel (the fallback is logged at INFO) and the leg passes, exercising uring only on kernels that enable it. A @@ -58,50 +58,50 @@ Two documentation files are generated from code and pinned by unit tests, so doc drift fails the gate: - `docs/KNOBS.md` — regenerate with - `AGENTFS_UPDATE_KNOBS=1 cargo +nightly test -p agentfs-cli --lib knobs::tests::generated_knobs_doc_matches_declarations -- --exact` + `VFS_UPDATE_KNOBS=1 cargo +nightly test -p vfs-cli --lib knobs::tests::generated_knobs_doc_matches_declarations -- --exact` - `docs/MANUAL.md` command reference — regenerate with - `AGENTFS_UPDATE_MANUAL=1 cargo +nightly test -p agentfs-cli --lib docs::tests::manual_help_parity -- --exact` + `VFS_UPDATE_MANUAL=1 cargo +nightly test -p vfs-cli --lib docs::tests::manual_help_parity -- --exact` ## Shell integration suite ```bash -AGENTFS_GATE_STRICT=1 crates/agentfs-cli/tests/all.sh +VFS_GATE_STRICT=1 crates/vfs-cli/tests/all.sh ``` The suite covers init/mount/run/exec flows, syscall coverage, signal -teardown, corruption torture (both `AGENTFS_FUSE_URING=1` and `=0` legs), +teardown, corruption torture (both `VFS_FUSE_URING=1` and `=0` legs), sidecar cleanup, MCP server behavior, and a `cli-smoke` pass over the whole user-level command surface (init, run, exec, clone, fs, timeline, backup/materialize, integrity, migrate, MCP, ps, completions, and the deprecated `nfs`/`mcp-server` aliases), and prints a PASS/SKIP/FAIL summary. Every test runs out of its own `mktemp -d` root with trap cleanup -and honors `AGENTFS_BIN` (falling back to `cargo run`). In strict mode a SKIP +and honors `VFS_BIN` (falling back to `cargo run`). In strict mode a SKIP is red unless its test label is named in -`AGENTFS_GATE_ALLOWED_SKIPS=`, the escape hatch for runner +`VFS_GATE_ALLOWED_SKIPS=`, the escape hatch for runner kernels that cannot provide a prerequisite at all; -`AGENTFS_GATE_FORCE_SKIP=` synthesizes a SKIP for testing the +`VFS_GATE_FORCE_SKIP=` synthesizes a SKIP for testing the runner itself. Never run the corruption torture test concurrently with another mount, test suite, or benchmark. ## Python validation gates -All harnesses take `--agentfs-bin` (or `AGENTFS_BIN`); build a release binary +All harnesses take `--vfs-bin` (or `VFS_BIN`); build a release binary first for anything timing-sensitive. ```bash # Orchestrated Phase 8 policy gate (smoke profile is the milestone gate). # Includes the noopen/flush/base-drift coherence harnesses as named gates. python3 scripts/validation/phase8-validation.py --smoke --timeout 20 \ - --agentfs-bin "$PWD/target/release/agentfs" --output /tmp/vfs-val/phase8.json + --vfs-bin "$PWD/target/release/vfs" --output /tmp/vfs-val/phase8.json # Focused standalone runs of the coherence harnesses: # Default-on FUSE semantics coherence (no-open and no-flush legs) -python3 scripts/validation/noopen-coherence.py --agentfs-bin "$PWD/target/release/agentfs" -python3 scripts/validation/flush-coherence.py --agentfs-bin "$PWD/target/release/agentfs" +python3 scripts/validation/noopen-coherence.py --vfs-bin "$PWD/target/release/vfs" +python3 scripts/validation/flush-coherence.py --vfs-bin "$PWD/target/release/vfs" # Overlay base-drift rejection python3 scripts/validation/external-base-mutation-coherence.py \ - --agentfs-bin "$PWD/target/release/agentfs" \ + --vfs-bin "$PWD/target/release/vfs" \ --output /tmp/vfs-val/external-base-mutation.json ``` @@ -117,7 +117,7 @@ parallelism), `phase8-writeback-durability.py` (fsynced data survives SIGKILL + remount), and `phase8-writeback-no-fsync-crash.py` (no-fsync crash consistency: missing/prefix data allowed, corruption rejected). -`AGENTFS_PROFILE=1` makes AgentFS emit `agentfs_profile_summary` counter +`VFS_PROFILE=1` makes Vfs emit `vfs_profile_summary` counter lines on exit; most harnesses parse and attach them to their JSON reports. ## Benchmarks (local-only policy) @@ -130,7 +130,7 @@ per-phase medians of 5 runs; single runs are noise: cargo +nightly build --release --workspace --bins python3 scripts/validation/git-workload-benchmark-multi.py \ --label bench --iterations 5 --warmup 1 \ - --agentfs-bin "$PWD/target/release/agentfs" \ + --vfs-bin "$PWD/target/release/vfs" \ --source \ --read-files 64 --read-bytes 4096 --edit-files 8 \ --output /tmp/vfs-val/bench-multi.json --keep-iterations @@ -143,11 +143,11 @@ Focused local benchmarks: `git-workload-benchmark.py` (single run with `--profile` phase breakdown), `read-path-benchmark.py`, `large-edit-benchmark.py` (one-byte edit to a large base file must grow the delta DB by O(changed chunks), with `--partial-origin` / `--no-partial-origin` -legs), `base-read-benchmark.py`, and `agentfs-clone-benchmark.py`. +legs), `base-read-benchmark.py`, and `vfs-clone-benchmark.py`. ## pjdfstest -AgentFS keeps three pjdfstest modes: +Vfs keeps three pjdfstest modes: - `phase45-ci`: a conservative, unprivileged supported subset. - `phase5-ci`: the expanded unprivileged supported subset (CI-wired in the @@ -175,7 +175,7 @@ Run the supported gate against a workspace build: ```bash cargo +nightly build --workspace scripts/validation/posix/run-pjdfstest.sh \ - --agentfs-bin "$PWD/target/debug/agentfs" \ + --vfs-bin "$PWD/target/debug/vfs" \ --pjdfstest-dir /path/to/pjdfstest \ --profile phase5-ci ``` @@ -192,22 +192,22 @@ Do not treat `full` as a required gate while known gaps remain. ## Production safety checks ```bash -# SQLite + AgentFS schema invariants (exit nonzero on any failed check) -agentfs integrity .agentfs/my-agent.db --json +# SQLite + Vfs schema invariants (exit nonzero on any failed check) +vfs integrity .vfs/my-agent.db --json # Portable snapshot: checkpoint WAL, copy main DB, reopen, re-verify -agentfs backup .agentfs/my-agent.db /tmp/my-agent-backup.db --verify +vfs backup .vfs/my-agent.db /tmp/my-agent-backup.db --verify ``` Partial-origin overlay databases are rejected by plain `backup` because their contents depend on an external base tree; use `backup --materialize` -or `agentfs materialize` first, and audit the dependency with -`agentfs integrity --require-portable --check-base`. +or `vfs materialize` first, and audit the dependency with +`vfs integrity --require-portable --check-base`. ## macOS: second-tier platform and the manual release gate macOS support is explicitly second-tier: mounting uses the NFS backend only -(no FUSE, no `agentfs ps`), and NFS protocol semantics are validated by cargo +(no FUSE, no `vfs ps`), and NFS protocol semantics are validated by cargo protocol/unit tests on Linux. There is no macOS coverage in the automated gate, so the macOS NFS git validation script is a **manual release gate**: it must be run on real macOS hardware before a release is cut. @@ -215,13 +215,13 @@ must be run on real macOS hardware before a release is cut. ```bash cargo +nightly build --release --workspace --bins scripts/validation/macos-nfs-git-validation.sh \ - --agentfs-bin "$PWD/target/release/agentfs" + --vfs-bin "$PWD/target/release/vfs" ``` -The harness is temp-directory scoped, initializes a fresh AgentFS database, -mounts it with `agentfs mount --backend nfs`, then runs `git init`, +The harness is temp-directory scoped, initializes a fresh Vfs database, +mounts it with `vfs mount --backend nfs`, then runs `git init`, `git add`, `git commit`, and `git fsck --strict`, and verifies at least one -loose object was written. It then verifies the `agentfs run` Seatbelt +loose object was written. It then verifies the `vfs run` Seatbelt read-scoping posture: a secret file in an unallowed directory under `$HOME` must be unreadable from inside the sandbox (permission error, no content leak), and re-running with `--allow ` must make it readable. The @@ -241,6 +241,6 @@ directory with spaces or quotes in its name still mounts and runs); `/System/Volumes/Preboot` has a metadata literal so path resolution down to the dyld cryptex root (`/System/Volumes/Preboot/Cryptexes`) can stat every component (spot-check that dynamically linked binaries start under the -sandbox); and `agentfs run ` must exit `127` (`126` for a -present but non-executable file), matching `agentfs exec` and the Linux run +sandbox); and `vfs run ` must exit `127` (`126` for a +present but non-executable file), matching `vfs exec` and the Linux run path. From e20f23759b24404efd5f34f2a9cd3a2315e7cc1b Mon Sep 17 00:00:00 2001 From: factory-ain3sh Date: Fri, 24 Jul 2026 01:06:45 -0700 Subject: [PATCH 3/8] test: rename validation tooling to vfs Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .github/workflows/rust.yml | 10 +- hawk.toml | 4 +- scripts/gate.sh | 22 +- scripts/install-deps.sh | 2 +- scripts/update-version.py | 2 +- .../validation/archive/backend-risk-spike.py | 12 +- scripts/validation/archive/phase0.sh | 8 +- .../validation/archive/phase6-validation.py | 70 +++--- .../validation/archive/phase65-validation.py | 54 ++--- scripts/validation/base-read-benchmark.py | 156 ++++++------- scripts/validation/bench-compare.py | 22 +- scripts/validation/consistency-canon.sh | 40 ++-- scripts/validation/ddl-census.py | 6 +- .../external-base-mutation-coherence.py | 42 ++-- scripts/validation/flush-coherence.py | 30 +-- .../validation/fuse-serialization-stress.py | 92 ++++---- .../git-workload-benchmark-multi.py | 46 ++-- scripts/validation/git-workload-benchmark.py | 206 +++++++++--------- scripts/validation/large-edit-benchmark.py | 108 ++++----- scripts/validation/lib/common.py | 38 ++-- .../validation/macos-nfs-git-validation.sh | 84 +++---- .../metadata-mutation-no-real-write.py | 54 ++--- scripts/validation/noopen-coherence.py | 46 ++-- .../partial-origin-no-real-write.py | 64 +++--- scripts/validation/phase7-validation.py | 92 ++++---- .../phase8-concurrent-git-stress.py | 104 ++++----- scripts/validation/phase8-validation.py | 46 ++-- .../validation/phase8-writeback-durability.py | 44 ++-- .../phase8-writeback-no-fsync-crash.py | 32 +-- .../validation/posix/pjdfstest/known-gaps.tsv | 4 +- .../validation/posix/pjdfstest/phase45-ci.txt | 2 +- .../validation/posix/pjdfstest/phase5-ci.txt | 2 +- scripts/validation/posix/run-pjdfstest.sh | 52 ++--- scripts/validation/read-path-benchmark.py | 146 ++++++------- scripts/validation/replay/replay_workload.py | 58 ++--- scripts/validation/vfs-clone-benchmark.py | 54 ++--- scripts/validation/workload-baseline.py | 116 +++++----- 37 files changed, 985 insertions(+), 985 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 89453d5e..78d14672 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -97,7 +97,7 @@ jobs: - name: Allow unprivileged user namespaces run: | # Ubuntu 24.04 runner images gate unprivileged user namespaces behind - # AppArmor; without this the agentfs run suites SKIP and the strict + # AppArmor; without this the vfs run suites SKIP and the strict # gate goes red instead of exercising the sandbox for real. sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 @@ -120,10 +120,10 @@ jobs: # fails the strict gate. The corruption-torture uring leg needs no # entry: with enable_uring=N the mount falls back to the legacy # channel and the leg passes. - AGENTFS_GATE_ALLOWED_SKIPS: fuse-sigint-panic-census + VFS_GATE_ALLOWED_SKIPS: fuse-sigint-panic-census run: | - # scripts/gate.sh runs crates/agentfs-cli/tests/all.sh with - # AGENTFS_GATE_STRICT=1, then phase8-validation.py --smoke (which + # scripts/gate.sh runs crates/vfs-cli/tests/all.sh with + # VFS_GATE_STRICT=1, then phase8-validation.py --smoke (which # includes the noopen/flush/base-drift coherence harnesses) and the # consistency-canon census. scripts/gate.sh @@ -131,7 +131,7 @@ jobs: - name: Run pjdfstest phase5-ci run: | scripts/validation/posix/run-pjdfstest.sh \ - --agentfs-bin "$PWD/target/release/agentfs" \ + --vfs-bin "$PWD/target/release/vfs" \ --pjdfstest-dir /tmp/pjdfstest \ --profile phase5-ci diff --git a/hawk.toml b/hawk.toml index bbce5a09..03990dbd 100644 --- a/hawk.toml +++ b/hawk.toml @@ -1,4 +1,4 @@ [[production]] -package = "agentfs-cli" -bin = "agentfs" +package = "vfs-cli" +bin = "vfs" reason = "shipped CLI binary" diff --git a/scripts/gate.sh b/scripts/gate.sh index bcb27b45..c9e0842a 100755 --- a/scripts/gate.sh +++ b/scripts/gate.sh @@ -11,15 +11,15 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$REPO_ROOT" -AGENTFS_BIN="${AGENTFS_BIN:-$REPO_ROOT/target/release/agentfs}" +VFS_BIN="${VFS_BIN:-$REPO_ROOT/target/release/vfs}" RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-nightly}" -SHELL_TIMEOUT="${AGENTFS_GATE_SHELL_TIMEOUT:-900}" -PHASE8_TIMEOUT="${AGENTFS_GATE_PHASE8_TIMEOUT:-20}" +SHELL_TIMEOUT="${VFS_GATE_SHELL_TIMEOUT:-900}" +PHASE8_TIMEOUT="${VFS_GATE_PHASE8_TIMEOUT:-20}" # Pin TMPDIR to a per-run scratch dir cleaned on exit: turso_core 0.5.3 leaks # /tmp/tursodb-ephemeral-* sort-spill files (vdbe/execute.rs:10096 never # unlinks them), so dependency litter must not accumulate on the host. -GATE_TMPDIR="$(mktemp -d "${TMPDIR:-/tmp}/agentfs-gate.XXXXXX")" +GATE_TMPDIR="$(mktemp -d "${TMPDIR:-/tmp}/vfs-gate.XXXXXX")" trap 'rm -rf "$GATE_TMPDIR"' EXIT export TMPDIR="$GATE_TMPDIR" TMP="$GATE_TMPDIR" TEMP="$GATE_TMPDIR" export PYTHONDONTWRITEBYTECODE=1 @@ -39,24 +39,24 @@ run_cargo clippy --workspace --all-targets -- -D warnings run_cargo test --workspace run_cargo build --release --workspace --bins -printf '\n==> crates/agentfs-cli/tests/all.sh\n' -AGENTFS_GATE_STRICT=1 \ -AGENTFS_GATE_ALLOWED_SKIPS="${AGENTFS_GATE_ALLOWED_SKIPS:-}" \ -AGENTFS_BIN="$AGENTFS_BIN" \ +printf '\n==> crates/vfs-cli/tests/all.sh\n' +VFS_GATE_STRICT=1 \ +VFS_GATE_ALLOWED_SKIPS="${VFS_GATE_ALLOWED_SKIPS:-}" \ +VFS_BIN="$VFS_BIN" \ CORRUPTION_TORTURE_WORKERS="${CORRUPTION_TORTURE_WORKERS:-4}" \ CORRUPTION_TORTURE_ITERATIONS="${CORRUPTION_TORTURE_ITERATIONS:-3}" \ CORRUPTION_TORTURE_TIMEOUT="${CORRUPTION_TORTURE_TIMEOUT:-120}" \ CORRUPTION_TORTURE_TEARDOWN_TIMEOUT="${CORRUPTION_TORTURE_TEARDOWN_TIMEOUT:-10}" \ -timeout "$SHELL_TIMEOUT" crates/agentfs-cli/tests/all.sh +timeout "$SHELL_TIMEOUT" crates/vfs-cli/tests/all.sh # Phase 8 smoke is the top-level python gate; the noopen/flush/base-drift # coherence harnesses run inside it (M7 scripts consolidation). run python3 scripts/validation/phase8-validation.py \ --smoke \ --timeout "$PHASE8_TIMEOUT" \ - --agentfs-bin "$AGENTFS_BIN" \ + --vfs-bin "$VFS_BIN" \ --output /tmp/vfs-val/phase8.json run scripts/validation/consistency-canon.sh -printf '\nHonest gate passed with AGENTFS_BIN=%s\n' "$AGENTFS_BIN" +printf '\nHonest gate passed with VFS_BIN=%s\n' "$VFS_BIN" diff --git a/scripts/install-deps.sh b/scripts/install-deps.sh index 20ca6ea1..2cf5dcb4 100755 --- a/scripts/install-deps.sh +++ b/scripts/install-deps.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -# Install system dependencies for agentfs +# Install system dependencies for vfs if [[ "$OSTYPE" == "linux-gnu"* ]]; then echo "Installing dependencies for Linux..." diff --git a/scripts/update-version.py b/scripts/update-version.py index d076f66c..30523f59 100755 --- a/scripts/update-version.py +++ b/scripts/update-version.py @@ -70,7 +70,7 @@ def refresh_lockfile(root: Path, dry_run: bool) -> None: def main() -> int: - parser = argparse.ArgumentParser(description="Update the AgentFS workspace version") + parser = argparse.ArgumentParser(description="Update the Vfs workspace version") parser.add_argument("version", help="Version number, for example 0.6.5 or 0.7.0-pre.1") parser.add_argument("--dry-run", action="store_true", help="Print changes without writing files") args = parser.parse_args() diff --git a/scripts/validation/archive/backend-risk-spike.py b/scripts/validation/archive/backend-risk-spike.py index 62e22fd7..672ba9ed 100755 --- a/scripts/validation/archive/backend-risk-spike.py +++ b/scripts/validation/archive/backend-risk-spike.py @@ -339,13 +339,13 @@ def main(argv: list[str]) -> int: }, }, "recommended_validation_commands": [ - "cargo +nightly test -p agentfs-core", - "cargo +nightly test -p agentfs-cli", - "cargo +nightly test -p agentfs-cli --no-default-features", - "crates/agentfs-cli/tests/all.sh", + "cargo +nightly test -p vfs-core", + "cargo +nightly test -p vfs-cli", + "cargo +nightly test -p vfs-cli --no-default-features", + "crates/vfs-cli/tests/all.sh", "scripts/validation/phase0.sh", - "scripts/validation/replay/replay_workload.py --agentfs-bin target/debug/agentfs /path/to/replay.jsonl", - "scripts/validation/posix/run-pjdfstest.sh --profile phase45-ci --agentfs-bin \"$PWD/target/debug/agentfs\" --pjdfstest-dir /path/to/pjdfstest", + "scripts/validation/replay/replay_workload.py --vfs-bin target/debug/vfs /path/to/replay.jsonl", + "scripts/validation/posix/run-pjdfstest.sh --profile phase45-ci --vfs-bin \"$PWD/target/debug/vfs\" --pjdfstest-dir /path/to/pjdfstest", ], "decision": { "status": args.decision_status, diff --git a/scripts/validation/archive/phase0.sh b/scripts/validation/archive/phase0.sh index b2ff01ca..1264e089 100755 --- a/scripts/validation/archive/phase0.sh +++ b/scripts/validation/archive/phase0.sh @@ -7,10 +7,10 @@ Usage: phase0.sh Runs the Phase 0/1 local validation smoke: 1. Phase 1 fork governance check - 2. Phase 0 built-in native-vs-AgentFS synthetic workload baseline + 2. Phase 0 built-in native-vs-Vfs synthetic workload baseline Environment: - AGENTFS_BIN optional agentfs executable path/name + VFS_BIN optional vfs executable path/name WORKLOAD_BASELINE_ITERATIONS smoke iterations (default: 1) WORKLOAD_BASELINE_TIMEOUT per-command timeout seconds (default: 120) WORKLOAD_BASELINE_KEEP_TEMP keep temp baseline directories when true/1 @@ -48,7 +48,7 @@ cat <<'NEXT_STEPS' == Next steps for real factory-mono baselines == Run a representative command against a real checkout, for example: - AGENTFS_BIN=/path/to/agentfs \ + VFS_BIN=/path/to/vfs \ scripts/validation/workload-baseline.py \ --source /path/to/factory-mono \ --command 'your representative build/test command' @@ -56,7 +56,7 @@ Run a representative command against a real checkout, for example: Notes: - By default the source tree is copied into temp directories before timing. - Add --exclude PATTERN for large caches that should not be part of the baseline copy. - - Use --keep-temp when you need to inspect the native and AgentFS worktrees. + - Use --keep-temp when you need to inspect the native and Vfs worktrees. - Use --in-place-native only for known read-only workloads. NEXT_STEPS diff --git a/scripts/validation/archive/phase6-validation.py b/scripts/validation/archive/phase6-validation.py index a369458b..297aae7e 100755 --- a/scripts/validation/archive/phase6-validation.py +++ b/scripts/validation/archive/phase6-validation.py @@ -33,7 +33,7 @@ max_files = int(os.environ.get("PHASE6_FACTORY_MAX_FILES", "512")) scan_bytes = int(os.environ.get("PHASE6_FACTORY_SCAN_BYTES", "4096")) skip_names = { - ".agentfs", + ".vfs", ".direnv", ".git", ".next", @@ -125,9 +125,9 @@ def parse_args(argv: list[str]) -> argparse.Namespace: """, ) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -201,7 +201,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: ) parser.add_argument( "--output", - help="write JSON result to this file; defaults to /tmp/agentfs-phase6-validation-*.json", + help="write JSON result to this file; defaults to /tmp/vfs-phase6-validation-*.json", ) parser.add_argument( "--json-indent", @@ -292,20 +292,20 @@ def run_subprocess( } -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate_path = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate_path = Path(vfs_bin).expanduser() if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") for candidate_path in ( - repo_root / "cli" / "target" / "debug" / "agentfs", - repo_root / "cli" / "target" / "release" / "agentfs", + repo_root / "cli" / "target" / "debug" / "vfs", + repo_root / "cli" / "target" / "release" / "vfs", ): if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path) @@ -319,12 +319,12 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN to an explicit binary\n" + "failed to build repo-local vfs binary; set VFS_BIN to an explicit binary\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = repo_root / "cli" / "target" / "debug" / "agentfs" + built = repo_root / "cli" / "target" / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) @@ -346,7 +346,7 @@ def git_commit(repo_root: Path) -> Optional[str]: def default_output_path() -> Path: stamp = time.strftime("%Y%m%d-%H%M%S") - return Path(tempfile.gettempdir()) / f"agentfs-phase6-validation-{stamp}-{uuid.uuid4().hex[:8]}.json" + return Path(tempfile.gettempdir()) / f"vfs-phase6-validation-{stamp}-{uuid.uuid4().hex[:8]}.json" def table_exists(conn: sqlite3.Connection, name: str) -> bool: @@ -418,11 +418,11 @@ def load_json(path: Path) -> Any: return json.loads(path.read_text(encoding="utf-8")) -def child_env(agentfs_bin: str) -> dict[str, str]: +def child_env(vfs_bin: str) -> dict[str, str]: env = os.environ.copy() env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") - env["AGENTFS_BIN"] = agentfs_bin + env["VFS_BIN"] = vfs_bin return env @@ -549,7 +549,7 @@ def run_read_path( if summary.get("all_equivalent") is not True: status = "failed" for mode in payload.get("modes", []): - counters = mode.get("agentfs", {}).get("profile_counters", {}).get("max_counters", {}) + counters = mode.get("vfs", {}).get("profile_counters", {}).get("max_counters", {}) if counters.get("chunk_read_queries", 0) != 0 or counters.get("chunk_read_chunks", 0) != 0: status = "failed" return { @@ -600,8 +600,8 @@ def run_large_edit( stored = int(inspect.get("fs_data_bytes", 0) or 0) override_rows = int(inspect.get("fs_chunk_override_rows", 0) or 0) native_seconds = payload.get("native", {}).get("run", {}).get("duration_seconds", 0) - agentfs_seconds = payload.get("agentfs_overlay", {}).get("run", {}).get("duration_seconds", 0) - ratio = (agentfs_seconds / native_seconds) if native_seconds else None + vfs_seconds = payload.get("vfs_overlay", {}).get("run", {}).get("duration_seconds", 0) + ratio = (vfs_seconds / native_seconds) if native_seconds else None if stored > 128 * 1024 or override_rows > 1 or ratio is None or ratio > 15.0: status = "failed" return { @@ -642,8 +642,8 @@ def run_no_real_write( } -def materialize_help(agentfs_bin: str, repo_root: Path, env: dict[str, str]) -> dict[str, Any]: - run = run_subprocess([agentfs_bin, "materialize", "--help"], repo_root, env, 30) +def materialize_help(vfs_bin: str, repo_root: Path, env: dict[str, str]) -> dict[str, Any]: + run = run_subprocess([vfs_bin, "materialize", "--help"], repo_root, env, 30) return { "available": run["returncode"] == 0, "probe": run, @@ -656,14 +656,14 @@ def run_materialize_if_available( env: dict[str, str], output_dir: Path, file_size_mib: int, - agentfs_bin: str, + vfs_bin: str, ) -> dict[str, Any]: - help_result = materialize_help(agentfs_bin, repo_root, env) + help_result = materialize_help(vfs_bin, repo_root, env) if not help_result["available"]: return { "name": "materialize_benchmark", "status": "skipped", - "reason": "agentfs materialize command is not available", + "reason": "vfs materialize command is not available", "probe": help_result["probe"], } @@ -685,9 +685,9 @@ def run_materialize_if_available( "setup_result": setup_payload, } - db_path = setup_payload.get("agentfs", {}).get("db_path") + db_path = setup_payload.get("vfs", {}).get("db_path") target_db = output_dir / "materialized.db" - command = [agentfs_bin, "materialize", str(db_path), "--output", str(target_db), "--verify"] + command = [vfs_bin, "materialize", str(db_path), "--output", str(target_db), "--verify"] run = run_subprocess(command, repo_root, env, args.timeout * 2 + 30) inspect = inspect_db(target_db) port_status = portability_status(inspect) @@ -748,16 +748,16 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - output_dir = Path(tempfile.mkdtemp(prefix="agentfs-phase6-validation-")) + output_dir = Path(tempfile.mkdtemp(prefix="vfs-phase6-validation-")) else: - temp_manager = tempfile.TemporaryDirectory(prefix="agentfs-phase6-validation-") + temp_manager = tempfile.TemporaryDirectory(prefix="vfs-phase6-validation-") output_dir = Path(temp_manager.name) exit_code = 0 result: dict[str, Any] try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) - env = child_env(agentfs_bin) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) + env = child_env(vfs_bin) runs: dict[str, dict[str, Any]] = {} runs["factory_bounded_read"] = run_factory_bounded_read(args, repo_root, env, output_dir) runs["read_path_profile"] = run_read_path(args, repo_root, env, output_dir) @@ -771,7 +771,7 @@ def main(argv: list[str]) -> int: args, repo_root, env, output_dir, file_size_mib ) runs["materialize_benchmark"] = run_materialize_if_available( - args, repo_root, env, output_dir, file_size_mib, agentfs_bin + args, repo_root, env, output_dir, file_size_mib, vfs_bin ) failed = [ @@ -797,8 +797,8 @@ def main(argv: list[str]) -> int: "factory_max_files": args.factory_max_files, "factory_scan_bytes": args.factory_scan_bytes, }, - "agentfs": { - "bin": agentfs_bin, + "vfs": { + "bin": vfs_bin, }, "summary": { "passed": exit_code == 0, diff --git a/scripts/validation/archive/phase65-validation.py b/scripts/validation/archive/phase65-validation.py index 84a60a64..40d0e2f0 100755 --- a/scripts/validation/archive/phase65-validation.py +++ b/scripts/validation/archive/phase65-validation.py @@ -31,7 +31,7 @@ max_files = int(os.environ.get("PHASE65_FACTORY_MAX_FILES", "512")) scan_bytes = int(os.environ.get("PHASE65_FACTORY_SCAN_BYTES", "4096")) skip_names = { - ".agentfs", + ".vfs", ".direnv", ".git", ".next", @@ -119,9 +119,9 @@ def parse_args(argv: list[str]) -> argparse.Namespace: """, ) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -252,20 +252,20 @@ def run_subprocess(argv: list[str], cwd: Path, env: dict[str, str], timeout: flo } -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate_path = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate_path = Path(vfs_bin).expanduser() if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") for candidate_path in ( - repo_root / "cli" / "target" / "debug" / "agentfs", - repo_root / "cli" / "target" / "release" / "agentfs", + repo_root / "cli" / "target" / "debug" / "vfs", + repo_root / "cli" / "target" / "release" / "vfs", ): if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path) @@ -279,12 +279,12 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN to an explicit binary\n" + "failed to build repo-local vfs binary; set VFS_BIN to an explicit binary\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = repo_root / "cli" / "target" / "debug" / "agentfs" + built = repo_root / "cli" / "target" / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) raise RuntimeError(f"repo-local build completed but binary was not found: {built}") @@ -321,17 +321,17 @@ def parse_json_stdout_tail(run: dict[str, Any]) -> Optional[dict[str, Any]]: return None -def child_env(agentfs_bin: str) -> dict[str, str]: +def child_env(vfs_bin: str) -> dict[str, str]: env = os.environ.copy() env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") - env["AGENTFS_BIN"] = agentfs_bin + env["VFS_BIN"] = vfs_bin return env def default_output_path() -> Path: stamp = time.strftime("%Y%m%d-%H%M%S") - return Path(tempfile.gettempdir()) / f"agentfs-phase65-validation-{stamp}-{uuid.uuid4().hex[:8]}.json" + return Path(tempfile.gettempdir()) / f"vfs-phase65-validation-{stamp}-{uuid.uuid4().hex[:8]}.json" def factory_command(args: argparse.Namespace) -> str: @@ -386,8 +386,8 @@ def run_factory_bounded_read( coverage_ok = True for iteration in payload.get("iterations", []): native_json = parse_json_stdout_tail(iteration.get("native", {})) - agentfs_json = parse_json_stdout_tail(iteration.get("agentfs", {})) - for workload_json in (native_json, agentfs_json): + vfs_json = parse_json_stdout_tail(iteration.get("vfs", {})) + for workload_json in (native_json, vfs_json): if ( not isinstance(workload_json, dict) or int(workload_json.get("files", 0) or 0) <= 0 @@ -424,7 +424,7 @@ def read_path_chunk_counters(payload: Optional[dict[str, Any]]) -> dict[str, Any if not isinstance(payload, dict): return counters for mode in payload.get("modes", []): - profile_counters = mode.get("agentfs", {}).get("profile_counters", {}) + profile_counters = mode.get("vfs", {}).get("profile_counters", {}) if int(profile_counters.get("summary_count", 0) or 0) <= 0: continue max_counters = profile_counters.get("max_counters", {}) @@ -532,7 +532,7 @@ def run_base_read( payload = load_json(output_path) if output_path.exists() else None status = "passed" if run["returncode"] == 0 else "failed" summary = payload.get("summary", {}) if isinstance(payload, dict) else {} - passthrough = payload.get("agentfs", {}).get("passthrough", {}) if isinstance(payload, dict) else {} + passthrough = payload.get("vfs", {}).get("passthrough", {}) if isinstance(payload, dict) else {} repeated_ratio = summary.get("repeated_open_read_workload_ratio") chunk_read_queries = int(summary.get("chunk_read_queries", 1) or 0) chunk_read_chunks = int(summary.get("chunk_read_chunks", 1) or 0) @@ -579,16 +579,16 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - output_dir = Path(tempfile.mkdtemp(prefix="agentfs-phase65-validation-")) + output_dir = Path(tempfile.mkdtemp(prefix="vfs-phase65-validation-")) else: - temp_manager = tempfile.TemporaryDirectory(prefix="agentfs-phase65-validation-") + temp_manager = tempfile.TemporaryDirectory(prefix="vfs-phase65-validation-") output_dir = Path(temp_manager.name) exit_code = 0 result: dict[str, Any] try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) - env = child_env(agentfs_bin) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) + env = child_env(vfs_bin) runs: dict[str, dict[str, Any]] = {} runs["factory_bounded_read"] = run_factory_bounded_read(args, repo_root, env, output_dir) runs["controlled_read_metadata"] = run_controlled_read_metadata(args, repo_root, env, output_dir) @@ -612,8 +612,8 @@ def main(argv: list[str]) -> int: "factory_max_files": args.factory_max_files, "factory_scan_bytes": args.factory_scan_bytes, }, - "agentfs": { - "bin": agentfs_bin, + "vfs": { + "bin": vfs_bin, "passthrough": passthrough, }, "summary": { diff --git a/scripts/validation/base-read-benchmark.py b/scripts/validation/base-read-benchmark.py index b1a12ddc..a79dbc13 100755 --- a/scripts/validation/base-read-benchmark.py +++ b/scripts/validation/base-read-benchmark.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Phase 6.5 native-vs-AgentFS unchanged-base read benchmark.""" +"""Phase 6.5 native-vs-Vfs unchanged-base read benchmark.""" from __future__ import annotations @@ -201,7 +201,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( description=( "Compare repeated read-only open/read/close and read-after-mutate " - "cache invalidation on native storage and an unchanged AgentFS base file." + "cache invalidation on native storage and an unchanged Vfs base file." ) ) parser.add_argument("--file-size-bytes", type=positive_int, default=65536) @@ -211,16 +211,16 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser.add_argument("--invalidation-post-reads", type=positive_int, default=3) parser.add_argument("--mutation-offset", type=non_negative_int, default=0) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", type=positive_float, default=positive_float(os.environ.get("BASE_READ_BENCHMARK_TIMEOUT", "120")), ) - parser.add_argument("--profile", action="store_true", default=env_flag("AGENTFS_PROFILE")) + parser.add_argument("--profile", action="store_true", default=env_flag("VFS_PROFILE")) parser.add_argument("--session-prefix", default=None) parser.add_argument( "--keep-temp", @@ -260,13 +260,13 @@ def extract_profile_summaries(stderr: Any) -> list[dict[str, Any]]: summaries: list[dict[str, Any]] = [] for line in text.splitlines(): line = line.strip() - if not line or "agentfs_profile_summary" not in line: + if not line or "vfs_profile_summary" not in line: continue try: value = json.loads(line) except json.JSONDecodeError: continue - if isinstance(value, dict) and value.get("event") == "agentfs_profile_summary": + if isinstance(value, dict) and value.get("event") == "vfs_profile_summary": summaries.append(value) return summaries @@ -388,20 +388,20 @@ def parse_json_stdout(run: dict[str, Any]) -> Optional[dict[str, Any]]: return None -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate_path = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate_path = Path(vfs_bin).expanduser() if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") for candidate_path in ( - repo_root / "cli" / "target" / "debug" / "agentfs", - repo_root / "cli" / "target" / "release" / "agentfs", + repo_root / "cli" / "target" / "debug" / "vfs", + repo_root / "cli" / "target" / "release" / "vfs", ): if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path) @@ -415,12 +415,12 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN to an explicit binary\n" + "failed to build repo-local vfs binary; set VFS_BIN to an explicit binary\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = repo_root / "cli" / "target" / "debug" / "agentfs" + built = repo_root / "cli" / "target" / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) raise RuntimeError(f"repo-local build completed but binary was not found: {built}") @@ -444,7 +444,7 @@ def prepare_environment(temp_root: Path, profile: bool) -> dict[str, str]: env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") if profile: - env["AGENTFS_PROFILE"] = "1" + env["VFS_PROFILE"] = "1" home = temp_root / "home" for path in (home, home / ".config", home / ".cache", home / ".local" / "share"): @@ -470,7 +470,7 @@ def create_fixture(root: Path, file_size_bytes: int) -> str: index = 0 with path.open("wb") as handle: while written < file_size_bytes: - seed = hashlib.sha256(f"agentfs-phase65-base-read-{index}".encode()).digest() + seed = hashlib.sha256(f"vfs-phase65-base-read-{index}".encode()).digest() block = (seed * 4096)[: min(65536, file_size_bytes - written)] handle.write(block) digest.update(block) @@ -590,43 +590,43 @@ def split_timing(run: dict[str, Any], workload: Optional[dict[str, Any]]) -> dic } -def compare_read_workloads(native: Optional[dict[str, Any]], agentfs: Optional[dict[str, Any]]) -> dict[str, Any]: - if native is None or agentfs is None: +def compare_read_workloads(native: Optional[dict[str, Any]], vfs: Optional[dict[str, Any]]) -> dict[str, Any]: + if native is None or vfs is None: return {"checked": False, "equivalent": False, "reason": "missing JSON workload output"} equivalent = ( - native.get("digest") == agentfs.get("digest") - and native.get("counts") == agentfs.get("counts") - and native.get("parameters") == agentfs.get("parameters") + native.get("digest") == vfs.get("digest") + and native.get("counts") == vfs.get("counts") + and native.get("parameters") == vfs.get("parameters") ) return { "checked": True, "equivalent": equivalent, "native_digest": native.get("digest"), - "agentfs_digest": agentfs.get("digest"), + "vfs_digest": vfs.get("digest"), } -def compare_invalidation_workloads(native: Optional[dict[str, Any]], agentfs: Optional[dict[str, Any]]) -> dict[str, Any]: - if native is None or agentfs is None: +def compare_invalidation_workloads(native: Optional[dict[str, Any]], vfs: Optional[dict[str, Any]]) -> dict[str, Any]: + if native is None or vfs is None: return {"checked": False, "equivalent": False, "reason": "missing JSON workload output"} fields = ("sha256_after", "old_byte", "new_byte", "stale_reads", "mutation_visible") - equivalent = all(native.get(field) == agentfs.get(field) for field in fields) + equivalent = all(native.get(field) == vfs.get(field) for field in fields) return { "checked": True, "equivalent": equivalent, - "fields": {field: {"native": native.get(field), "agentfs": agentfs.get(field)} for field in fields}, + "fields": {field: {"native": native.get(field), "vfs": vfs.get(field)} for field in fields}, } -def ratio(agentfs_seconds: Optional[float], native_seconds: Optional[float]) -> Optional[float]: - if native_seconds is None or agentfs_seconds is None or native_seconds <= 0: +def ratio(vfs_seconds: Optional[float], native_seconds: Optional[float]) -> Optional[float]: + if native_seconds is None or vfs_seconds is None or native_seconds <= 0: return None - return agentfs_seconds / native_seconds + return vfs_seconds / native_seconds def default_output_path() -> Path: stamp = time.strftime("%Y%m%d-%H%M%S") - return Path(tempfile.gettempdir()) / f"agentfs-base-read-benchmark-{stamp}-{uuid.uuid4().hex[:8]}.json" + return Path(tempfile.gettempdir()) / f"vfs-base-read-benchmark-{stamp}-{uuid.uuid4().hex[:8]}.json" def main(argv: list[str]) -> int: @@ -636,41 +636,41 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-base-read-benchmark-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-base-read-benchmark-")) else: - temp_manager = tempfile.TemporaryDirectory(prefix="agentfs-base-read-benchmark-") + temp_manager = tempfile.TemporaryDirectory(prefix="vfs-base-read-benchmark-") temp_root = Path(temp_manager.name) exit_code = 0 result: dict[str, Any] try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) env = prepare_environment(temp_root, args.profile) source_root = temp_root / "source" native_root = temp_root / "native" - agentfs_base_root = temp_root / "agentfs-base" + vfs_base_root = temp_root / "vfs-base" original_sha = create_fixture(source_root, args.file_size_bytes) copy_fixture(source_root, native_root) - copy_fixture(source_root, agentfs_base_root) - agentfs_base_before = tree_hash(agentfs_base_root) + copy_fixture(source_root, vfs_base_root) + vfs_base_before = tree_hash(vfs_base_root) session_prefix = args.session_prefix or f"base-read-{uuid.uuid4().hex}" read_argv = read_workload_argv(args.iterations, args.read_bytes) native_read_run = run_subprocess(read_argv, native_root, env, args.timeout) - agentfs_read_run = run_subprocess( - [agentfs_bin, "run", "--session", f"{session_prefix}-repeat", "--no-default-allows", "--"] + read_argv, - agentfs_base_root, + vfs_read_run = run_subprocess( + [vfs_bin, "run", "--session", f"{session_prefix}-repeat", "--no-default-allows", "--"] + read_argv, + vfs_base_root, env, args.timeout, ) native_read_payload = parse_json_stdout(native_read_run) - agentfs_read_payload = parse_json_stdout(agentfs_read_run) - read_equivalence = compare_read_workloads(native_read_payload, agentfs_read_payload) - read_profile = profile_counter_summary(agentfs_read_run.get("profile_summaries", [])) + vfs_read_payload = parse_json_stdout(vfs_read_run) + read_equivalence = compare_read_workloads(native_read_payload, vfs_read_payload) + read_profile = profile_counter_summary(vfs_read_run.get("profile_summaries", [])) read_counters = read_profile["max_counters"] repeated_passed = ( native_read_run["returncode"] == 0 - and agentfs_read_run["returncode"] == 0 + and vfs_read_run["returncode"] == 0 and read_equivalence.get("equivalent") is True and int(read_counters.get("chunk_read_queries", 0) or 0) == 0 and int(read_counters.get("chunk_read_chunks", 0) or 0) == 0 @@ -678,33 +678,33 @@ def main(argv: list[str]) -> int: invalidation_argv = invalidation_workload_argv(args) native_invalidation_run = run_subprocess(invalidation_argv, native_root, env, args.timeout) - agentfs_invalidation_run = run_subprocess( - [agentfs_bin, "run", "--session", f"{session_prefix}-invalidate", "--no-default-allows", "--"] + vfs_invalidation_run = run_subprocess( + [vfs_bin, "run", "--session", f"{session_prefix}-invalidate", "--no-default-allows", "--"] + invalidation_argv, - agentfs_base_root, + vfs_base_root, env, args.timeout, ) native_invalidation_payload = parse_json_stdout(native_invalidation_run) - agentfs_invalidation_payload = parse_json_stdout(agentfs_invalidation_run) + vfs_invalidation_payload = parse_json_stdout(vfs_invalidation_run) invalidation_equivalence = compare_invalidation_workloads( - native_invalidation_payload, agentfs_invalidation_payload + native_invalidation_payload, vfs_invalidation_payload ) - agentfs_base_sha_after = hash_file(agentfs_base_root / "hot.bin") - agentfs_base_after = tree_hash(agentfs_base_root) + vfs_base_sha_after = hash_file(vfs_base_root / "hot.bin") + vfs_base_after = tree_hash(vfs_base_root) native_sha_after = hash_file(native_root / "hot.bin") stale_reads = ( - int(agentfs_invalidation_payload.get("stale_reads", 1)) - if isinstance(agentfs_invalidation_payload, dict) + int(vfs_invalidation_payload.get("stale_reads", 1)) + if isinstance(vfs_invalidation_payload, dict) else 1 ) - invalidation_profile = profile_counter_summary(agentfs_invalidation_run.get("profile_summaries", [])) + invalidation_profile = profile_counter_summary(vfs_invalidation_run.get("profile_summaries", [])) invalidation_passed = ( native_invalidation_run["returncode"] == 0 - and agentfs_invalidation_run["returncode"] == 0 + and vfs_invalidation_run["returncode"] == 0 and invalidation_equivalence.get("equivalent") is True and stale_reads == 0 - and agentfs_base_after["sha256"] == agentfs_base_before["sha256"] + and vfs_base_after["sha256"] == vfs_base_before["sha256"] and native_sha_after != original_sha ) @@ -716,9 +716,9 @@ def main(argv: list[str]) -> int: if native_read_payload and isinstance(native_read_payload.get("total_seconds"), (int, float)) else None ) - agentfs_workload_seconds = ( - float(agentfs_read_payload["total_seconds"]) - if agentfs_read_payload and isinstance(agentfs_read_payload.get("total_seconds"), (int, float)) + vfs_workload_seconds = ( + float(vfs_read_payload["total_seconds"]) + if vfs_read_payload and isinstance(vfs_read_payload.get("total_seconds"), (int, float)) else None ) @@ -734,8 +734,8 @@ def main(argv: list[str]) -> int: "invalidation_post_reads": args.invalidation_post_reads, "mutation_offset": args.mutation_offset, }, - "agentfs": { - "bin": agentfs_bin, + "vfs": { + "bin": vfs_bin, "profile_enabled": args.profile, "passthrough": passthrough_status(read_counters), }, @@ -749,8 +749,8 @@ def main(argv: list[str]) -> int: ) if not passed ], - "repeated_open_read_ratio": ratio(agentfs_read_run["duration_seconds"], native_read_run["duration_seconds"]), - "repeated_open_read_workload_ratio": ratio(agentfs_workload_seconds, native_workload_seconds), + "repeated_open_read_ratio": ratio(vfs_read_run["duration_seconds"], native_read_run["duration_seconds"]), + "repeated_open_read_workload_ratio": ratio(vfs_workload_seconds, native_workload_seconds), "chunk_read_queries": int(read_counters.get("chunk_read_queries", 0) or 0), "chunk_read_chunks": int(read_counters.get("chunk_read_chunks", 0) or 0), "stale_reads": stale_reads, @@ -763,10 +763,10 @@ def main(argv: list[str]) -> int: "workload": native_read_payload, "timing": split_timing(native_read_run, native_read_payload), }, - "agentfs": { - "run": agentfs_read_run, - "workload": agentfs_read_payload, - "timing": split_timing(agentfs_read_run, agentfs_read_payload), + "vfs": { + "run": vfs_read_run, + "workload": vfs_read_payload, + "timing": split_timing(vfs_read_run, vfs_read_payload), "profile_counters": read_profile, }, "equivalence": read_equivalence, @@ -778,20 +778,20 @@ def main(argv: list[str]) -> int: "workload": native_invalidation_payload, "timing": split_timing(native_invalidation_run, native_invalidation_payload), }, - "agentfs": { - "run": agentfs_invalidation_run, - "workload": agentfs_invalidation_payload, - "timing": split_timing(agentfs_invalidation_run, agentfs_invalidation_payload), + "vfs": { + "run": vfs_invalidation_run, + "workload": vfs_invalidation_payload, + "timing": split_timing(vfs_invalidation_run, vfs_invalidation_payload), "profile_counters": invalidation_profile, }, "equivalence": invalidation_equivalence, "base_file": { "original_sha256": original_sha, "native_sha256_after": native_sha_after, - "agentfs_base_sha256_after": agentfs_base_sha_after, - "agentfs_base_unchanged": agentfs_base_after["sha256"] == agentfs_base_before["sha256"], - "agentfs_base_tree_before": agentfs_base_before, - "agentfs_base_tree_after": agentfs_base_after, + "vfs_base_sha256_after": vfs_base_sha_after, + "vfs_base_unchanged": vfs_base_after["sha256"] == vfs_base_before["sha256"], + "vfs_base_tree_before": vfs_base_before, + "vfs_base_tree_after": vfs_base_after, }, }, }, diff --git a/scripts/validation/bench-compare.py b/scripts/validation/bench-compare.py index ba17649c..22545e49 100755 --- a/scripts/validation/bench-compare.py +++ b/scripts/validation/bench-compare.py @@ -29,7 +29,7 @@ class BenchmarkFormatError(ValueError): @dataclass(frozen=True) class PhaseMedian: phase: str - agentfs_seconds: float + vfs_seconds: float count: int @@ -103,22 +103,22 @@ def parse_phase_medians(payload: dict[str, Any], source: Path) -> dict[str, Phas raise BenchmarkFormatError(f"{source}: phase name must be a string") if not isinstance(phase_payload, dict): raise BenchmarkFormatError(f"{source}: phases.{phase} must be an object") - agentfs = phase_payload.get("agentfs_seconds") - if not isinstance(agentfs, dict): + vfs = phase_payload.get("vfs_seconds") + if not isinstance(vfs, dict): raise BenchmarkFormatError( - f"{source}: phases.{phase}.agentfs_seconds must be an object" + f"{source}: phases.{phase}.vfs_seconds must be an object" ) - count = require_int(agentfs.get("count"), f"{source}: phases.{phase}.agentfs_seconds.count") + count = require_int(vfs.get("count"), f"{source}: phases.{phase}.vfs_seconds.count") if count != EXPECTED_ITERATIONS: raise BenchmarkFormatError( - f"{source}: phases.{phase}.agentfs_seconds.count expected " + f"{source}: phases.{phase}.vfs_seconds.count expected " f"{EXPECTED_ITERATIONS}, got {count}" ) median = require_number( - agentfs.get("median"), - f"{source}: phases.{phase}.agentfs_seconds.median", + vfs.get("median"), + f"{source}: phases.{phase}.vfs_seconds.median", ) - parsed[phase] = PhaseMedian(phase=phase, agentfs_seconds=median, count=count) + parsed[phase] = PhaseMedian(phase=phase, vfs_seconds=median, count=count) return parsed @@ -139,8 +139,8 @@ def compare_phase_medians( comparisons: list[PhaseComparison] = [] for phase in sorted(baseline): - base = baseline[phase].agentfs_seconds - cand = candidate[phase].agentfs_seconds + base = baseline[phase].vfs_seconds + cand = candidate[phase].vfs_seconds if base <= 0: raise BenchmarkFormatError( f"baseline phase {phase} has non-positive median {base}" diff --git a/scripts/validation/consistency-canon.sh b/scripts/validation/consistency-canon.sh index 5d908edd..df1237b0 100755 --- a/scripts/validation/consistency-canon.sh +++ b/scripts/validation/consistency-canon.sh @@ -38,11 +38,11 @@ check_python "dag" - <<'PY' import json, subprocess, sys expected = { - "agentfs-cli": {"agentfs-core", "agentfs-mount"}, - "agentfs-core": set(), - "agentfs-fuse": {"agentfs-core"}, - "agentfs-nfs": {"agentfs-core"}, - "agentfs-mount": {"agentfs-core", "agentfs-fuse", "agentfs-nfs"}, + "vfs-cli": {"vfs-core", "vfs-mount"}, + "vfs-core": set(), + "vfs-fuse": {"vfs-core"}, + "vfs-nfs": {"vfs-core"}, + "vfs-mount": {"vfs-core", "vfs-fuse", "vfs-nfs"}, } meta = json.loads( @@ -58,7 +58,7 @@ if set(packages) != set(expected): for name, wanted in expected.items(): actual = { dep["name"] for dep in packages[name]["dependencies"] - if dep["name"].startswith("agentfs-") + if dep["name"].startswith("vfs-") } if actual != wanted: print(f"{name} first-party deps {sorted(actual)} != expected {sorted(wanted)}") @@ -73,14 +73,14 @@ from pathlib import Path problems = [] -fuse = Path("crates/agentfs-fuse/src/lib.rs").read_text() +fuse = Path("crates/vfs-fuse/src/lib.rs").read_text() fuse_pubs = re.findall(r"^pub .*$", fuse, re.M) if fuse_pubs != ["pub use adapter::{mount, FuseMountOptions, SessionHandle};"]: problems.append(f"fuse lib.rs pub surface drifted: {fuse_pubs}") if re.search(r"^pub mod", fuse, re.M): problems.append("fuse lib.rs exposes a pub mod") -nfs = Path("crates/agentfs-nfs/src/lib.rs").read_text() +nfs = Path("crates/vfs-nfs/src/lib.rs").read_text() nfs_items = re.findall(r"^pub (?:async )?(?:struct|fn|enum|trait|mod|use) (\w+)", nfs, re.M) if sorted(nfs_items) != ["NfsServeOptions", "ServerHandle", "serve"]: problems.append(f"nfs lib.rs pub surface drifted: {sorted(nfs_items)}") @@ -170,8 +170,8 @@ exec(os.environ["CANON_SCAN_HELPER"]) def allowed(path): p = str(path) return ( - p.startswith("crates/agentfs-cli/src/cmd/") - or p == "crates/agentfs-cli/src/main.rs" + p.startswith("crates/vfs-cli/src/cmd/") + or p == "crates/vfs-cli/src/main.rs" or path.name == "build.rs" ) @@ -200,9 +200,9 @@ exec(os.environ["CANON_SCAN_HELPER"]) # Env reads live in each crate's config module (canon section 7 item 3). ALLOWED_PREFIXES = ( - "crates/agentfs-core/src/config/", - "crates/agentfs-fuse/src/adapter/config.rs", - "crates/agentfs-cli/src/config.rs", + "crates/vfs-core/src/config/", + "crates/vfs-fuse/src/adapter/config.rs", + "crates/vfs-cli/src/config.rs", ) offenders = [] @@ -226,10 +226,10 @@ check_python "envfilter-coverage" - <<'PY' import sys from pathlib import Path -logging = Path("crates/agentfs-cli/src/logging.rs").read_text() +logging = Path("crates/vfs-cli/src/logging.rs").read_text() missing = [ target for target in - ("agentfs=", "agentfs_cli=", "agentfs_core=", "agentfs_fuse=", "agentfs_nfs=", "agentfs_mount=") + ("vfs=", "vfs_cli=", "vfs_core=", "vfs_fuse=", "vfs_nfs=", "vfs_mount=") if target not in logging ] if missing: @@ -248,7 +248,7 @@ if 'await_holding_lock = "deny"' not in root: print("workspace lints table does not deny clippy::await_holding_lock") sys.exit(1) missing = [ - crate for crate in ("agentfs-cli", "agentfs-core", "agentfs-fuse", "agentfs-nfs", "agentfs-mount") + crate for crate in ("vfs-cli", "vfs-core", "vfs-fuse", "vfs-nfs", "vfs-mount") if "workspace = true" not in Path(f"crates/{crate}/Cargo.toml").read_text().split("[lints]")[-1] or "[lints]" not in Path(f"crates/{crate}/Cargo.toml").read_text() ] @@ -264,10 +264,10 @@ import re, sys from pathlib import Path modules = [ - "crates/agentfs-core/src/fs/agentfs/batcher.rs", - "crates/agentfs-fuse/src/adapter/cache.rs", - "crates/agentfs-core/src/fs/overlay/mod.rs", - "crates/agentfs-core/src/semantics/handles.rs", + "crates/vfs-core/src/fs/vfs/batcher.rs", + "crates/vfs-fuse/src/adapter/cache.rs", + "crates/vfs-core/src/fs/overlay/mod.rs", + "crates/vfs-core/src/semantics/handles.rs", ] missing = [ m for m in modules diff --git a/scripts/validation/ddl-census.py b/scripts/validation/ddl-census.py index e78acf06..70b0dbdf 100755 --- a/scripts/validation/ddl-census.py +++ b/scripts/validation/ddl-census.py @@ -24,7 +24,7 @@ } SCHEMA_AUTHORITY_DIRS = ( Path("sdk/rust/src/schema"), - Path("crates/agentfs-core/src/schema"), + Path("crates/vfs-core/src/schema"), ) EXCLUDED_DIR_NAMES = {".git", "target"} @@ -365,14 +365,14 @@ def emit_result(result: CensusResult) -> int: def run_self_test() -> int: with tempfile.TemporaryDirectory(prefix="ddl-census-fixture-") as tmp: root = Path(tmp) - schema_dir = root / "crates/agentfs-core/src/schema" + schema_dir = root / "crates/vfs-core/src/schema" schema_dir.mkdir(parents=True) (schema_dir / "mod.rs").write_text( 'pub const AUTHORITY_DDL: &str = "CREATE TABLE authority (id INTEGER)";\n', encoding="utf-8", ) - src_dir = root / "crates/agentfs-core/src" + src_dir = root / "crates/vfs-core/src" (src_dir / "lib.rs").write_text( r''' #[cfg(test)] diff --git a/scripts/validation/external-base-mutation-coherence.py b/scripts/validation/external-base-mutation-coherence.py index 30679cdd..ca9e6347 100644 --- a/scripts/validation/external-base-mutation-coherence.py +++ b/scripts/validation/external-base-mutation-coherence.py @@ -32,21 +32,21 @@ def tail_text(text: str) -> str: return text if len(text) <= OUTPUT_TAIL_CHARS else text[-OUTPUT_TAIL_CHARS:] -def resolve_agentfs_bin(agentfs_bin: str | None, repo_root: Path) -> str: - if agentfs_bin: - candidate = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: str | None, repo_root: Path) -> str: + if vfs_bin: + candidate = Path(vfs_bin).expanduser() if candidate.is_file() and os.access(candidate, os.X_OK): return str(candidate.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"agentfs binary not found or not executable: {agentfs_bin}") + raise RuntimeError(f"vfs binary not found or not executable: {vfs_bin}") - candidate = repo_root / "target" / "release" / "agentfs" + candidate = repo_root / "target" / "release" / "vfs" if candidate.is_file() and os.access(candidate, os.X_OK): return str(candidate) - raise RuntimeError("no release agentfs binary found, pass --agentfs-bin or build release") + raise RuntimeError("no release vfs binary found, pass --vfs-bin or build release") def sha256(data: bytes) -> str: @@ -55,7 +55,7 @@ def sha256(data: bytes) -> str: def parse_profile_summary(output: str) -> dict[str, Any]: for line in reversed(output.splitlines()): - if '"agentfs_profile_summary"' not in line: + if '"vfs_profile_summary"' not in line: continue start = line.find("{") if start < 0: @@ -327,12 +327,12 @@ def run_leg( label: str, extra_env: dict[str, str], expect_noopen: bool, - agentfs_bin: str, + vfs_bin: str, timeout: float, cleanup_timeout: float, ) -> dict[str, Any]: started = time.perf_counter() - with tempfile.TemporaryDirectory(prefix=f"agentfs-base-drift-{label}-") as tmp: + with tempfile.TemporaryDirectory(prefix=f"vfs-base-drift-{label}-") as tmp: root = Path(tmp) work = root / "work" base = root / "base" @@ -345,9 +345,9 @@ def run_leg( env = os.environ.copy() env.update(extra_env) - env["AGENTFS_PROFILE"] = "1" + env["VFS_PROFILE"] = "1" - init = run_checked([agentfs_bin, "init", "drift", "--base", str(base)], work, env, timeout) + init = run_checked([vfs_bin, "init", "drift", "--base", str(base)], work, env, timeout) if init.returncode != 0: return { "label": label, @@ -365,9 +365,9 @@ def run_leg( try: proc = subprocess.Popen( [ - agentfs_bin, + vfs_bin, "mount", - str(work / ".agentfs" / "drift.db"), + str(work / ".vfs" / "drift.db"), str(mountpoint), "--foreground", ], @@ -504,24 +504,24 @@ def main() -> int: signal.signal(signal.SIGINT, raise_keyboard_interrupt) signal.signal(signal.SIGTERM, raise_keyboard_interrupt) parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--agentfs-bin", default=os.environ.get("AGENTFS_BIN")) + parser.add_argument("--vfs-bin", default=os.environ.get("VFS_BIN")) parser.add_argument("--timeout", type=float, default=60.0) parser.add_argument("--cleanup-timeout", type=float, default=DEFAULT_CLEANUP_TIMEOUT) parser.add_argument("--output", default=None) args = parser.parse_args() repo_root = Path(__file__).resolve().parents[2] - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) legs = [ ("default_noopen", {}, True), - ("noopen_off", {"AGENTFS_FUSE_NOOPEN": "0"}, False), + ("noopen_off", {"VFS_FUSE_NOOPEN": "0"}, False), ] runs = [] interrupted = False for label, env, noopen in legs: leg_started = time.perf_counter() try: - run = run_leg(label, env, noopen, agentfs_bin, args.timeout, args.cleanup_timeout) + run = run_leg(label, env, noopen, vfs_bin, args.timeout, args.cleanup_timeout) except KeyboardInterrupt as exc: run = { "label": label, @@ -537,14 +537,14 @@ def main() -> int: break report = { "schema_version": 1, - "agentfs_bin": agentfs_bin, + "vfs_bin": vfs_bin, "passed": not interrupted and all(run.get("passed") for run in runs), "interrupted": interrupted, "runs": runs, } output = args.output or os.path.join( tempfile.gettempdir(), - f"agentfs-external-base-mutation-{time.strftime('%Y%m%d-%H%M%S')}.json", + f"vfs-external-base-mutation-{time.strftime('%Y%m%d-%H%M%S')}.json", ) Path(output).write_text(json.dumps(report, indent=2)) diff --git a/scripts/validation/flush-coherence.py b/scripts/validation/flush-coherence.py index e074e387..7d9e9e42 100644 --- a/scripts/validation/flush-coherence.py +++ b/scripts/validation/flush-coherence.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Attr coherence around close() with and without the FLUSH round trip. -With AGENTFS_FUSE_NOFLUSH=1 the adapter answers the first FLUSH with ENOSYS, +With VFS_FUSE_NOFLUSH=1 the adapter answers the first FLUSH with ENOSYS, so the kernel stops sending FLUSH and a closed handle's buffered write tail reaches the SDK only at the async RELEASE (or via the adapter's pending-tail drains on attr-bearing paths). This script hammers exactly that window: @@ -37,7 +37,7 @@ from typing import Any, Optional sys.path.insert(0, str(Path(__file__).resolve().parent)) -from lib.common import resolve_agentfs_bin, tail_text # noqa: E402 +from lib.common import resolve_vfs_bin, tail_text # noqa: E402 WORKLOAD = r''' import ctypes @@ -128,7 +128,7 @@ def parse_workload_json(stdout: str) -> Optional[dict[str, Any]]: def parse_fuse_counters(output: str) -> Optional[dict[str, Any]]: for line in reversed(output.splitlines()): - if '"agentfs_profile_summary"' not in line: + if '"vfs_profile_summary"' not in line: continue start = line.find("{") if start < 0: @@ -144,7 +144,7 @@ def parse_fuse_counters(output: str) -> Optional[dict[str, Any]]: def run_config( - agentfs_bin: str, + vfs_bin: str, temp_root: Path, label: str, iterations: int, @@ -155,15 +155,15 @@ def run_config( db = temp_root / f"{label}.db" db.touch() env = os.environ.copy() - env["AGENTFS_PROFILE"] = "1" - env["AGENTFS_FUSE_NOFLUSH"] = "1" if noflush else "0" + env["VFS_PROFILE"] = "1" + env["VFS_FUSE_NOFLUSH"] = "1" if noflush else "0" if entry_ttl_ms is None: - env.pop("AGENTFS_FUSE_ENTRY_TTL_MS", None) + env.pop("VFS_FUSE_ENTRY_TTL_MS", None) else: - env["AGENTFS_FUSE_ENTRY_TTL_MS"] = str(entry_ttl_ms) + env["VFS_FUSE_ENTRY_TTL_MS"] = str(entry_ttl_ms) argv = [ - agentfs_bin, + vfs_bin, "exec", str(db), sys.executable, @@ -220,14 +220,14 @@ def run_config( def main() -> int: parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--agentfs-bin", default=os.environ.get("AGENTFS_BIN")) + parser.add_argument("--vfs-bin", default=os.environ.get("VFS_BIN")) parser.add_argument("--iterations", type=int, default=120) parser.add_argument("--timeout", type=float, default=600.0) parser.add_argument("--output", default=None) args = parser.parse_args() repo_root = Path(__file__).resolve().parents[2] - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) configs = [ ("flush_default_ttl", False, None), @@ -237,12 +237,12 @@ def main() -> int: ] runs = [] - with tempfile.TemporaryDirectory(prefix="agentfs-flush-coherence-") as tmp: + with tempfile.TemporaryDirectory(prefix="vfs-flush-coherence-") as tmp: temp_root = Path(tmp) for label, noflush, ttl in configs: runs.append( run_config( - agentfs_bin, + vfs_bin, temp_root, label, args.iterations, @@ -260,7 +260,7 @@ def main() -> int: report = { "schema_version": 1, - "agentfs_bin": agentfs_bin, + "vfs_bin": vfs_bin, "iterations": args.iterations, "noflush_pending_tail_drains_total": tail_drains, "window_exercised": window_exercised, @@ -269,7 +269,7 @@ def main() -> int: } output = args.output or os.path.join( tempfile.gettempdir(), - f"agentfs-flush-coherence-{time.strftime('%Y%m%d-%H%M%S')}.json", + f"vfs-flush-coherence-{time.strftime('%Y%m%d-%H%M%S')}.json", ) Path(output).write_text(json.dumps(report, indent=2)) diff --git a/scripts/validation/fuse-serialization-stress.py b/scripts/validation/fuse-serialization-stress.py index 30986281..d916cf29 100755 --- a/scripts/validation/fuse-serialization-stress.py +++ b/scripts/validation/fuse-serialization-stress.py @@ -48,7 +48,7 @@ def positive_int(value): files = sorted( path for path in root.rglob("*") - if path.is_file() and ".agentfs" not in path.relative_to(root).parts + if path.is_file() and ".vfs" not in path.relative_to(root).parts ) if not files: raise SystemExit("fixture has no files") @@ -130,7 +130,7 @@ def env_flag(name: str) -> bool: def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( description=( - "Run a tiny native-vs-AgentFS threaded read workload and capture " + "Run a tiny native-vs-Vfs threaded read workload and capture " "FUSE read/write lane and adapter lock profile counters." ) ) @@ -155,9 +155,9 @@ def parse_args(argv: list[str]) -> argparse.Namespace: help="bytes read per open/read/close operation", ) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -169,7 +169,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: "--profile", action="store_true", default=True, - help="enable AGENTFS_PROFILE=1 for AgentFS invocation (default: enabled)", + help="enable VFS_PROFILE=1 for Vfs invocation (default: enabled)", ) parser.add_argument("--session", default=f"fuse-serialization-{uuid.uuid4().hex}") parser.add_argument("--output", help="write JSON result to this file") @@ -192,13 +192,13 @@ def extract_profile_summaries(stderr: Any) -> list[dict[str, Any]]: text = stderr.decode("utf-8", errors="replace") if isinstance(stderr, bytes) else str(stderr or "") summaries: list[dict[str, Any]] = [] for line in text.splitlines(): - if "agentfs_profile_summary" not in line: + if "vfs_profile_summary" not in line: continue try: value = json.loads(line) except json.JSONDecodeError: continue - if isinstance(value, dict) and value.get("event") == "agentfs_profile_summary": + if isinstance(value, dict) and value.get("event") == "vfs_profile_summary": summaries.append(value) return summaries @@ -275,20 +275,20 @@ def parse_json_stdout(run: dict[str, Any]) -> Optional[dict[str, Any]]: return None -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - path = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + path = Path(vfs_bin).expanduser() if path.is_file() and os.access(path, os.X_OK): return str(path.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"agentfs executable not found: {agentfs_bin}") + raise RuntimeError(f"vfs executable not found: {vfs_bin}") for path in ( - repo_root / "cli" / "target" / "debug" / "agentfs", - repo_root / "cli" / "target" / "release" / "agentfs", + repo_root / "cli" / "target" / "debug" / "vfs", + repo_root / "cli" / "target" / "release" / "vfs", ): if path.is_file() and os.access(path, os.X_OK): return str(path) @@ -307,10 +307,10 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: stderr=subprocess.PIPE, ) if build.returncode != 0: - raise RuntimeError(f"failed to build agentfs\n{tail_text(build.stderr)}") - built = repo_root / "cli" / "target" / "debug" / "agentfs" + raise RuntimeError(f"failed to build vfs\n{tail_text(build.stderr)}") + built = repo_root / "cli" / "target" / "debug" / "vfs" if not built.is_file(): - raise RuntimeError(f"built agentfs binary missing: {built}") + raise RuntimeError(f"built vfs binary missing: {built}") return str(built) @@ -319,7 +319,7 @@ def prepare_environment(temp_root: Path, profile: bool) -> dict[str, str]: env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") if profile: - env["AGENTFS_PROFILE"] = "1" + env["VFS_PROFILE"] = "1" home = temp_root / "home" for path in (home, home / ".config", home / ".cache", home / ".local" / "share"): path.mkdir(parents=True, exist_ok=True) @@ -333,7 +333,7 @@ def prepare_environment(temp_root: Path, profile: bool) -> dict[str, str]: def create_fixture(root: Path, files: int, file_size: int) -> None: root.mkdir(parents=True, exist_ok=True) for index in range(files): - seed = hashlib.sha256(f"agentfs-phase65-serialization-{index}".encode()).digest() + seed = hashlib.sha256(f"vfs-phase65-serialization-{index}".encode()).digest() data = (seed * ((file_size // len(seed)) + 1))[:file_size] (root / f"file_{index:04d}.dat").write_bytes(data) @@ -353,51 +353,51 @@ def workload_argv(args: argparse.Namespace, workload_script: Path) -> list[str]: def default_output_path() -> Path: stamp = time.strftime("%Y%m%d-%H%M%S") - return Path(tempfile.gettempdir()) / f"agentfs-fuse-serialization-stress-{stamp}.json" + return Path(tempfile.gettempdir()) / f"vfs-fuse-serialization-stress-{stamp}.json" def main(argv: list[str]) -> int: args = parse_args(argv) repo_root = Path(__file__).resolve().parents[2] temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-fuse-serialization-stress-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-fuse-serialization-stress-")) if not args.keep_temp: - temp_manager = tempfile.TemporaryDirectory(prefix="agentfs-fuse-serialization-stress-home-") + temp_manager = tempfile.TemporaryDirectory(prefix="vfs-fuse-serialization-stress-home-") output_path = Path(args.output).expanduser() if args.output else default_output_path() exit_code = 0 try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) env_root = Path(temp_manager.name) if temp_manager is not None else temp_root env = prepare_environment(env_root, args.profile) native_root = temp_root / "native" - agentfs_root = temp_root / "agentfs" + vfs_root = temp_root / "vfs" workload_script = temp_root / "concurrent_read_workload.py" workload_script.write_text(CONCURRENT_READ_WORKLOAD, encoding="utf-8") create_fixture(native_root, args.files, args.file_size_bytes) - shutil.copytree(native_root, agentfs_root) + shutil.copytree(native_root, vfs_root) workload = workload_argv(args, workload_script) - agentfs_command = " ".join(shlex.quote(part) for part in workload) - agentfs_argv = [ - agentfs_bin, + vfs_command = " ".join(shlex.quote(part) for part in workload) + vfs_argv = [ + vfs_bin, "init", "--force", "--base", - str(agentfs_root), + str(vfs_root), "--backend", "fuse", "--command", - agentfs_command, + vfs_command, args.session, ] native_run = run_subprocess(workload, native_root, env, args.timeout) - agentfs_run = run_subprocess(agentfs_argv, agentfs_root, env, args.timeout) + vfs_run = run_subprocess(vfs_argv, vfs_root, env, args.timeout) native_workload = parse_json_stdout(native_run) - agentfs_workload = parse_json_stdout(agentfs_run) - profile_counters = max_profile_counters(agentfs_run["profile_summaries"]) + vfs_workload = parse_json_stdout(vfs_run) + profile_counters = max_profile_counters(vfs_run["profile_summaries"]) profile_counters_present = ( - len(agentfs_run["profile_summaries"]) > 0 + len(vfs_run["profile_summaries"]) > 0 and "fuse_adapter_lock_wait_count" in profile_counters and "fuse_adapter_lock_wait_nanos" in profile_counters and "fuse_read_lane_wait_count" in profile_counters @@ -414,11 +414,11 @@ def main(argv: list[str]) -> int: exclusive_fallback_count = profile_counters.get("fuse_exclusive_fallback_count", 0) equivalent = ( native_workload is not None - and agentfs_workload is not None - and native_workload.get("digest") == agentfs_workload.get("digest") - and native_workload.get("counts") == agentfs_workload.get("counts") + and vfs_workload is not None + and native_workload.get("digest") == vfs_workload.get("digest") + and native_workload.get("counts") == vfs_workload.get("counts") ) - if native_run["returncode"] != 0 or agentfs_run["returncode"] != 0 or not equivalent: + if native_run["returncode"] != 0 or vfs_run["returncode"] != 0 or not equivalent: exit_code = 1 if args.profile and not profile_counters_present: exit_code = 1 @@ -429,7 +429,7 @@ def main(argv: list[str]) -> int: "command": { "argv": [str(Path(__file__).resolve())] + argv, "workload_argv": workload, - "agentfs_argv": agentfs_argv, + "vfs_argv": vfs_argv, }, "parameters": { "files": args.files, @@ -439,17 +439,17 @@ def main(argv: list[str]) -> int: "read_bytes": args.read_bytes, }, "native": {"run": native_run, "workload": native_workload}, - "agentfs": { - "run": agentfs_run, - "workload": agentfs_workload, + "vfs": { + "run": vfs_run, + "workload": vfs_workload, "profile_counters": profile_counters, }, "summary": { "equivalent": equivalent, "native_seconds": native_run["duration_seconds"], - "agentfs_seconds": agentfs_run["duration_seconds"], + "vfs_seconds": vfs_run["duration_seconds"], "ratio": ( - agentfs_run["duration_seconds"] / native_run["duration_seconds"] + vfs_run["duration_seconds"] / native_run["duration_seconds"] if native_run["duration_seconds"] > 0 else None ), diff --git a/scripts/validation/git-workload-benchmark-multi.py b/scripts/validation/git-workload-benchmark-multi.py index 2e2ca034..c7511f9e 100755 --- a/scripts/validation/git-workload-benchmark-multi.py +++ b/scripts/validation/git-workload-benchmark-multi.py @@ -4,7 +4,7 @@ Single-shot benchmark runs are noisy (page cache, scheduler, disk activity). This wrapper runs the underlying benchmark N times and reports median + percentile statistics per phase, so we can make confident before/after -comparisons when tuning AgentFS. +comparisons when tuning Vfs. The wrapper is intentionally non-invasive: it shells out to the existing benchmark with --output, parses each JSON, and aggregates. Pass-through of @@ -60,9 +60,9 @@ def summarize_floats(values: list[float]) -> dict[str, float | int]: def aggregate(runs: list[dict[str, Any]]) -> dict[str, Any]: overall_ratios: list[float] = [] native_totals: list[float] = [] - agentfs_totals: list[float] = [] + vfs_totals: list[float] = [] phase_natives: dict[str, list[float]] = {} - phase_agentfs: dict[str, list[float]] = {} + phase_vfs: dict[str, list[float]] = {} phase_ratios: dict[str, list[float]] = {} for run in runs: @@ -71,30 +71,30 @@ def aggregate(runs: list[dict[str, Any]]) -> dict[str, Any]: if isinstance(ratio, (int, float)): overall_ratios.append(float(ratio)) n = summary.get("native_seconds") - a = summary.get("agentfs_seconds") + a = summary.get("vfs_seconds") if isinstance(n, (int, float)): native_totals.append(float(n)) if isinstance(a, (int, float)): - agentfs_totals.append(float(a)) + vfs_totals.append(float(a)) for phase, payload in (summary.get("phase_ratios") or {}).items(): if not isinstance(payload, dict): continue nv = payload.get("native_seconds") - av = payload.get("agentfs_seconds") + av = payload.get("vfs_seconds") rv = payload.get("ratio") if isinstance(nv, (int, float)): phase_natives.setdefault(phase, []).append(float(nv)) if isinstance(av, (int, float)): - phase_agentfs.setdefault(phase, []).append(float(av)) + phase_vfs.setdefault(phase, []).append(float(av)) if isinstance(rv, (int, float)): phase_ratios.setdefault(phase, []).append(float(rv)) phase_stats: dict[str, Any] = {} - for phase in sorted(set(phase_natives) | set(phase_agentfs) | set(phase_ratios)): + for phase in sorted(set(phase_natives) | set(phase_vfs) | set(phase_ratios)): phase_stats[phase] = { "native_seconds": summarize_floats(phase_natives.get(phase, [])), - "agentfs_seconds": summarize_floats(phase_agentfs.get(phase, [])), + "vfs_seconds": summarize_floats(phase_vfs.get(phase, [])), "ratio": summarize_floats(phase_ratios.get(phase, [])), } @@ -102,19 +102,19 @@ def aggregate(runs: list[dict[str, Any]]) -> dict[str, Any]: "iterations": len(runs), "overall": { "native_seconds": summarize_floats(native_totals), - "agentfs_seconds": summarize_floats(agentfs_totals), + "vfs_seconds": summarize_floats(vfs_totals), "ratio": summarize_floats(overall_ratios), }, "phases": phase_stats, } -def run_one(forward_argv: list[str], output_path: Path, agentfs_bin: str | None) -> dict[str, Any]: +def run_one(forward_argv: list[str], output_path: Path, vfs_bin: str | None) -> dict[str, Any]: benchmark = Path(__file__).resolve().with_name("git-workload-benchmark.py") argv = [sys.executable, str(benchmark), "--output", str(output_path)] + forward_argv env = os.environ.copy() - if agentfs_bin is not None: - env["AGENTFS_BIN"] = agentfs_bin + if vfs_bin is not None: + env["VFS_BIN"] = vfs_bin started = time.perf_counter() proc = subprocess.run(argv, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) duration = time.perf_counter() - started @@ -141,13 +141,13 @@ def render_human(label: str, agg: dict[str, Any]) -> str: out: list[str] = [] overall = agg["overall"] n = overall["native_seconds"] - a = overall["agentfs_seconds"] + a = overall["vfs_seconds"] r = overall["ratio"] head = ( f"=== {label} (iterations={agg['iterations']}) ===\n" f" native median={format_seconds(n.get('median', float('nan')))}" f" [p25={format_seconds(n.get('p25', float('nan')))}, p75={format_seconds(n.get('p75', float('nan')))}]\n" - f" agentfs median={format_seconds(a.get('median', float('nan')))}" + f" vfs median={format_seconds(a.get('median', float('nan')))}" f" [p25={format_seconds(a.get('p25', float('nan')))}, p75={format_seconds(a.get('p75', float('nan')))}]\n" f" ratio median={r.get('median', float('nan')):.2f}x" f" [p25={r.get('p25', float('nan')):.2f}x, p75={r.get('p75', float('nan')):.2f}x]" @@ -158,12 +158,12 @@ def render_human(label: str, agg: dict[str, Any]) -> str: for phase, stats in agg["phases"].items(): r = stats["ratio"] nv = stats["native_seconds"] - av = stats["agentfs_seconds"] + av = stats["vfs_seconds"] if r.get("count", 0) == 0: continue out.append( f" {phase:<14s} native={format_seconds(nv['median'])}" - f" agentfs={format_seconds(av['median'])}" + f" vfs={format_seconds(av['median'])}" f" ratio={r['median']:.2f}x" f" (p25={r['p25']:.2f}x p75={r['p75']:.2f}x)" ) @@ -193,9 +193,9 @@ def parse_args(argv: list[str]) -> tuple[argparse.Namespace, list[str]]: help="number of warmup iterations whose results are discarded (default: 1)", ) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="override AGENTFS_BIN for the underlying benchmark", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="override VFS_BIN for the underlying benchmark", ) parser.add_argument( "--output", @@ -236,13 +236,13 @@ def main(argv: list[str]) -> int: for i in range(args.warmup): out_path = (persist_dir / f"warmup-{i:02d}.json") if persist_dir else (tmp_root / f"warmup-{i:02d}.json") print(f"[warmup {i+1}/{args.warmup}] running...", file=sys.stderr, flush=True) - warmup_runs.append(run_one(forward, out_path, args.agentfs_bin)) + warmup_runs.append(run_one(forward, out_path, args.vfs_bin)) runs: list[dict[str, Any]] = [] for i in range(args.iterations): out_path = (persist_dir / f"iter-{i:02d}.json") if persist_dir else (tmp_root / f"iter-{i:02d}.json") print(f"[iter {i+1}/{args.iterations}] running...", file=sys.stderr, flush=True) - payload = run_one(forward, out_path, args.agentfs_bin) + payload = run_one(forward, out_path, args.vfs_bin) runs.append(payload) result = payload.get("result") or {} summary = result.get("summary") or {} @@ -261,7 +261,7 @@ def main(argv: list[str]) -> int: aggregation["label"] = args.label aggregation["forwarded_argv"] = forward aggregation["warmup_iterations"] = args.warmup - aggregation["agentfs_bin"] = args.agentfs_bin + aggregation["vfs_bin"] = args.vfs_bin aggregation["iteration_returncodes"] = [r["returncode"] for r in runs] aggregation["iteration_wall_seconds"] = [r["wall_seconds"] for r in runs] aggregation["git_ai_census"] = { diff --git a/scripts/validation/git-workload-benchmark.py b/scripts/validation/git-workload-benchmark.py index 5e4b46c2..ff7f7093 100755 --- a/scripts/validation/git-workload-benchmark.py +++ b/scripts/validation/git-workload-benchmark.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Phase 7 native-vs-AgentFS Git workload benchmark and principle gate.""" +"""Phase 7 native-vs-Vfs Git workload benchmark and principle gate.""" from __future__ import annotations @@ -51,19 +51,19 @@ def profile_checkpoint(label): - """Request an AgentFS profiling checkpoint at a phase boundary. + """Request a Vfs profiling checkpoint at a phase boundary. - Only meaningful when running inside an AgentFS sandbox with profiling - enabled. We signal the parent `agentfs run` process (SIGUSR1), which emits a + Only meaningful when running inside a Vfs sandbox with profiling + enabled. We signal the parent `vfs run` process (SIGUSR1), which emits a cumulative, sequence-tagged profile summary to its stderr; the analyzer subtracts consecutive checkpoints to obtain per-phase counter deltas. A small - sleep lets the parent flush before the next phase begins. Guarded on AGENTFS + sleep lets the parent flush before the next phase begins. Guarded on VFS so native runs never signal the benchmark harness. """ PROFILE_CHECKPOINTS.append(label) - if os.environ.get("AGENTFS") != "1": + if os.environ.get("VFS") != "1": return - if os.environ.get("AGENTFS_PROFILE", "") not in {"1", "true", "TRUE", "yes", "on"}: + if os.environ.get("VFS_PROFILE", "") not in {"1", "true", "TRUE", "yes", "on"}: return try: os.kill(os.getppid(), signal.SIGUSR1) @@ -95,7 +95,7 @@ def git_env(): def run_git(argv, cwd): # Honor the harness's pinned-git override: PATH shims are invisible inside - # the agentfs sandbox, so only an absolute system-path GIT avoids the + # the vfs sandbox, so only an absolute system-path GIT avoids the # daemonizing hook-manager shim (scripts/validation/lib/common.py). git = os.environ.get("GIT", "git") started = time.perf_counter() @@ -191,7 +191,7 @@ def edit_files(workdir, paths, limit): for index, rel in enumerate(selected): path = workdir / rel before_size = path.stat().st_size - payload = f"\nAgentFS Git benchmark edit {index:02d} for {rel}\n".encode("utf-8") + payload = f"\nVfs Git benchmark edit {index:02d} for {rel}\n".encode("utf-8") with path.open("ab", buffering=0) as handle: handle.write(payload) handle.flush() @@ -239,7 +239,7 @@ def main(argv): parser.add_argument("--read-files", type=int, required=True) parser.add_argument("--read-bytes", type=int, required=True) parser.add_argument("--edit-files", type=int, required=True) - parser.add_argument("--search-token", default="AGENTFS_TOKEN") + parser.add_argument("--search-token", default="VFS_TOKEN") parser.add_argument("--skip-fsck", action="store_true") args = parser.parse_args(argv) @@ -258,7 +258,7 @@ def main(argv): profile_checkpoint("clone") started = time.perf_counter() - checkout = run_git(["checkout", "-B", "agentfs-benchmark"], workdir) + checkout = run_git(["checkout", "-B", "vfs-benchmark"], workdir) require_ok(checkout, "checkout") head = run_git(["rev-parse", "HEAD"], workdir) require_ok(head, "rev-parse") @@ -362,7 +362,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( description=( "Compare a deterministic Git-like mixed workflow on native storage " - "against the same workflow through an AgentFS overlay." + "against the same workflow through a Vfs overlay." ), formatter_class=argparse.RawDescriptionHelpFormatter, epilog="""Examples: @@ -373,10 +373,10 @@ def parse_args(argv: list[str]) -> argparse.Namespace: scripts/validation/git-workload-benchmark.py --source /path/to/repo --read-files 128 Environment: - AGENTFS_BIN path/name of agentfs executable - AGENTFS_PROFILE set to 0 only when --no-profile is supplied + VFS_BIN path/name of vfs executable + VFS_PROFILE set to 0 only when --no-profile is supplied GIT_WORKLOAD_BENCHMARK_KEEP_TEMP=1 - keep temporary source/native/AgentFS trees + keep temporary source/native/Vfs trees """, ) source_group = parser.add_mutually_exclusive_group() @@ -400,11 +400,11 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser.add_argument("--read-files", type=positive_int, default=64) parser.add_argument("--read-bytes", type=positive_int, default=4096) parser.add_argument("--edit-files", type=positive_int, default=8) - parser.add_argument("--search-token", default="AGENTFS_TOKEN") + parser.add_argument("--search-token", default="VFS_TOKEN") parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -412,19 +412,19 @@ def parse_args(argv: list[str]) -> argparse.Namespace: default=positive_float(os.environ.get("GIT_WORKLOAD_BENCHMARK_TIMEOUT", "180")), help="per-command timeout in seconds (default: 180)", ) - parser.add_argument("--session", default=None, help="AgentFS session id (default: generated)") + parser.add_argument("--session", default=None, help="Vfs session id (default: generated)") profile_group = parser.add_mutually_exclusive_group() profile_group.add_argument( "--profile", dest="profile", action="store_true", - help="enable AGENTFS_PROFILE=1 for AgentFS invocation (default)", + help="enable VFS_PROFILE=1 for Vfs invocation (default)", ) profile_group.add_argument( "--no-profile", dest="profile", action="store_false", - help="disable AgentFS profile summaries", + help="disable Vfs profile summaries", ) parser.set_defaults(profile=True) parser.add_argument("--skip-fsck", action="store_true", help="skip git fsck --strict phase") @@ -468,13 +468,13 @@ def extract_profile_summaries(stderr: Any) -> list[dict[str, Any]]: summaries: list[dict[str, Any]] = [] for line in text.splitlines(): line = line.strip() - if not line or "agentfs_profile_summary" not in line: + if not line or "vfs_profile_summary" not in line: continue try: value = json.loads(line) except json.JSONDecodeError: continue - if isinstance(value, dict) and value.get("event") == "agentfs_profile_summary": + if isinstance(value, dict) and value.get("event") == "vfs_profile_summary": summaries.append(value) return summaries @@ -634,16 +634,16 @@ def parse_json_stdout(run: dict[str, Any]) -> Optional[dict[str, Any]]: return None -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate_path = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate_path = Path(vfs_bin).expanduser() if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") # Prefer release over debug: release binaries are what benchmarks should be # measuring (debug is unoptimized and can be 10x slower), AND release tends @@ -653,8 +653,8 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: # pre-ABI-collapse workspace kept returning ENOSYS while the just-built # release binary worked fine. for candidate_path in ( - repo_root / "cli" / "target" / "release" / "agentfs", - repo_root / "cli" / "target" / "debug" / "agentfs", + repo_root / "cli" / "target" / "release" / "vfs", + repo_root / "cli" / "target" / "debug" / "vfs", ): if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path) @@ -668,12 +668,12 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN to an explicit binary\n" + "failed to build repo-local vfs binary; set VFS_BIN to an explicit binary\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = repo_root / "cli" / "target" / "debug" / "agentfs" + built = repo_root / "cli" / "target" / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) raise RuntimeError(f"repo-local build completed but binary was not found: {built}") @@ -698,10 +698,10 @@ def git_env() -> dict[str, str]: env.setdefault("GIT_TERMINAL_PROMPT", "0") env.setdefault("NO_COLOR", "1") env.setdefault("LC_ALL", "C") - env["GIT_AUTHOR_NAME"] = "AgentFS Benchmark" - env["GIT_AUTHOR_EMAIL"] = "agentfs-benchmark@example.invalid" - env["GIT_COMMITTER_NAME"] = "AgentFS Benchmark" - env["GIT_COMMITTER_EMAIL"] = "agentfs-benchmark@example.invalid" + env["GIT_AUTHOR_NAME"] = "Vfs Benchmark" + env["GIT_AUTHOR_EMAIL"] = "vfs-benchmark@example.invalid" + env["GIT_COMMITTER_NAME"] = "Vfs Benchmark" + env["GIT_COMMITTER_EMAIL"] = "vfs-benchmark@example.invalid" return env @@ -740,9 +740,9 @@ def create_generated_repo(root: Path, file_count: int, dir_count: int, file_size init = run_git(["init"], root, env=env) require_git_ok(init, "git init generated repo") require_git_ok(run_git(["checkout", "-B", "main"], root, env=env), "git checkout main") - require_git_ok(run_git(["config", "user.name", "AgentFS Benchmark"], root, env=env), "git config user.name") + require_git_ok(run_git(["config", "user.name", "Vfs Benchmark"], root, env=env), "git config user.name") require_git_ok( - run_git(["config", "user.email", "agentfs-benchmark@example.invalid"], root, env=env), + run_git(["config", "user.email", "vfs-benchmark@example.invalid"], root, env=env), "git config user.email", ) @@ -753,18 +753,18 @@ def create_generated_repo(root: Path, file_count: int, dir_count: int, file_size directory.mkdir(parents=True, exist_ok=True) if category == "src": filename = f"module_{index:05d}.py" - header = f"# Generated source {index}\nTOKEN = 'AGENTFS_TOKEN_{index % 11}'\n" + header = f"# Generated source {index}\nTOKEN = 'VFS_TOKEN_{index % 11}'\n" elif category == "tests": filename = f"test_{index:05d}.py" - header = f"# Generated test {index}\ndef test_{index:05d}():\n assert 'AGENTFS_TOKEN'\n" + header = f"# Generated test {index}\ndef test_{index:05d}():\n assert 'VFS_TOKEN'\n" elif category == "docs": filename = f"note_{index:05d}.md" - header = f"# Generated note {index}\n\nAGENTFS_TOKEN documentation fixture.\n" + header = f"# Generated note {index}\n\nVFS_TOKEN documentation fixture.\n" else: filename = f"blob_{index:05d}.txt" - header = f"data fixture {index} AGENTFS_TOKEN\n" - seed = hashlib.sha256(f"agentfs-git-fixture-{index}".encode("utf-8")).hexdigest() - filler = "".join(f"{line:04d} {seed} AGENTFS_TOKEN_{line % 7}\n" for line in range(128)) + header = f"data fixture {index} VFS_TOKEN\n" + seed = hashlib.sha256(f"vfs-git-fixture-{index}".encode("utf-8")).hexdigest() + filler = "".join(f"{line:04d} {seed} VFS_TOKEN_{line % 7}\n" for line in range(128)) content = (header + filler)[:file_size] if not content.endswith("\n"): content += "\n" @@ -779,10 +779,10 @@ def create_generated_repo(root: Path, file_count: int, dir_count: int, file_size touched = sorted((root / "src").rglob("*.py"))[: max(1, min(4, file_count))] for index, path in enumerate(touched): with path.open("a", encoding="utf-8") as handle: - handle.write(f"\n# second commit marker {index} AGENTFS_TOKEN\n") + handle.write(f"\n# second commit marker {index} VFS_TOKEN\n") require_git_ok(run_git(["add", "."], root, env=env), "git add second commit") require_git_ok(run_git(["commit", "-m", "update source markers"], root, env=env), "git commit second") - require_git_ok(run_git(["tag", "agentfs-benchmark-fixture"], root, env=env), "git tag fixture") + require_git_ok(run_git(["tag", "vfs-benchmark-fixture"], root, env=env), "git tag fixture") def prepare_bare_mirror(args: argparse.Namespace, temp_root: Path) -> tuple[Path, dict[str, Any]]: @@ -849,9 +849,9 @@ def prepare_environment(temp_root: Path, profile: bool) -> dict[str, str]: env.setdefault("GIT_CONFIG_NOSYSTEM", "1") env.setdefault("GIT_TERMINAL_PROMPT", "0") if profile: - env["AGENTFS_PROFILE"] = "1" + env["VFS_PROFILE"] = "1" else: - env.pop("AGENTFS_PROFILE", None) + env.pop("VFS_PROFILE", None) home = temp_root / "home" for path in (home, home / ".config", home / ".cache", home / ".local" / "share"): @@ -1070,18 +1070,18 @@ def workload_argv(args: argparse.Namespace) -> list[str]: return argv -def phase_ratios(native_workload: Optional[dict[str, Any]], agentfs_workload: Optional[dict[str, Any]]) -> dict[str, Any]: +def phase_ratios(native_workload: Optional[dict[str, Any]], vfs_workload: Optional[dict[str, Any]]) -> dict[str, Any]: native_phases = native_workload.get("phase_seconds", {}) if isinstance(native_workload, dict) else {} - agentfs_phases = agentfs_workload.get("phase_seconds", {}) if isinstance(agentfs_workload, dict) else {} - names = sorted(set(native_phases) | set(agentfs_phases)) + vfs_phases = vfs_workload.get("phase_seconds", {}) if isinstance(vfs_workload, dict) else {} + names = sorted(set(native_phases) | set(vfs_phases)) ratios = {} for name in names: native_value = native_phases.get(name) - agentfs_value = agentfs_phases.get(name) + vfs_value = vfs_phases.get(name) ratios[name] = { "native_seconds": native_value, - "agentfs_seconds": agentfs_value, - "ratio": (agentfs_value / native_value) if isinstance(native_value, (int, float)) and native_value > 0 and isinstance(agentfs_value, (int, float)) else None, + "vfs_seconds": vfs_value, + "ratio": (vfs_value / native_value) if isinstance(native_value, (int, float)) and native_value > 0 and isinstance(vfs_value, (int, float)) else None, } return ratios @@ -1117,22 +1117,22 @@ def comparable_workload(workload: Optional[dict[str, Any]]) -> Optional[dict[str } -def equivalence(native_workload: Optional[dict[str, Any]], agentfs_workload: Optional[dict[str, Any]]) -> dict[str, Any]: +def equivalence(native_workload: Optional[dict[str, Any]], vfs_workload: Optional[dict[str, Any]]) -> dict[str, Any]: native_compare = comparable_workload(native_workload) - agentfs_compare = comparable_workload(agentfs_workload) - if native_compare is None or agentfs_compare is None: + vfs_compare = comparable_workload(vfs_workload) + if native_compare is None or vfs_compare is None: return { "checked": False, "equivalent": False, "reason": "missing comparable workload JSON", "native": native_compare, - "agentfs": agentfs_compare, + "vfs": vfs_compare, } return { "checked": True, - "equivalent": native_compare == agentfs_compare, + "equivalent": native_compare == vfs_compare, "native": native_compare, - "agentfs": agentfs_compare, + "vfs": vfs_compare, } @@ -1142,10 +1142,10 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-git-workload-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-git-workload-")) else: temp_manager = tempfile.TemporaryDirectory( - prefix="agentfs-git-workload-", ignore_cleanup_errors=True + prefix="vfs-git-workload-", ignore_cleanup_errors=True ) temp_root = Path(temp_manager.name) @@ -1154,34 +1154,34 @@ def main(argv: list[str]) -> int: try: common.pin_distro_git(os.environ, temp_root) require_git() - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) env = prepare_environment(temp_root, args.profile) session = args.session or f"git-workload-{uuid.uuid4().hex}" - db_path = Path(env["HOME"]) / ".agentfs" / "run" / session / "delta.db" + db_path = Path(env["HOME"]) / ".vfs" / "run" / session / "delta.db" mirror, source_info = prepare_bare_mirror(args, temp_root) native_root = temp_root / "native" - agentfs_base_root = temp_root / "agentfs-base" + vfs_base_root = temp_root / "vfs-base" copy_mirror(mirror, native_root) - copy_mirror(mirror, agentfs_base_root) + copy_mirror(mirror, vfs_base_root) - base_before = tree_hash(agentfs_base_root) + base_before = tree_hash(vfs_base_root) base_workload = workload_argv(args) native_run = run_subprocess(base_workload, native_root, env, args.timeout) - agentfs_command = [ - agentfs_bin, + vfs_command = [ + vfs_bin, "run", "--session", session, "--no-default-allows", "--", ] + base_workload - agentfs_run = run_subprocess(agentfs_command, agentfs_base_root, env, args.timeout) - base_after = tree_hash(agentfs_base_root) + vfs_run = run_subprocess(vfs_command, vfs_base_root, env, args.timeout) + base_after = tree_hash(vfs_base_root) db_after = db_artifacts(db_path) inspect_after = inspect_db(db_path) integrity_run = run_subprocess( - [agentfs_bin, "integrity", str(db_path), "--json", "--require-portable"], + [vfs_bin, "integrity", str(db_path), "--json", "--require-portable"], temp_root, env, args.timeout, @@ -1189,7 +1189,7 @@ def main(argv: list[str]) -> int: integrity_payload = parse_json_stdout(integrity_run) backup_path = temp_root / "git-workload-backup.db" backup_run = run_subprocess( - [agentfs_bin, "backup", str(db_path), str(backup_path), "--verify"], + [vfs_bin, "backup", str(db_path), str(backup_path), "--verify"], temp_root, env, args.timeout, @@ -1198,24 +1198,24 @@ def main(argv: list[str]) -> int: backup_inspect = inspect_db(backup_path) native_workload = parse_json_stdout(native_run) - agentfs_workload = parse_json_stdout(agentfs_run) - equivalent = equivalence(native_workload, agentfs_workload) - profile_summaries = agentfs_run.get("profile_summaries", []) + vfs_workload = parse_json_stdout(vfs_run) + equivalent = equivalence(native_workload, vfs_workload) + profile_summaries = vfs_run.get("profile_summaries", []) profile_counters = profile_counter_summary(profile_summaries) phase_labels = ( - agentfs_workload.get("profile_checkpoints", []) - if isinstance(agentfs_workload, dict) + vfs_workload.get("profile_checkpoints", []) + if isinstance(vfs_workload, dict) else [] ) per_phase_counters = per_phase_profile_counters(profile_summaries, phase_labels) - ratios = phase_ratios(native_workload, agentfs_workload) + ratios = phase_ratios(native_workload, vfs_workload) native_total = native_workload.get("total_seconds") if isinstance(native_workload, dict) else None - agentfs_total = agentfs_workload.get("total_seconds") if isinstance(agentfs_workload, dict) else None + vfs_total = vfs_workload.get("total_seconds") if isinstance(vfs_workload, dict) else None overall_ratio = ( - agentfs_total / native_total + vfs_total / native_total if isinstance(native_total, (int, float)) and native_total > 0 - and isinstance(agentfs_total, (int, float)) + and isinstance(vfs_total, (int, float)) else None ) base_unchanged = base_before["sha256"] == base_after["sha256"] @@ -1252,18 +1252,18 @@ def main(argv: list[str]) -> int: correctness = { "native_returncode_zero": native_run["returncode"] == 0, - "agentfs_returncode_zero": agentfs_run["returncode"] == 0, + "vfs_returncode_zero": vfs_run["returncode"] == 0, "equivalence": equivalent, - "agentfs_base_unchanged": base_unchanged, - "agentfs_db_inspectable": inspectable, - "agentfs_portable": portable is True, - "agentfs_no_nonempty_sidecars": no_sidecars, - "agentfs_integrity_require_portable": integrity_ok, - "agentfs_backup_verify": backup_ok, + "vfs_base_unchanged": base_unchanged, + "vfs_db_inspectable": inspectable, + "vfs_portable": portable is True, + "vfs_no_nonempty_sidecars": no_sidecars, + "vfs_integrity_require_portable": integrity_ok, + "vfs_backup_verify": backup_ok, "performance_passed": performance_passed, "passed": ( native_run["returncode"] == 0 - and agentfs_run["returncode"] == 0 + and vfs_run["returncode"] == 0 and equivalent.get("equivalent") is True and base_unchanged and portability_ok @@ -1283,8 +1283,8 @@ def main(argv: list[str]) -> int: "command": { "argv": [str(Path(__file__).resolve())] + argv, "workload_argv": base_workload, - "agentfs_prefix": [ - agentfs_bin, + "vfs_prefix": [ + vfs_bin, "run", "--session", session, @@ -1293,8 +1293,8 @@ def main(argv: list[str]) -> int: ], }, "environment": { - "AGENTFS_PROFILE": env.get("AGENTFS_PROFILE"), - "AGENTFS_BIN": args.agentfs_bin, + "VFS_PROFILE": env.get("VFS_PROFILE"), + "VFS_BIN": args.vfs_bin, }, "parameters": { "fixture_files": args.fixture_files, @@ -1308,8 +1308,8 @@ def main(argv: list[str]) -> int: "timeout_seconds": args.timeout, }, "source": source_info, - "agentfs": { - "bin": agentfs_bin, + "vfs": { + "bin": vfs_bin, "session": session, "db_path": str(db_path), "profile_enabled": args.profile, @@ -1319,13 +1319,13 @@ def main(argv: list[str]) -> int: }, "summary": { "native_seconds": native_total, - "agentfs_seconds": agentfs_total, + "vfs_seconds": vfs_total, "ratio": overall_ratio, "phase_ratios": ratios, "threshold_failures": threshold_failures, "performance_passed": performance_passed, "all_equivalent": equivalent.get("equivalent") is True, - "agentfs_base_unchanged": base_unchanged, + "vfs_base_unchanged": base_unchanged, "passed": correctness["passed"], "correctness_passed": correctness["passed"], }, @@ -1333,9 +1333,9 @@ def main(argv: list[str]) -> int: "run": strip_stdout(native_run), "workload": native_workload, }, - "agentfs_overlay": { - "run": strip_stdout(agentfs_run), - "workload": agentfs_workload, + "vfs_overlay": { + "run": strip_stdout(vfs_run), + "workload": vfs_workload, "profile_summaries": profile_summaries, }, "base_tree": { diff --git a/scripts/validation/large-edit-benchmark.py b/scripts/validation/large-edit-benchmark.py index f04765b8..5107aa30 100755 --- a/scripts/validation/large-edit-benchmark.py +++ b/scripts/validation/large-edit-benchmark.py @@ -109,7 +109,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( description=( "Compare a native single-byte edit to the same edit through an " - "AgentFS overlay and report delta DB growth." + "Vfs overlay and report delta DB growth." ), formatter_class=argparse.RawDescriptionHelpFormatter, epilog="""Examples: @@ -120,8 +120,8 @@ def parse_args(argv: list[str]) -> argparse.Namespace: scripts/validation/large-edit-benchmark.py --file-size-mib 1 --timeout 60 Environment: - AGENTFS_BIN path/name of agentfs executable - AGENTFS_PROFILE set to 1 to collect AgentFS profile summaries + VFS_BIN path/name of vfs executable + VFS_PROFILE set to 1 to collect Vfs profile summaries """, ) parser.add_argument( @@ -136,9 +136,9 @@ def parse_args(argv: list[str]) -> argparse.Namespace: help="byte offset to edit (default: middle of the file)", ) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -149,26 +149,26 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser.add_argument( "--session", default=None, - help="AgentFS run session id (default: generated unique id)", + help="Vfs run session id (default: generated unique id)", ) parser.add_argument( "--profile", action="store_true", - default=env_flag("AGENTFS_PROFILE"), - help="enable AGENTFS_PROFILE=1 for AgentFS invocations", + default=env_flag("VFS_PROFILE"), + help="enable VFS_PROFILE=1 for Vfs invocations", ) partial_origin_group = parser.add_mutually_exclusive_group() partial_origin_group.add_argument( "--partial-origin", dest="partial_origin", action="store_true", - help="pass --partial-origin on to AgentFS overlay invocations", + help="pass --partial-origin on to Vfs overlay invocations", ) partial_origin_group.add_argument( "--no-partial-origin", dest="partial_origin", action="store_false", - help="omit --partial-origin for AgentFS overlay invocations", + help="omit --partial-origin for Vfs overlay invocations", ) parser.set_defaults(partial_origin=False) parser.add_argument( @@ -213,13 +213,13 @@ def extract_profile_summaries(stderr: Any) -> list[dict[str, Any]]: summaries: list[dict[str, Any]] = [] for line in text.splitlines(): line = line.strip() - if not line or "agentfs_profile_summary" not in line: + if not line or "vfs_profile_summary" not in line: continue try: value = json.loads(line) except json.JSONDecodeError: continue - if isinstance(value, dict) and value.get("event") == "agentfs_profile_summary": + if isinstance(value, dict) and value.get("event") == "vfs_profile_summary": summaries.append(value) return summaries @@ -293,20 +293,20 @@ def run_subprocess( } -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate_path = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate_path = Path(vfs_bin).expanduser() if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") for candidate_path in ( - repo_root / "cli" / "target" / "debug" / "agentfs", - repo_root / "cli" / "target" / "release" / "agentfs", + repo_root / "cli" / "target" / "debug" / "vfs", + repo_root / "cli" / "target" / "release" / "vfs", ): if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path) @@ -320,12 +320,12 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN to an explicit binary\n" + "failed to build repo-local vfs binary; set VFS_BIN to an explicit binary\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = repo_root / "cli" / "target" / "debug" / "agentfs" + built = repo_root / "cli" / "target" / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) @@ -352,7 +352,7 @@ def create_large_file(path: Path, size_bytes: int) -> str: block_index = 0 with path.open("wb") as handle: while written < size_bytes: - seed = hashlib.sha256(f"agentfs-phase5-large-edit-{block_index}".encode()).digest() + seed = hashlib.sha256(f"vfs-phase5-large-edit-{block_index}".encode()).digest() block = (seed * ((ONE_MIB // len(seed)) + 1))[: min(ONE_MIB, size_bytes - written)] handle.write(block) digest.update(block) @@ -489,7 +489,7 @@ def prepare_environment(temp_root: Path, profile: bool) -> dict[str, str]: env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") if profile: - env["AGENTFS_PROFILE"] = "1" + env["VFS_PROFILE"] = "1" home = temp_root / "home" for path in (home, home / ".config", home / ".cache", home / ".local" / "share"): @@ -517,79 +517,79 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-large-edit-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-large-edit-")) else: - temp_manager = tempfile.TemporaryDirectory(prefix="agentfs-large-edit-") + temp_manager = tempfile.TemporaryDirectory(prefix="vfs-large-edit-") temp_root = Path(temp_manager.name) exit_code = 0 result: dict[str, Any] try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) env = prepare_environment(temp_root, args.profile) session = args.session or f"large-edit-{uuid.uuid4()}" source_root = temp_root / "source" native_root = temp_root / "native" - agentfs_base_root = temp_root / "agentfs-base" + vfs_base_root = temp_root / "vfs-base" source_file = source_root / "large.bin" original_sha = create_large_file(source_file, file_size_bytes) copy_base_tree(source_root, native_root) - copy_base_tree(source_root, agentfs_base_root) + copy_base_tree(source_root, vfs_base_root) - db_path = Path(env["HOME"]) / ".agentfs" / "run" / session / "delta.db" - agentfs_run_prefix = [ - agentfs_bin, + db_path = Path(env["HOME"]) / ".vfs" / "run" / session / "delta.db" + vfs_run_prefix = [ + vfs_bin, "run", "--session", session, "--no-default-allows", ] if args.partial_origin: - agentfs_run_prefix.extend(["--partial-origin", "on"]) + vfs_run_prefix.extend(["--partial-origin", "on"]) warmup_command = [ - *agentfs_run_prefix, + *vfs_run_prefix, "--", sys.executable, "-c", READONLY_WARMUP, ] - warmup = run_subprocess(warmup_command, agentfs_base_root, env, args.timeout) + warmup = run_subprocess(warmup_command, vfs_base_root, env, args.timeout) db_before = db_artifacts(db_path) inspect_before = inspect_db(db_path) native_command = [sys.executable, "-c", EDIT_WORKLOAD, "large.bin", str(offset)] - agentfs_command = [*agentfs_run_prefix, "--"] + native_command + vfs_command = [*vfs_run_prefix, "--"] + native_command native = run_subprocess(native_command, native_root, env, args.timeout) - agentfs = run_subprocess(agentfs_command, agentfs_base_root, env, args.timeout) + vfs = run_subprocess(vfs_command, vfs_base_root, env, args.timeout) db_after = db_artifacts(db_path) inspect_after = inspect_db(db_path) native_json = parse_json_stdout(native) - agentfs_json = parse_json_stdout(agentfs) - agentfs_base_sha_after = hash_file(agentfs_base_root / "large.bin") + vfs_json = parse_json_stdout(vfs) + vfs_base_sha_after = hash_file(vfs_base_root / "large.bin") native_sha_after = hash_file(native_root / "large.bin") comparable_fields = ("size", "size_before", "offset", "old_byte", "new_byte", "sha256") outputs_match = ( native_json is not None - and agentfs_json is not None - and all(native_json.get(field) == agentfs_json.get(field) for field in comparable_fields) + and vfs_json is not None + and all(native_json.get(field) == vfs_json.get(field) for field in comparable_fields) ) correctness = { "native_returncode_zero": native["returncode"] == 0, - "agentfs_returncode_zero": agentfs["returncode"] == 0, + "vfs_returncode_zero": vfs["returncode"] == 0, "warmup_returncode_zero": warmup["returncode"] == 0, "outputs_match": outputs_match, - "agentfs_base_unchanged": agentfs_base_sha_after == original_sha, + "vfs_base_unchanged": vfs_base_sha_after == original_sha, "native_file_changed": native_sha_after != original_sha, "passed": ( warmup["returncode"] == 0 and native["returncode"] == 0 - and agentfs["returncode"] == 0 + and vfs["returncode"] == 0 and outputs_match - and agentfs_base_sha_after == original_sha + and vfs_base_sha_after == original_sha and native_sha_after != original_sha ), } @@ -606,14 +606,14 @@ def main(argv: list[str]) -> int: "offset": offset, "edit_width_bytes": 1, }, - "agentfs": { - "bin": agentfs_bin, + "vfs": { + "bin": vfs_bin, "session": session, "db_path": str(db_path), "profile_enabled": args.profile, "partial_origin_enabled": args.partial_origin, "partial_origin_cli": "on" if args.partial_origin else "omitted", - "profile_summary_count": len(warmup["profile_summaries"]) + len(agentfs["profile_summaries"]), + "profile_summary_count": len(warmup["profile_summaries"]) + len(vfs["profile_summaries"]), }, "database": { "before_edit": db_before, @@ -627,16 +627,16 @@ def main(argv: list[str]) -> int: "run": native, "result": native_json, }, - "agentfs_overlay": { - "duration_seconds": agentfs["duration_seconds"], + "vfs_overlay": { + "duration_seconds": vfs["duration_seconds"], "warmup": warmup, - "run": agentfs, - "result": agentfs_json, + "run": vfs, + "result": vfs_json, }, "base_file": { "original_sha256": original_sha, "native_sha256_after": native_sha_after, - "agentfs_base_sha256_after": agentfs_base_sha_after, + "vfs_base_sha256_after": vfs_base_sha_after, }, "correctness": correctness, "temp_dir": str(temp_root), diff --git a/scripts/validation/lib/common.py b/scripts/validation/lib/common.py index 6e55f5cb..957441cd 100644 --- a/scripts/validation/lib/common.py +++ b/scripts/validation/lib/common.py @@ -5,7 +5,7 @@ the package from any CWD: sys.path.insert(0, str(Path(__file__).resolve().parent)) - from lib.common import resolve_agentfs_bin, run_subprocess + from lib.common import resolve_vfs_bin, run_subprocess """ from __future__ import annotations @@ -54,12 +54,12 @@ def pin_distro_git( whose `git` symlinks the distro binary, and point the global git config at a hookless file so no hook manager can re-enter via config. - The PATH shim only holds for host-side processes: inside an `agentfs run` + The PATH shim only holds for host-side processes: inside an `vfs run` sandbox, temp dirs and home files are hidden, so any shim dir silently falls out of PATH and the hook-manager git takes over. That is what ``env["GIT"]`` is for — it names the distro binary by absolute (system, sandbox-visible) path, and every harness git invocation must honor it, - including the workload scripts spawned inside `agentfs run`. Pass ``home`` + including the workload scripts spawned inside `vfs run`. Pass ``home`` to also write a hookless ``~/.gitconfig`` into a temp HOME (belt and braces for host-side legs). """ @@ -107,7 +107,7 @@ def git_ai_processes() -> dict[int, dict[str, Any]]: info: dict[str, Any] = { "cmdline": " ".join(argv), "home": None, - "agentfs_session": False, + "vfs_session": False, } try: environ = (entry / "environ").read_bytes() @@ -116,8 +116,8 @@ def git_ai_processes() -> dict[int, dict[str, Any]]: for chunk in environ.split(b"\0"): if chunk.startswith(b"HOME="): info["home"] = chunk[len(b"HOME="):].decode("utf-8", "replace") - elif chunk.startswith(b"AGENTFS_SESSION="): - info["agentfs_session"] = True + elif chunk.startswith(b"VFS_SESSION="): + info["vfs_session"] = True procs[int(entry.name)] = info return procs @@ -129,7 +129,7 @@ def git_ai_leaks( The user's own hook manager churns independently (its daemon respawns with HOME under the real home dir), so a bare pid diff false-positives. Count - only new processes with a temp-dir HOME or an AGENTFS session in their + only new processes with a temp-dir HOME or an VFS session in their environment — the same discrimination rule library/environment.md requires before killing one. """ @@ -139,7 +139,7 @@ def git_ai_leaks( if pid in before: continue home = info.get("home") or "" - if info.get("agentfs_session") or home.startswith(tmp_prefix): + if info.get("vfs_session") or home.startswith(tmp_prefix): leaks.append({"pid": pid, **info}) return leaks @@ -260,29 +260,29 @@ def workspace_target_dir(repo_root: Path) -> Path: return repo_root / "target" -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate = Path(vfs_bin).expanduser() if candidate.is_file() and os.access(candidate, os.X_OK): return str(candidate.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") target_dir = workspace_target_dir(repo_root) # Release first: it is what the gates measure and it is rebuilt more often # during active development, so it is less likely to be stale. for candidate in ( - target_dir / "release" / "agentfs", - target_dir / "debug" / "agentfs", + target_dir / "release" / "vfs", + target_dir / "debug" / "vfs", ): if candidate.is_file() and os.access(candidate, os.X_OK): return str(candidate) build = subprocess.run( - ["cargo", "build", "-p", "agentfs-cli", "--manifest-path", str(repo_root / "Cargo.toml")], + ["cargo", "build", "-p", "vfs-cli", "--manifest-path", str(repo_root / "Cargo.toml")], cwd=str(repo_root), text=True, stdout=subprocess.PIPE, @@ -290,11 +290,11 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN explicitly\n" + "failed to build repo-local vfs binary; set VFS_BIN explicitly\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = target_dir / "debug" / "agentfs" + built = target_dir / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) raise RuntimeError(f"repo-local build completed but binary was not found: {built}") diff --git a/scripts/validation/macos-nfs-git-validation.sh b/scripts/validation/macos-nfs-git-validation.sh index 8fbd60ae..fef54c03 100755 --- a/scripts/validation/macos-nfs-git-validation.sh +++ b/scripts/validation/macos-nfs-git-validation.sh @@ -1,28 +1,28 @@ #!/usr/bin/env bash # # Validate the macOS NFS path for git loose-object writes (#333) and the -# `agentfs run` seatbelt read-scoping posture (secret outside the allow +# `vfs run` seatbelt read-scoping posture (secret outside the allow # list must be unreadable; `--allow` must make it readable). # # Usage: -# macos-nfs-git-validation.sh [--agentfs-bin PATH] [--report-dir DIR] [--keep-work] +# macos-nfs-git-validation.sh [--vfs-bin PATH] [--report-dir DIR] [--keep-work] # # Environment: -# AGENTFS_BIN agentfs executable to invoke (default: agentfs) +# VFS_BIN vfs executable to invoke (default: vfs) # REPORT_DIR directory where logs should be written # SKIP_CODE exit code for unsupported platform/prerequisites (default: 77) # set -Eeuo pipefail SKIP_CODE="${SKIP_CODE:-77}" -AGENTFS_BIN="${AGENTFS_BIN:-agentfs}" +VFS_BIN="${VFS_BIN:-vfs}" REPORT_DIR="${REPORT_DIR:-}" KEEP_WORK=0 WORK_DIR="" MOUNT_DIR="" MOUNT_PID="" -AGENTFS_RESOLVED="" +VFS_RESOLVED="" RUN_WORK_DIR="" SECRET_DIR="" RUN_SESSION_DENY="" @@ -37,12 +37,12 @@ skip() { exit "$SKIP_CODE" } -resolve_agentfs() { - if [[ "$AGENTFS_BIN" == */* ]]; then - [[ -x "$AGENTFS_BIN" ]] || return 1 - AGENTFS_RESOLVED="$AGENTFS_BIN" +resolve_vfs() { + if [[ "$VFS_BIN" == */* ]]; then + [[ -x "$VFS_BIN" ]] || return 1 + VFS_RESOLVED="$VFS_BIN" else - AGENTFS_RESOLVED="$(command -v "$AGENTFS_BIN" 2>/dev/null)" || return 1 + VFS_RESOLVED="$(command -v "$VFS_BIN" 2>/dev/null)" || return 1 fi } @@ -50,7 +50,7 @@ safe_rm_tmp() { local path="$1" [[ -n "$path" ]] || return 0 case "$path" in - /tmp/agentfs-macos-nfs-git-work.*|/tmp/agentfs-macos-nfs-git-mnt.*|/private/tmp/agentfs-macos-nfs-git-work.*|/private/tmp/agentfs-macos-nfs-git-mnt.*|/tmp/agentfs-macos-read-scope-work.*|/private/tmp/agentfs-macos-read-scope-work.*) + /tmp/vfs-macos-nfs-git-work.*|/tmp/vfs-macos-nfs-git-mnt.*|/private/tmp/vfs-macos-nfs-git-work.*|/private/tmp/vfs-macos-nfs-git-mnt.*|/tmp/vfs-macos-read-scope-work.*|/private/tmp/vfs-macos-read-scope-work.*) rm -rf -- "$path" ;; *) @@ -63,7 +63,7 @@ safe_rm_secret_dir() { local path="$1" [[ -n "$path" ]] || return 0 case "$path" in - */.agentfs-macos-read-scope.*) + */.vfs-macos-read-scope.*) rm -rf -- "$path" ;; *) @@ -77,7 +77,7 @@ safe_rm_run_session() { [[ -n "$session" ]] || return 0 case "$session" in macos-read-scope-*) - rm -rf -- "${HOME:?}/.agentfs/run/$session" + rm -rf -- "${HOME:?}/.vfs/run/$session" ;; *) printf 'Refusing to remove non-harness run session: %s\n' "$session" >&2 @@ -133,8 +133,8 @@ cleanup() { printf 'Kept mount directory: %s\n' "$MOUNT_DIR" >&2 [[ -n "$RUN_WORK_DIR" ]] && printf 'Kept run work directory: %s\n' "$RUN_WORK_DIR" >&2 [[ -n "$SECRET_DIR" ]] && printf 'Kept secret directory: %s\n' "$SECRET_DIR" >&2 - [[ -n "$RUN_SESSION_DENY" ]] && printf 'Kept run session: %s\n' "$HOME/.agentfs/run/$RUN_SESSION_DENY" >&2 - [[ -n "$RUN_SESSION_ALLOW" ]] && printf 'Kept run session: %s\n' "$HOME/.agentfs/run/$RUN_SESSION_ALLOW" >&2 + [[ -n "$RUN_SESSION_DENY" ]] && printf 'Kept run session: %s\n' "$HOME/.vfs/run/$RUN_SESSION_DENY" >&2 + [[ -n "$RUN_SESSION_ALLOW" ]] && printf 'Kept run session: %s\n' "$HOME/.vfs/run/$RUN_SESSION_ALLOW" >&2 fi exit "$status" @@ -142,9 +142,9 @@ cleanup() { while [[ $# -gt 0 ]]; do case "$1" in - --agentfs-bin) - [[ $# -ge 2 ]] || { echo "missing value for --agentfs-bin" >&2; exit 2; } - AGENTFS_BIN="$2" + --vfs-bin) + [[ $# -ge 2 ]] || { echo "missing value for --vfs-bin" >&2; exit 2; } + VFS_BIN="$2" shift 2 ;; --report-dir) @@ -173,7 +173,7 @@ if [[ "$(uname -s)" != "Darwin" ]]; then fi missing=() -resolve_agentfs || missing+=("agentfs") +resolve_vfs || missing+=("vfs") command -v git >/dev/null 2>&1 || missing+=("git") [[ -x /sbin/mount_nfs ]] || missing+=("/sbin/mount_nfs") [[ -x /sbin/umount ]] || missing+=("/sbin/umount") @@ -186,36 +186,36 @@ if [[ ${#missing[@]} -gt 0 ]]; then fi if [[ -z "$REPORT_DIR" ]]; then - REPORT_DIR="$(mktemp -d /tmp/agentfs-macos-nfs-git-report.XXXXXX)" + REPORT_DIR="$(mktemp -d /tmp/vfs-macos-nfs-git-report.XXXXXX)" else mkdir -p "$REPORT_DIR" REPORT_DIR="$(cd "$REPORT_DIR" && pwd)" fi -WORK_DIR="$(canonical_dir "$(mktemp -d /tmp/agentfs-macos-nfs-git-work.XXXXXX)")" -MOUNT_DIR="$(canonical_dir "$(mktemp -d /tmp/agentfs-macos-nfs-git-mnt.XXXXXX)")" +WORK_DIR="$(canonical_dir "$(mktemp -d /tmp/vfs-macos-nfs-git-work.XXXXXX)")" +MOUNT_DIR="$(canonical_dir "$(mktemp -d /tmp/vfs-macos-nfs-git-mnt.XXXXXX)")" trap cleanup EXIT INT TERM AGENT_ID="macos-nfs-git-$$-$(date +%s)" -DB_PATH="$WORK_DIR/.agentfs/$AGENT_ID.db" +DB_PATH="$WORK_DIR/.vfs/$AGENT_ID.db" -printf 'AgentFS binary: %s\n' "$AGENTFS_RESOLVED" +printf 'Vfs binary: %s\n' "$VFS_RESOLVED" printf 'Report directory: %s\n' "$REPORT_DIR" printf 'Work directory: %s\n' "$WORK_DIR" printf 'Mount directory: %s\n' "$MOUNT_DIR" ( cd "$WORK_DIR" - "$AGENTFS_RESOLVED" init "$AGENT_ID" + "$VFS_RESOLVED" init "$AGENT_ID" ) >"$REPORT_DIR/init.log" 2>&1 if [[ ! -f "$DB_PATH" ]]; then - printf 'FAILED: expected AgentFS database was not created at %s\n' "$DB_PATH" >&2 + printf 'FAILED: expected Vfs database was not created at %s\n' "$DB_PATH" >&2 printf 'See %s/init.log\n' "$REPORT_DIR" >&2 exit 1 fi -"$AGENTFS_RESOLVED" mount --backend nfs "$DB_PATH" "$MOUNT_DIR" --foreground >"$REPORT_DIR/mount.log" 2>&1 & +"$VFS_RESOLVED" mount --backend nfs "$DB_PATH" "$MOUNT_DIR" --foreground >"$REPORT_DIR/mount.log" 2>&1 & MOUNT_PID=$! mounted=0 @@ -234,7 +234,7 @@ if [[ "$mounted" -ne 1 ]]; then if grep -Eqi 'operation not permitted|permission denied|not permitted|must be root|requires.*root' "$REPORT_DIR/mount.log"; then skip "mount_nfs is unavailable to this user; see $REPORT_DIR/mount.log" fi - printf 'FAILED: AgentFS NFS mount did not become ready at %s\n' "$MOUNT_DIR" >&2 + printf 'FAILED: Vfs NFS mount did not become ready at %s\n' "$MOUNT_DIR" >&2 printf 'See %s/mount.log\n' "$REPORT_DIR" >&2 exit 1 fi @@ -244,9 +244,9 @@ set +e set -Eeuo pipefail cd "$MOUNT_DIR" git init - git config user.name "AgentFS macOS NFS validation" - git config user.email "agentfs-validation@example.invalid" - printf 'hello from AgentFS macOS NFS validation\n' >README.txt + git config user.name "Vfs macOS NFS validation" + git config user.email "vfs-validation@example.invalid" + printf 'hello from Vfs macOS NFS validation\n' >README.txt git add README.txt git commit -m "validate macos nfs git loose objects" git fsck --strict @@ -267,16 +267,16 @@ if [[ "$git_status" -ne 0 ]]; then exit "$git_status" fi -# --- agentfs run read-scoping leg ----------------------------------------- +# --- vfs run read-scoping leg ----------------------------------------- # The darwin seatbelt profile is default-deny for reads: a secret outside the # session/allow/platform roots must be unreadable, and `--allow` must make it # readable (and writable, as before). -printf 'Running agentfs run read-scoping checks...\n' +printf 'Running vfs run read-scoping checks...\n' -RUN_WORK_DIR="$(canonical_dir "$(mktemp -d /tmp/agentfs-macos-read-scope-work.XXXXXX)")" -SECRET_DIR="$(canonical_dir "$(mktemp -d "$HOME/.agentfs-macos-read-scope.XXXXXX")")" -printf 'agentfs-read-scope-secret\n' >"$SECRET_DIR/secret.txt" +RUN_WORK_DIR="$(canonical_dir "$(mktemp -d /tmp/vfs-macos-read-scope-work.XXXXXX)")" +SECRET_DIR="$(canonical_dir "$(mktemp -d "$HOME/.vfs-macos-read-scope.XXXXXX")")" +printf 'vfs-read-scope-secret\n' >"$SECRET_DIR/secret.txt" RUN_SESSION_DENY="macos-read-scope-deny-$$-$(date +%s)" RUN_SESSION_ALLOW="macos-read-scope-allow-$$-$(date +%s)" @@ -284,21 +284,21 @@ RUN_SESSION_ALLOW="macos-read-scope-allow-$$-$(date +%s)" set +e ( cd "$RUN_WORK_DIR" - "$AGENTFS_RESOLVED" run --session "$RUN_SESSION_DENY" \ + "$VFS_RESOLVED" run --session "$RUN_SESSION_DENY" \ /bin/cat "$SECRET_DIR/secret.txt" ) >"$REPORT_DIR/read-scope-deny.log" 2>&1 deny_status=$? set -e -if ! grep -q 'Welcome to AgentFS' "$REPORT_DIR/read-scope-deny.log"; then - printf 'FAILED: agentfs run never reached the sandbox (mount failure?). See %s/read-scope-deny.log\n' "$REPORT_DIR" >&2 +if ! grep -q 'Welcome to Vfs' "$REPORT_DIR/read-scope-deny.log"; then + printf 'FAILED: vfs run never reached the sandbox (mount failure?). See %s/read-scope-deny.log\n' "$REPORT_DIR" >&2 exit 1 fi if [[ "$deny_status" -eq 0 ]]; then printf 'FAILED: read of %s/secret.txt outside the allow list unexpectedly succeeded. See %s/read-scope-deny.log\n' "$SECRET_DIR" "$REPORT_DIR" >&2 exit 1 fi -if grep -q 'agentfs-read-scope-secret' "$REPORT_DIR/read-scope-deny.log"; then +if grep -q 'vfs-read-scope-secret' "$REPORT_DIR/read-scope-deny.log"; then printf 'FAILED: secret content leaked despite exit status %s. See %s/read-scope-deny.log\n' "$deny_status" "$REPORT_DIR" >&2 exit 1 fi @@ -311,13 +311,13 @@ printf 'Read of unallowed path denied as expected (exit %s).\n' "$deny_status" set +e ( cd "$RUN_WORK_DIR" - "$AGENTFS_RESOLVED" run --session "$RUN_SESSION_ALLOW" --allow "$SECRET_DIR" \ + "$VFS_RESOLVED" run --session "$RUN_SESSION_ALLOW" --allow "$SECRET_DIR" \ /bin/cat "$SECRET_DIR/secret.txt" ) >"$REPORT_DIR/read-scope-allow.log" 2>&1 allow_status=$? set -e -if [[ "$allow_status" -ne 0 ]] || ! grep -q 'agentfs-read-scope-secret' "$REPORT_DIR/read-scope-allow.log"; then +if [[ "$allow_status" -ne 0 ]] || ! grep -q 'vfs-read-scope-secret' "$REPORT_DIR/read-scope-allow.log"; then printf 'FAILED: --allow %s did not make the secret readable (exit %s). See %s/read-scope-allow.log\n' "$SECRET_DIR" "$allow_status" "$REPORT_DIR" >&2 exit 1 fi diff --git a/scripts/validation/metadata-mutation-no-real-write.py b/scripts/validation/metadata-mutation-no-real-write.py index 3d150ef7..4425441e 100644 --- a/scripts/validation/metadata-mutation-no-real-write.py +++ b/scripts/validation/metadata-mutation-no-real-write.py @@ -2,11 +2,11 @@ """Validate that metadata-class mutations never touch the real base tree. Exercises create / overwrite / truncate / rename / unlink / chmod / utimens and a -concurrent read-after-write through an AgentFS mount, then asserts: +concurrent read-after-write through a Vfs mount, then asserts: 1. the host base tree is byte- and metadata-identical before and after the run (every mutation must land in the single delta database, never the base); - 2. a fresh AgentFS run over the same session database reproduces every mutation + 2. a fresh Vfs run over the same session database reproduces every mutation (proving the virtual state is fully persisted in the single file, with no hidden host-side state). @@ -179,7 +179,7 @@ def env_flag(name: str) -> bool: def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--agentfs-bin", default=os.environ.get("AGENTFS_BIN")) + parser.add_argument("--vfs-bin", default=os.environ.get("VFS_BIN")) parser.add_argument("--session", default=None) parser.add_argument("--timeout", type=positive_float, default=120.0) parser.add_argument("--output", default=None) @@ -188,7 +188,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: "--profile", dest="profile", action="store_true", - default=env_flag("AGENTFS_PROFILE"), + default=env_flag("VFS_PROFILE"), ) parser.add_argument("--keep-temp", action="store_true", default=env_flag("KEEP_TEMP")) return parser.parse_args(argv) @@ -268,23 +268,23 @@ def parse_json_stdout(run: dict[str, Any]) -> Optional[dict[str, Any]]: return None -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate = Path(vfs_bin).expanduser() if candidate.is_file() and os.access(candidate, os.X_OK): return str(candidate.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"agentfs binary not found or not executable: {agentfs_bin}") + raise RuntimeError(f"vfs binary not found or not executable: {vfs_bin}") for candidate in ( - repo_root / "cli" / "target" / "release" / "agentfs", - repo_root / "cli" / "target" / "debug" / "agentfs", + repo_root / "cli" / "target" / "release" / "vfs", + repo_root / "cli" / "target" / "debug" / "vfs", ): if candidate.is_file() and os.access(candidate, os.X_OK): return str(candidate) - raise RuntimeError("no agentfs binary found; pass --agentfs-bin or set AGENTFS_BIN") + raise RuntimeError("no vfs binary found; pass --vfs-bin or set VFS_BIN") def prepare_environment(temp_root: Path, profile: bool) -> dict[str, str]: @@ -292,9 +292,9 @@ def prepare_environment(temp_root: Path, profile: bool) -> dict[str, str]: env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") if profile: - env["AGENTFS_PROFILE"] = "1" + env["VFS_PROFILE"] = "1" else: - env.pop("AGENTFS_PROFILE", None) + env.pop("VFS_PROFILE", None) home = temp_root / "home" for path in (home, home / ".config", home / ".cache", home / ".local" / "share"): path.mkdir(parents=True, exist_ok=True) @@ -348,9 +348,9 @@ def tree_hash(root: Path) -> dict[str, Any]: return {"sha256": digest.hexdigest(), "file_count": file_count} -def agentfs_run_command(agentfs_bin: str, session: str, workload: str) -> list[str]: +def vfs_run_command(vfs_bin: str, session: str, workload: str) -> list[str]: return [ - agentfs_bin, + vfs_bin, "run", "--session", session, @@ -407,15 +407,15 @@ def main(argv: list[str]) -> int: repo_root = Path(__file__).resolve().parents[2] if args.keep_temp: - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-mutation-no-real-write-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-mutation-no-real-write-")) temp_manager = None else: - temp_manager = tempfile.TemporaryDirectory(prefix="agentfs-mutation-no-real-write-") + temp_manager = tempfile.TemporaryDirectory(prefix="vfs-mutation-no-real-write-") temp_root = Path(temp_manager.name) exit_code = 0 try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) env = prepare_environment(temp_root, args.profile) session = args.session or f"mutation-no-real-write-{uuid.uuid4().hex}" base_root = temp_root / "base" @@ -423,14 +423,14 @@ def main(argv: list[str]) -> int: before = tree_hash(base_root) mutation_run = run_subprocess( - agentfs_run_command(agentfs_bin, session, MUTATION_WORKLOAD), + vfs_run_command(vfs_bin, session, MUTATION_WORKLOAD), base_root, env, args.timeout, ) after = tree_hash(base_root) verify_run = run_subprocess( - agentfs_run_command(agentfs_bin, session, VERIFY_WORKLOAD), + vfs_run_command(vfs_bin, session, VERIFY_WORKLOAD), base_root, env, args.timeout, @@ -439,11 +439,11 @@ def main(argv: list[str]) -> int: mutation_json = parse_json_stdout(mutation_run) verify_json = parse_json_stdout(verify_run) - db_path = Path(env["HOME"]) / ".agentfs" / "run" / session / "delta.db" + db_path = Path(env["HOME"]) / ".vfs" / "run" / session / "delta.db" checks = evaluate(mutation_json, verify_json) - checks["agentfs_mutation_rc_zero"] = mutation_run["returncode"] == 0 - checks["agentfs_verify_rc_zero"] = verify_run["returncode"] == 0 + checks["vfs_mutation_rc_zero"] = mutation_run["returncode"] == 0 + checks["vfs_verify_rc_zero"] = verify_run["returncode"] == 0 checks["base_unchanged_after_mutation"] = before["sha256"] == after["sha256"] checks["base_unchanged_after_remount"] = before["sha256"] == after_remount["sha256"] passed = all(bool(v) for v in checks.values()) @@ -453,8 +453,8 @@ def main(argv: list[str]) -> int: result = { "schema_version": 1, "benchmark": "metadata-mutation-no-real-write", - "agentfs": { - "bin": agentfs_bin, + "vfs": { + "bin": vfs_bin, "session": session, "db_path": str(db_path), "profile_enabled": args.profile, diff --git a/scripts/validation/noopen-coherence.py b/scripts/validation/noopen-coherence.py index dc85d5cb..dd1c9f02 100644 --- a/scripts/validation/noopen-coherence.py +++ b/scripts/validation/noopen-coherence.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Coherence around zero-message opens (AGENTFS_FUSE_NOOPEN / kernel no_open). +"""Coherence around zero-message opens (VFS_FUSE_NOOPEN / kernel no_open). With the kernel's `no_open` latched, open(2)/close(2) complete with no FUSE request, all file I/O arrives with `fh=0`, and FUSE_RELEASE is skipped for @@ -7,17 +7,17 @@ from a shared per-inode file table resolved on first I/O. This script hammers the seams of that model: - exec scenarios (pure AgentFS db): + exec scenarios (pure Vfs db): - write -> close -> immediately stat / scandir / hardlink-stat / re-read (race loop, absolute size asserts); - ftruncate through an fd (SETATTR arrives with fh=0); - O_TRUNC reopen (delivered as SETATTR size=0, never atomic); - mmap shared-write + msync; - fd kept open across an unlink (per-ino file must keep serving); - - a small AGENTFS_FUSE_INO_FILES_CAP config that forces soft-cap + - a small VFS_FUSE_INO_FILES_CAP config that forces soft-cap eviction + re-resolution mid-workload. - overlay scenario (agentfs run --session over a base tree): + overlay scenario (vfs run --session over a base tree): - read a base file (read-only resolution = host passthrough), append to it (write upgrade -> copy-up replaces the per-ino file), then re-read through fresh fds and verify base+append content and sizes. @@ -42,7 +42,7 @@ from typing import Any, Optional sys.path.insert(0, str(Path(__file__).resolve().parent)) -from lib.common import resolve_agentfs_bin, tail_text # noqa: E402 +from lib.common import resolve_vfs_bin, tail_text # noqa: E402 EXEC_WORKLOAD = r''' import ctypes @@ -177,7 +177,7 @@ def parse_workload_json(stdout: str) -> Optional[dict[str, Any]]: def parse_fuse_counters(output: str) -> Optional[dict[str, Any]]: for line in reversed(output.splitlines()): - if '"agentfs_profile_summary"' not in line: + if '"vfs_profile_summary"' not in line: continue start = line.find("{") if start < 0: @@ -238,14 +238,14 @@ def run_one( def cleanup_run_session_dir(session_id: str) -> None: """Remove exactly the session dir a run leg created. - Never sweeps ~/.agentfs/run wholesale (the user may have real sessions + Never sweeps ~/.vfs/run wholesale (the user may have real sessions there), and never raises: this runs in a finally block, so an exception here would mask the leg's real failure. If the leg died with its FUSE mount still live or wedged, stats under the mountpoint fail (ENOTCONN) and a bare rmtree would silently leave the dir behind — detach the mount first so the dir actually goes away. """ - session_dir = Path.home() / ".agentfs" / "run" / session_id + session_dir = Path.home() / ".vfs" / "run" / session_id mountpoint = session_dir / "mnt" try: os.stat(mountpoint) @@ -269,50 +269,50 @@ def cleanup_run_session_dir(session_id: str) -> None: def base_env(extra: dict[str, str]) -> dict[str, str]: env = os.environ.copy() - env["AGENTFS_PROFILE"] = "1" - env.pop("AGENTFS_FUSE_INO_FILES_CAP", None) + env["VFS_PROFILE"] = "1" + env.pop("VFS_FUSE_INO_FILES_CAP", None) env.update(extra) return env def main() -> int: parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--agentfs-bin", default=os.environ.get("AGENTFS_BIN")) + parser.add_argument("--vfs-bin", default=os.environ.get("VFS_BIN")) parser.add_argument("--iterations", type=int, default=60) parser.add_argument("--timeout", type=float, default=600.0) parser.add_argument("--output", default=None) args = parser.parse_args() repo_root = Path(__file__).resolve().parents[2] - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) exec_configs = [ - ("exec_noopen_off", {"AGENTFS_FUSE_NOOPEN": "0"}, False), - ("exec_noopen_on", {"AGENTFS_FUSE_NOOPEN": "1"}, True), + ("exec_noopen_off", {"VFS_FUSE_NOOPEN": "0"}, False), + ("exec_noopen_on", {"VFS_FUSE_NOOPEN": "1"}, True), ( "exec_noopen_ttl0", - {"AGENTFS_FUSE_NOOPEN": "1", "AGENTFS_FUSE_ENTRY_TTL_MS": "0"}, + {"VFS_FUSE_NOOPEN": "1", "VFS_FUSE_ENTRY_TTL_MS": "0"}, True, ), ( "exec_noopen_smallcap", - {"AGENTFS_FUSE_NOOPEN": "1", "AGENTFS_FUSE_INO_FILES_CAP": "16"}, + {"VFS_FUSE_NOOPEN": "1", "VFS_FUSE_INO_FILES_CAP": "16"}, True, ), ] overlay_configs = [ - ("overlay_noopen_off", {"AGENTFS_FUSE_NOOPEN": "0"}, False), - ("overlay_noopen_on", {"AGENTFS_FUSE_NOOPEN": "1"}, True), + ("overlay_noopen_off", {"VFS_FUSE_NOOPEN": "0"}, False), + ("overlay_noopen_on", {"VFS_FUSE_NOOPEN": "1"}, True), ] runs = [] - with tempfile.TemporaryDirectory(prefix="agentfs-noopen-coherence-") as tmp: + with tempfile.TemporaryDirectory(prefix="vfs-noopen-coherence-") as tmp: temp_root = Path(tmp) for label, extra, noopen in exec_configs: db = temp_root / f"{label}.db" db.touch() argv = [ - agentfs_bin, + vfs_bin, "exec", str(db), sys.executable, @@ -331,7 +331,7 @@ def main() -> int: (base_root / "base.txt").write_bytes(b"base-content\n") session_id = f"noopen-coh-{uuid.uuid4().hex[:8]}" argv = [ - agentfs_bin, + vfs_bin, "run", "--session", session_id, @@ -354,7 +354,7 @@ def main() -> int: report = { "schema_version": 1, - "agentfs_bin": agentfs_bin, + "vfs_bin": vfs_bin, "iterations": args.iterations, "noopen_resolutions_total": resolutions, "noopen_upgrades_total": upgrades, @@ -363,7 +363,7 @@ def main() -> int: } output = args.output or os.path.join( tempfile.gettempdir(), - f"agentfs-noopen-coherence-{time.strftime('%Y%m%d-%H%M%S')}.json", + f"vfs-noopen-coherence-{time.strftime('%Y%m%d-%H%M%S')}.json", ) Path(output).write_text(json.dumps(report, indent=2)) diff --git a/scripts/validation/partial-origin-no-real-write.py b/scripts/validation/partial-origin-no-real-write.py index 500532f7..d4e0e72c 100755 --- a/scripts/validation/partial-origin-no-real-write.py +++ b/scripts/validation/partial-origin-no-real-write.py @@ -85,7 +85,7 @@ def env_flag(name: str) -> bool: def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( description=( - "Run a partial-origin write through agentfs run and fail if sampled " + "Run a partial-origin write through vfs run and fail if sampled " "base-file bytes or stable metadata change." ), formatter_class=argparse.RawDescriptionHelpFormatter, @@ -97,8 +97,8 @@ def parse_args(argv: list[str]) -> argparse.Namespace: scripts/validation/partial-origin-no-real-write.py --file-size-mib 200 --timeout 180 Environment: - AGENTFS_BIN path/name of agentfs executable - AGENTFS_PROFILE set to 1 to collect AgentFS profile summaries + VFS_BIN path/name of vfs executable + VFS_PROFILE set to 1 to collect Vfs profile summaries """, ) parser.add_argument( @@ -119,9 +119,9 @@ def parse_args(argv: list[str]) -> argparse.Namespace: help="bytes to hash at each sampled range (default: 4096)", ) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -132,13 +132,13 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser.add_argument( "--session", default=None, - help="AgentFS run session id (default: generated unique id)", + help="Vfs run session id (default: generated unique id)", ) parser.add_argument( "--profile", action="store_true", - default=env_flag("AGENTFS_PROFILE"), - help="enable AGENTFS_PROFILE=1 for AgentFS invocation", + default=env_flag("VFS_PROFILE"), + help="enable VFS_PROFILE=1 for Vfs invocation", ) parser.add_argument( "--keep-temp", @@ -176,13 +176,13 @@ def extract_profile_summaries(stderr: Any) -> list[dict[str, Any]]: summaries: list[dict[str, Any]] = [] for line in text.splitlines(): line = line.strip() - if not line or "agentfs_profile_summary" not in line: + if not line or "vfs_profile_summary" not in line: continue try: value = json.loads(line) except json.JSONDecodeError: continue - if isinstance(value, dict) and value.get("event") == "agentfs_profile_summary": + if isinstance(value, dict) and value.get("event") == "vfs_profile_summary": summaries.append(value) return summaries @@ -270,20 +270,20 @@ def parse_json_stdout(run: dict[str, Any]) -> Optional[dict[str, Any]]: return None -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate_path = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate_path = Path(vfs_bin).expanduser() if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") for candidate_path in ( - repo_root / "cli" / "target" / "debug" / "agentfs", - repo_root / "cli" / "target" / "release" / "agentfs", + repo_root / "cli" / "target" / "debug" / "vfs", + repo_root / "cli" / "target" / "release" / "vfs", ): if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path) @@ -297,12 +297,12 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN to an explicit binary\n" + "failed to build repo-local vfs binary; set VFS_BIN to an explicit binary\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = repo_root / "cli" / "target" / "debug" / "agentfs" + built = repo_root / "cli" / "target" / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) @@ -329,7 +329,7 @@ def create_large_file(path: Path, size_bytes: int) -> str: block_index = 0 with path.open("wb") as handle: while written < size_bytes: - seed = hashlib.sha256(f"agentfs-phase6-no-real-write-{block_index}".encode()).digest() + seed = hashlib.sha256(f"vfs-phase6-no-real-write-{block_index}".encode()).digest() block = (seed * ((ONE_MIB // len(seed)) + 1))[: min(ONE_MIB, size_bytes - written)] handle.write(block) digest.update(block) @@ -435,7 +435,7 @@ def prepare_environment(temp_root: Path, profile: bool) -> dict[str, str]: env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") if profile: - env["AGENTFS_PROFILE"] = "1" + env["VFS_PROFILE"] = "1" home = temp_root / "home" for path in (home, home / ".config", home / ".cache", home / ".local" / "share"): @@ -463,15 +463,15 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-no-real-write-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-no-real-write-")) else: - temp_manager = tempfile.TemporaryDirectory(prefix="agentfs-no-real-write-") + temp_manager = tempfile.TemporaryDirectory(prefix="vfs-no-real-write-") temp_root = Path(temp_manager.name) exit_code = 0 result: dict[str, Any] try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) env = prepare_environment(temp_root, args.profile) session = args.session or f"no-real-write-{uuid.uuid4()}" source_root = temp_root / "base" @@ -480,7 +480,7 @@ def main(argv: list[str]) -> int: before_sample = sample_base(base_file, args.sample_bytes, offset) command = [ - agentfs_bin, + vfs_bin, "run", "--session", session, @@ -497,13 +497,13 @@ def main(argv: list[str]) -> int: run = run_subprocess(command, source_root, env, args.timeout) after_sample = sample_base(base_file, args.sample_bytes, offset) workload_json = parse_json_stdout(run) - db_path = Path(env["HOME"]) / ".agentfs" / "run" / session / "delta.db" + db_path = Path(env["HOME"]) / ".vfs" / "run" / session / "delta.db" db_inspect = inspect_db(db_path) base_sample_unchanged = before_sample["samples"] == after_sample["samples"] base_metadata_unchanged = before_sample["stable_stat"] == after_sample["stable_stat"] correctness = { - "agentfs_returncode_zero": run["returncode"] == 0, + "vfs_returncode_zero": run["returncode"] == 0, "workload_json_present": workload_json is not None, "base_sample_unchanged": base_sample_unchanged, "base_metadata_unchanged": base_metadata_unchanged, @@ -531,8 +531,8 @@ def main(argv: list[str]) -> int: "edit_width_bytes": 1, "sample_bytes": args.sample_bytes, }, - "agentfs": { - "bin": agentfs_bin, + "vfs": { + "bin": vfs_bin, "session": session, "db_path": str(db_path), "profile_enabled": args.profile, @@ -550,7 +550,7 @@ def main(argv: list[str]) -> int: "before_sample": before_sample, "after_sample": after_sample, }, - "agentfs_overlay": { + "vfs_overlay": { "duration_seconds": run["duration_seconds"], "run": run, "result": workload_json, diff --git a/scripts/validation/phase7-validation.py b/scripts/validation/phase7-validation.py index a0e75f42..e1f90964 100755 --- a/scripts/validation/phase7-validation.py +++ b/scripts/validation/phase7-validation.py @@ -73,9 +73,9 @@ def parse_args(argv: list[str]) -> argparse.Namespace: """, ) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -225,20 +225,20 @@ def run_subprocess( return result -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate_path = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate_path = Path(vfs_bin).expanduser() if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") for candidate_path in ( - repo_root / "cli" / "target" / "debug" / "agentfs", - repo_root / "cli" / "target" / "release" / "agentfs", + repo_root / "cli" / "target" / "debug" / "vfs", + repo_root / "cli" / "target" / "release" / "vfs", ): if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path) @@ -252,12 +252,12 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN to an explicit binary\n" + "failed to build repo-local vfs binary; set VFS_BIN to an explicit binary\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = repo_root / "cli" / "target" / "debug" / "agentfs" + built = repo_root / "cli" / "target" / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) @@ -279,7 +279,7 @@ def git_commit(repo_root: Path) -> Optional[str]: def default_output_path() -> Path: stamp = time.strftime("%Y%m%d-%H%M%S") - return Path(tempfile.gettempdir()) / f"agentfs-phase7-validation-{stamp}-{uuid.uuid4().hex[:8]}.json" + return Path(tempfile.gettempdir()) / f"vfs-phase7-validation-{stamp}-{uuid.uuid4().hex[:8]}.json" def load_json(path: Path) -> Any: @@ -377,11 +377,11 @@ def inspect_db(db_path: Path) -> dict[str, Any]: return {"inspectable": False, "reason": str(exc), "path": str(db_path)} -def child_env(agentfs_bin: str, output_dir: Path) -> dict[str, str]: +def child_env(vfs_bin: str, output_dir: Path) -> dict[str, str]: env = os.environ.copy() env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") - env["AGENTFS_BIN"] = agentfs_bin + env["VFS_BIN"] = vfs_bin child_tmp = output_dir / "child-tmp" child_tmp.mkdir(parents=True, exist_ok=True) env["TMPDIR"] = str(child_tmp) @@ -542,7 +542,7 @@ def run_git_workload( repo_root: Path, env: dict[str, str], output_dir: Path, - agentfs_bin: str, + vfs_bin: str, ) -> dict[str, Any]: script = ( Path(args.git_workload_script).expanduser() @@ -561,7 +561,7 @@ def run_git_workload( output_path = output_dir / "git-workload-benchmark.json" argv = [sys.executable, str(script)] optional_args = [ - ("--agentfs-bin", agentfs_bin), + ("--vfs-bin", vfs_bin), ("--timeout", str(args.timeout)), ("--profile", None), ("--strict-portable", None), @@ -590,7 +590,7 @@ def run_git_workload( correctness_ok = False base_unchanged_values = [ - value for value in recursive_key_values(payload, "agentfs_base_unchanged") if isinstance(value, bool) + value for value in recursive_key_values(payload, "vfs_base_unchanged") if isinstance(value, bool) ] base_unchanged = all(base_unchanged_values) if base_unchanged_values else None @@ -689,18 +689,18 @@ def run_strict_large_edit( inspect = payload.get("database", {}).get("inspect_after", {}) portability = inspect.get("portability_status") or portability_status(inspect) if isinstance(inspect, dict) else {} native_seconds = payload.get("native", {}).get("duration_seconds") - agentfs_seconds = payload.get("agentfs_overlay", {}).get("duration_seconds") + vfs_seconds = payload.get("vfs_overlay", {}).get("duration_seconds") ratio_value = ( - float(agentfs_seconds) / float(native_seconds) + float(vfs_seconds) / float(native_seconds) if isinstance(native_seconds, (int, float)) - and isinstance(agentfs_seconds, (int, float)) + and isinstance(vfs_seconds, (int, float)) and float(native_seconds) > 0 else None ) gate = { "correctness_passed": correctness.get("passed") is True, - "base_unchanged": correctness.get("agentfs_base_unchanged") is True, - "partial_origin_enabled": payload.get("agentfs", {}).get("partial_origin_enabled"), + "base_unchanged": correctness.get("vfs_base_unchanged") is True, + "partial_origin_enabled": payload.get("vfs", {}).get("partial_origin_enabled"), "partial_origin_rows": int(portability.get("partial_origin_rows", 0) or 0), "portable": portability.get("portable"), "ratio": ratio_value, @@ -719,7 +719,7 @@ def run_strict_large_edit( def strict_db_path(record: dict[str, Any]) -> Optional[Path]: payload = record.get("result") if isinstance(payload, dict): - raw = payload.get("agentfs", {}).get("db_path") + raw = payload.get("vfs", {}).get("db_path") if isinstance(raw, str): return Path(raw) return None @@ -730,7 +730,7 @@ def run_strict_partial_rows_check(record: dict[str, Any], args: argparse.Namespa if db_path is None: return skipped_gate( "strict_no_partial_origin_rows", - "strict portable benchmark did not produce an AgentFS database path", + "strict portable benchmark did not produce a Vfs database path", gate_required(args), ) inspect = inspect_db(db_path) @@ -751,11 +751,11 @@ def run_integrity( args: argparse.Namespace, repo_root: Path, env: dict[str, str], - agentfs_bin: str, + vfs_bin: str, ) -> dict[str, Any]: if db_path is None: return skipped_gate(name, "no database path available", gate_required(args)) - argv = [agentfs_bin, "integrity", str(db_path), "--json", "--require-portable"] + argv = [vfs_bin, "integrity", str(db_path), "--json", "--require-portable"] run = run_subprocess(argv, repo_root, env, args.timeout, keep_stdout=True) report = parse_json_text(str(run.get("stdout", ""))) ok = run["returncode"] == 0 and isinstance(report, dict) and report.get("ok") is True @@ -802,13 +802,13 @@ def run_backup( args: argparse.Namespace, repo_root: Path, env: dict[str, str], - agentfs_bin: str, + vfs_bin: str, *, materialize: bool, ) -> dict[str, Any]: if source_db is None: return skipped_gate(name, "no source database path available", gate_required(args)) - argv = [agentfs_bin, "backup", str(source_db), str(target_db), "--verify"] + argv = [vfs_bin, "backup", str(source_db), str(target_db), "--verify"] if materialize: argv.append("--materialize") run = run_subprocess(argv, repo_root, env, args.timeout * 2 + 30) @@ -937,7 +937,7 @@ def run_base_read( payload.get("runs", {}) .get("cache_invalidation", {}) .get("base_file", {}) - .get("agentfs_base_unchanged") + .get("vfs_base_unchanged") ), } if ( @@ -997,13 +997,13 @@ def run_partial_setup( correctness = payload.get("correctness", {}) record["gate"] = { "correctness_passed": correctness.get("passed") is True, - "partial_origin_enabled": payload.get("agentfs", {}).get("partial_origin_enabled"), + "partial_origin_enabled": payload.get("vfs", {}).get("partial_origin_enabled"), "partial_origin_rows": partial_rows, "origin_backed": portability.get("origin_backed"), } if record["status"] == "passed" and ( correctness.get("passed") is not True - or payload.get("agentfs", {}).get("partial_origin_enabled") is not True + or payload.get("vfs", {}).get("partial_origin_enabled") is not True or partial_rows <= 0 ): record["status"] = "failed" @@ -1016,13 +1016,13 @@ def run_materialize( repo_root: Path, env: dict[str, str], output_dir: Path, - agentfs_bin: str, + vfs_bin: str, ) -> dict[str, Any]: source_db = strict_db_path(source_record) if source_db is None: return skipped_gate("materialize_verify", "partial-origin setup did not produce a database path", gate_required(args)) target_db = output_dir / "materialized.db" - argv = [agentfs_bin, "materialize", str(source_db), "--output", str(target_db), "--verify"] + argv = [vfs_bin, "materialize", str(source_db), "--output", str(target_db), "--verify"] run = run_subprocess(argv, repo_root, env, args.timeout * 2 + 30) sidecars = sidecar_status(target_db) inspect = inspect_db(target_db) @@ -1080,26 +1080,26 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - output_dir = Path(tempfile.mkdtemp(prefix="agentfs-phase7-validation-")) + output_dir = Path(tempfile.mkdtemp(prefix="vfs-phase7-validation-")) else: - temp_manager = tempfile.TemporaryDirectory(prefix="agentfs-phase7-validation-") + temp_manager = tempfile.TemporaryDirectory(prefix="vfs-phase7-validation-") output_dir = Path(temp_manager.name) exit_code = 0 result: dict[str, Any] try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) - env = child_env(agentfs_bin, output_dir) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) + env = child_env(vfs_bin, output_dir) runs: dict[str, dict[str, Any]] = {} - runs["git_workload_benchmark"] = run_git_workload(args, repo_root, env, output_dir, agentfs_bin) + runs["git_workload_benchmark"] = run_git_workload(args, repo_root, env, output_dir, vfs_bin) runs["strict_portable_large_edit"] = run_strict_large_edit(args, repo_root, env, output_dir) strict_db = strict_db_path(runs["strict_portable_large_edit"]) runs["strict_no_partial_origin_rows"] = run_strict_partial_rows_check( runs["strict_portable_large_edit"], args ) runs["strict_portable_integrity"] = run_integrity( - "strict_portable_integrity", strict_db, args, repo_root, env, agentfs_bin + "strict_portable_integrity", strict_db, args, repo_root, env, vfs_bin ) runs["strict_backup_verify"] = run_backup( "strict_backup_verify", @@ -1108,7 +1108,7 @@ def main(argv: list[str]) -> int: args, repo_root, env, - agentfs_bin, + vfs_bin, materialize=False, ) runs["partial_origin_no_real_write"] = run_no_real_write(args, repo_root, env, output_dir) @@ -1116,7 +1116,7 @@ def main(argv: list[str]) -> int: runs["partial_origin_materialize_setup"] = run_partial_setup(args, repo_root, env, output_dir) partial_db = strict_db_path(runs["partial_origin_materialize_setup"]) runs["materialize_verify"] = run_materialize( - runs["partial_origin_materialize_setup"], args, repo_root, env, output_dir, agentfs_bin + runs["partial_origin_materialize_setup"], args, repo_root, env, output_dir, vfs_bin ) runs["backup_materialize_verify"] = run_backup( "backup_materialize_verify", @@ -1125,7 +1125,7 @@ def main(argv: list[str]) -> int: args, repo_root, env, - agentfs_bin, + vfs_bin, materialize=True, ) @@ -1154,14 +1154,14 @@ def main(argv: list[str]) -> int: "materialize_file_size_mib": args.materialize_file_size_mib or (200 if args.full_gates else 1), "require_git_workload": bool(args.require_git_workload), }, - "agentfs": {"bin": agentfs_bin}, + "vfs": {"bin": vfs_bin}, "policy": { "full_mode_skipped_required_gates_fail": True, "git_workload_absent_policy": ( "skipped in smoke unless --require-git-workload is set; required in full" ), "strict_mode_forbids_partial_origin_rows": True, - "strict_portable_integrity_command": "agentfs integrity --json --require-portable", + "strict_portable_integrity_command": "vfs integrity --json --require-portable", "backup_outputs_must_not_depend_on_nonempty_wal_or_shm_sidecars": True, }, "summary": { diff --git a/scripts/validation/phase8-concurrent-git-stress.py b/scripts/validation/phase8-concurrent-git-stress.py index ee2c3201..18f86fbf 100755 --- a/scripts/validation/phase8-concurrent-git-stress.py +++ b/scripts/validation/phase8-concurrent-git-stress.py @@ -2,7 +2,7 @@ """Phase 8 concurrent Git status/diff stress gate. The gate builds a deterministic local Git fixture, runs the same concurrent -read-mostly Git workload natively and through AgentFS, and requires the hashed +read-mostly Git workload natively and through Vfs, and requires the hashed status/diff/log outputs to match exactly. """ @@ -63,7 +63,7 @@ def git_env(): def run_git(label, argv, cwd): # Honor the harness's pinned-git override: PATH shims are invisible inside - # the agentfs sandbox, so only an absolute system-path GIT avoids the + # the vfs sandbox, so only an absolute system-path GIT avoids the # daemonizing hook-manager shim (scripts/validation/lib/common.py). git = os.environ.get("GIT", "git") started = time.perf_counter() @@ -213,7 +213,7 @@ def env_flag(name: str) -> bool: def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( - description="Run concurrent git status/status/diff/log through native storage and AgentFS." + description="Run concurrent git status/status/diff/log through native storage and Vfs." ) parser.add_argument("--fixture-files", type=positive_int, default=48) parser.add_argument("--fixture-dirs", type=positive_int, default=6) @@ -221,9 +221,9 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser.add_argument("--edit-files", type=positive_int, default=4) parser.add_argument("--append-bytes", type=positive_int, default=128) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -333,20 +333,20 @@ def parse_json_stdout(run: dict[str, Any]) -> Optional[dict[str, Any]]: return None -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate = Path(vfs_bin).expanduser() if candidate.is_file() and os.access(candidate, os.X_OK): return str(candidate.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") for candidate in ( - repo_root / "cli" / "target" / "debug" / "agentfs", - repo_root / "cli" / "target" / "release" / "agentfs", + repo_root / "cli" / "target" / "debug" / "vfs", + repo_root / "cli" / "target" / "release" / "vfs", ): if candidate.is_file() and os.access(candidate, os.X_OK): return str(candidate) @@ -360,11 +360,11 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN explicitly\n" + "failed to build repo-local vfs binary; set VFS_BIN explicitly\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = repo_root / "cli" / "target" / "debug" / "agentfs" + built = repo_root / "cli" / "target" / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) raise RuntimeError(f"repo-local build completed but binary was not found: {built}") @@ -389,10 +389,10 @@ def git_env() -> dict[str, str]: env.setdefault("GIT_TERMINAL_PROMPT", "0") env.setdefault("NO_COLOR", "1") env.setdefault("LC_ALL", "C") - env["GIT_AUTHOR_NAME"] = "AgentFS Phase8" - env["GIT_AUTHOR_EMAIL"] = "agentfs-phase8@example.invalid" - env["GIT_COMMITTER_NAME"] = "AgentFS Phase8" - env["GIT_COMMITTER_EMAIL"] = "agentfs-phase8@example.invalid" + env["GIT_AUTHOR_NAME"] = "Vfs Phase8" + env["GIT_AUTHOR_EMAIL"] = "vfs-phase8@example.invalid" + env["GIT_COMMITTER_NAME"] = "Vfs Phase8" + env["GIT_COMMITTER_EMAIL"] = "vfs-phase8@example.invalid" return env @@ -425,9 +425,9 @@ def create_generated_repo(root: Path, file_count: int, dir_count: int, file_size env["GIT_COMMITTER_DATE"] = "2024-01-01T00:00:00Z" require_git_ok(run_git(["init"], root, env=env), "git init generated repo") require_git_ok(run_git(["checkout", "-B", "main"], root, env=env), "git checkout main") - require_git_ok(run_git(["config", "user.name", "AgentFS Phase8"], root, env=env), "git config user.name") + require_git_ok(run_git(["config", "user.name", "Vfs Phase8"], root, env=env), "git config user.name") require_git_ok( - run_git(["config", "user.email", "agentfs-phase8@example.invalid"], root, env=env), + run_git(["config", "user.email", "vfs-phase8@example.invalid"], root, env=env), "git config user.email", ) @@ -448,7 +448,7 @@ def create_generated_repo(root: Path, file_count: int, dir_count: int, file_size else: filename = f"blob_{index:05d}.txt" header = f"phase8 data fixture {index} PHASE8_TOKEN\n" - seed = hashlib.sha256(f"agentfs-phase8-concurrent-git-{index}".encode("utf-8")).hexdigest() + seed = hashlib.sha256(f"vfs-phase8-concurrent-git-{index}".encode("utf-8")).hexdigest() filler = "".join(f"{line:04d} {seed} PHASE8_TOKEN_{line % 7}\n" for line in range(128)) content = (header + filler)[:file_size] if not content.endswith("\n"): @@ -476,9 +476,9 @@ def prepare_environment(temp_root: Path, profile: bool) -> dict[str, str]: env.setdefault("GIT_CONFIG_NOSYSTEM", "1") env.setdefault("GIT_TERMINAL_PROMPT", "0") if profile: - env["AGENTFS_PROFILE"] = "1" + env["VFS_PROFILE"] = "1" else: - env.pop("AGENTFS_PROFILE", None) + env.pop("VFS_PROFILE", None) home = temp_root / "home" for path in (home, home / ".config", home / ".cache", home / ".local" / "share"): @@ -606,9 +606,9 @@ def db_artifacts(db_path: Path) -> dict[str, Any]: } -def run_integrity(agentfs_bin: str, db_path: Path, cwd: Path, env: dict[str, str], timeout: float) -> dict[str, Any]: +def run_integrity(vfs_bin: str, db_path: Path, cwd: Path, env: dict[str, str], timeout: float) -> dict[str, Any]: run = run_subprocess( - [agentfs_bin, "integrity", str(db_path), "--json", "--require-portable"], + [vfs_bin, "integrity", str(db_path), "--json", "--require-portable"], cwd, env, timeout, @@ -635,7 +635,7 @@ def workload_argv(args: argparse.Namespace) -> list[str]: def default_output_path() -> Path: stamp = time.strftime("%Y%m%d-%H%M%S") - return Path(tempfile.gettempdir()) / f"agentfs-phase8-concurrent-git-{stamp}-{uuid.uuid4().hex[:8]}.json" + return Path(tempfile.gettempdir()) / f"vfs-phase8-concurrent-git-{stamp}-{uuid.uuid4().hex[:8]}.json" def main(argv: list[str]) -> int: @@ -645,10 +645,10 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-phase8-concurrent-git-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-phase8-concurrent-git-")) else: temp_manager = tempfile.TemporaryDirectory( - prefix="agentfs-phase8-concurrent-git-", + prefix="vfs-phase8-concurrent-git-", ignore_cleanup_errors=True, ) temp_root = Path(temp_manager.name) @@ -659,14 +659,14 @@ def main(argv: list[str]) -> int: common.pin_distro_git(os.environ, temp_root) if shutil.which(os.environ.get("GIT", "git")) is None: raise RuntimeError("git executable is required") - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) env = prepare_environment(temp_root, args.profile) session = args.session or f"phase8-concurrent-git-{uuid.uuid4().hex}" - db_path = Path(env["HOME"]) / ".agentfs" / "run" / session / "delta.db" + db_path = Path(env["HOME"]) / ".vfs" / "run" / session / "delta.db" source_root = temp_root / "source" native_root = temp_root / "native" - agentfs_base_root = temp_root / "agentfs-base" + vfs_base_root = temp_root / "vfs-base" create_generated_repo( source_root, args.fixture_files, @@ -674,38 +674,38 @@ def main(argv: list[str]) -> int: args.fixture_file_size_bytes, ) shutil.copytree(source_root, native_root, symlinks=True) - shutil.copytree(source_root, agentfs_base_root, symlinks=True) + shutil.copytree(source_root, vfs_base_root, symlinks=True) - base_before = tree_hash(agentfs_base_root) + base_before = tree_hash(vfs_base_root) workload = workload_argv(args) native_run = run_subprocess(workload, native_root, env, args.timeout) - agentfs_run = run_subprocess( - [agentfs_bin, "run", "--session", session, "--no-default-allows", "--"] + workload, - agentfs_base_root, + vfs_run = run_subprocess( + [vfs_bin, "run", "--session", session, "--no-default-allows", "--"] + workload, + vfs_base_root, env, args.timeout, ) - base_after = tree_hash(agentfs_base_root) + base_after = tree_hash(vfs_base_root) native_payload = parse_json_stdout(native_run) - agentfs_payload = parse_json_stdout(agentfs_run) + vfs_payload = parse_json_stdout(vfs_run) digest_equal = ( isinstance(native_payload, dict) - and isinstance(agentfs_payload, dict) - and native_payload.get("digest") == agentfs_payload.get("digest") + and isinstance(vfs_payload, dict) + and native_payload.get("digest") == vfs_payload.get("digest") ) zero_exits = ( native_run["returncode"] == 0 - and agentfs_run["returncode"] == 0 + and vfs_run["returncode"] == 0 and isinstance(native_payload, dict) - and isinstance(agentfs_payload, dict) + and isinstance(vfs_payload, dict) and native_payload.get("zero_exits") is True - and agentfs_payload.get("zero_exits") is True + and vfs_payload.get("zero_exits") is True ) base_unchanged = base_before["sha256"] == base_after["sha256"] db_after = db_artifacts(db_path) db_inspect = inspect_db(db_path) - integrity = run_integrity(agentfs_bin, db_path, temp_root, env, args.timeout) if db_path.exists() else { + integrity = run_integrity(vfs_bin, db_path, temp_root, env, args.timeout) if db_path.exists() else { "run": None, "result": None, "ok": False, @@ -730,8 +730,8 @@ def main(argv: list[str]) -> int: "command": { "argv": [str(Path(__file__).resolve())] + argv, "workload_argv": workload, - "agentfs_prefix": [ - agentfs_bin, + "vfs_prefix": [ + vfs_bin, "run", "--session", session, @@ -747,8 +747,8 @@ def main(argv: list[str]) -> int: "append_bytes": args.append_bytes, "timeout_seconds": args.timeout, }, - "agentfs": { - "bin": agentfs_bin, + "vfs": { + "bin": vfs_bin, "session": session, "db_path": str(db_path), "profile_enabled": args.profile, @@ -758,13 +758,13 @@ def main(argv: list[str]) -> int: "zero_exits": zero_exits, "digest_equal": digest_equal, "native_digest": native_payload.get("digest") if isinstance(native_payload, dict) else None, - "agentfs_digest": agentfs_payload.get("digest") if isinstance(agentfs_payload, dict) else None, + "vfs_digest": vfs_payload.get("digest") if isinstance(vfs_payload, dict) else None, "base_unchanged": base_unchanged, "strict_no_sidecar_files": db_after.get("strict_no_sidecar_files"), "integrity_ok": integrity.get("ok"), }, "native": {"run": native_run, "workload": native_payload}, - "agentfs_overlay": {"run": agentfs_run, "workload": agentfs_payload}, + "vfs_overlay": {"run": vfs_run, "workload": vfs_payload}, "base_tree": {"before": base_before, "after": base_after, "unchanged": base_unchanged}, "database": {"after": db_after, "inspect_after": db_inspect, "integrity": integrity}, "temp_dir": str(temp_root), diff --git a/scripts/validation/phase8-validation.py b/scripts/validation/phase8-validation.py index b03f3996..ed292e97 100755 --- a/scripts/validation/phase8-validation.py +++ b/scripts/validation/phase8-validation.py @@ -81,9 +81,9 @@ def parse_args(argv: list[str]) -> argparse.Namespace: mode.add_argument("--smoke", action="store_true", help="run smoke-sized gates without enforcing Phase 8 perf/parallel targets") mode.add_argument("--full", action="store_true", help="run full Phase 8 policy (default)") parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -99,7 +99,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: def default_output_path() -> Path: stamp = time.strftime("%Y%m%d-%H%M%S") - return Path(tempfile.gettempdir()) / f"agentfs-phase8-validation-{stamp}-{uuid.uuid4().hex[:8]}.json" + return Path(tempfile.gettempdir()) / f"vfs-phase8-validation-{stamp}-{uuid.uuid4().hex[:8]}.json" def load_json(path: Path) -> Optional[dict[str, Any]]: @@ -121,11 +121,11 @@ def tool_path(name: str) -> Optional[str]: return found -def child_env(agentfs_bin: str, output_dir: Path) -> dict[str, str]: +def child_env(vfs_bin: str, output_dir: Path) -> dict[str, str]: env = os.environ.copy() env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") - env["AGENTFS_BIN"] = agentfs_bin + env["VFS_BIN"] = vfs_bin child_tmp = output_dir / "tmp" child_tmp.mkdir(parents=True, exist_ok=True) env["TMPDIR"] = str(child_tmp) @@ -231,8 +231,8 @@ def max_counter(payload: Optional[dict[str, Any]], key: str) -> Optional[int]: return None candidates: list[Any] = [] candidates.append(payload.get("summary", {}).get(key)) - candidates.append(payload.get("agentfs", {}).get("profile_counters", {}).get(key)) - candidates.append(payload.get("agentfs", {}).get("profile_counters", {}).get("max_counters", {}).get(key)) + candidates.append(payload.get("vfs", {}).get("profile_counters", {}).get(key)) + candidates.append(payload.get("vfs", {}).get("profile_counters", {}).get("max_counters", {}).get(key)) for item in candidates: if isinstance(item, int): return item @@ -316,10 +316,10 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - output_dir = Path(tempfile.mkdtemp(prefix="agentfs-phase8-validation-")) + output_dir = Path(tempfile.mkdtemp(prefix="vfs-phase8-validation-")) else: temp_manager = tempfile.TemporaryDirectory( - prefix="agentfs-phase8-validation-", + prefix="vfs-phase8-validation-", ignore_cleanup_errors=True, ) output_dir = Path(temp_manager.name) @@ -329,22 +329,22 @@ def main(argv: list[str]) -> int: try: git_ai_before = common.git_ai_processes() pinned_git = common.pin_distro_git(os.environ, output_dir) - agentfs_bin = common.resolve_agentfs_bin(args.agentfs_bin, repo_root) - env = child_env(agentfs_bin, output_dir) + vfs_bin = common.resolve_vfs_bin(args.vfs_bin, repo_root) + env = child_env(vfs_bin, output_dir) scripts = repo_root / "scripts" / "validation" gates: dict[str, dict[str, Any]] = {} gates["phase7_validation_smoke"] = run_json_gate( "phase7-validation-smoke", scripts / "phase7-validation.py", - ["--smoke", "--timeout", str(args.timeout), "--agentfs-bin", agentfs_bin], + ["--smoke", "--timeout", str(args.timeout), "--vfs-bin", vfs_bin], repo_root, env, args.timeout * 6 + 120, output_dir, ) - git_args = ["--timeout", str(args.timeout), "--agentfs-bin", agentfs_bin, "--profile"] + git_args = ["--timeout", str(args.timeout), "--vfs-bin", vfs_bin, "--profile"] if args.smoke: git_args.extend( [ @@ -380,7 +380,7 @@ def main(argv: list[str]) -> int: enforce=enforce_phase8, ) - fuse_args = ["--timeout", str(args.timeout), "--agentfs-bin", agentfs_bin, "--profile"] + fuse_args = ["--timeout", str(args.timeout), "--vfs-bin", vfs_bin, "--profile"] if args.smoke: fuse_args.extend(["--files", "4", "--file-size-bytes", "1024", "--threads", "2", "--iterations", "4", "--read-bytes", "512"]) gates["fuse_serialization_parallelism"] = run_json_gate( @@ -394,7 +394,7 @@ def main(argv: list[str]) -> int: ) apply_parallel_policy(gates["fuse_serialization_parallelism"], enforce=enforce_phase8) - concurrent_args = ["--timeout", str(args.timeout), "--agentfs-bin", agentfs_bin] + concurrent_args = ["--timeout", str(args.timeout), "--vfs-bin", vfs_bin] if args.smoke: concurrent_args.extend(["--fixture-files", "12", "--fixture-dirs", "3", "--fixture-file-size-bytes", "512", "--edit-files", "2", "--append-bytes", "32"]) gates["phase8_concurrent_git_stress"] = run_json_gate( @@ -407,8 +407,8 @@ def main(argv: list[str]) -> int: output_dir, ) - durability_args = ["--timeout", str(args.timeout), "--agentfs-bin", agentfs_bin] - no_fsync_args = ["--timeout", str(args.timeout), "--agentfs-bin", agentfs_bin] + durability_args = ["--timeout", str(args.timeout), "--vfs-bin", vfs_bin] + no_fsync_args = ["--timeout", str(args.timeout), "--vfs-bin", vfs_bin] if args.smoke: durability_args.extend(["--write-bytes", "1024"]) no_fsync_args.extend(["--write-bytes", "1024"]) @@ -431,7 +431,7 @@ def main(argv: list[str]) -> int: output_dir, ) - base_read_args = ["--timeout", str(args.timeout), "--agentfs-bin", agentfs_bin, "--profile"] + base_read_args = ["--timeout", str(args.timeout), "--vfs-bin", vfs_bin, "--profile"] if args.smoke: base_read_args.extend(["--file-size-bytes", "65536", "--iterations", "4", "--read-bytes", "4096"]) else: @@ -459,7 +459,7 @@ def main(argv: list[str]) -> int: gates["noopen_coherence"] = run_json_gate( "noopen-coherence", scripts / "noopen-coherence.py", - ["--agentfs-bin", agentfs_bin], + ["--vfs-bin", vfs_bin], repo_root, env, coherence_wall_timeout, @@ -468,7 +468,7 @@ def main(argv: list[str]) -> int: gates["flush_coherence"] = run_json_gate( "flush-coherence", scripts / "flush-coherence.py", - ["--agentfs-bin", agentfs_bin], + ["--vfs-bin", vfs_bin], repo_root, env, coherence_wall_timeout, @@ -477,7 +477,7 @@ def main(argv: list[str]) -> int: gates["external_base_mutation_coherence"] = run_json_gate( "external-base-mutation-coherence", scripts / "external-base-mutation-coherence.py", - ["--agentfs-bin", agentfs_bin], + ["--vfs-bin", vfs_bin], repo_root, env, coherence_wall_timeout, @@ -532,7 +532,7 @@ def main(argv: list[str]) -> int: "gates": gates, "env": { "python": sys.executable, - "agentfs_bin": agentfs_bin, + "vfs_bin": vfs_bin, "git": tool_path("git"), "fusermount3": tool_path("fusermount3"), "fusermount": tool_path("fusermount"), diff --git a/scripts/validation/phase8-writeback-durability.py b/scripts/validation/phase8-writeback-durability.py index 78769073..26312109 100755 --- a/scripts/validation/phase8-writeback-durability.py +++ b/scripts/validation/phase8-writeback-durability.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Phase 8 writeback durability crash/reopen gate. -Writes bytes through a fresh AgentFS FUSE mount, fsyncs the file and parent +Writes bytes through a fresh Vfs FUSE mount, fsyncs the file and parent directory, SIGKILLs the mount process, remounts the same DB, and requires the bytes to be present with portable integrity and an unchanged base tree. """ @@ -30,7 +30,7 @@ parse_json_stdout, positive_float, positive_int, - resolve_agentfs_bin, + resolve_vfs_bin, run_subprocess, tail_text, terminate_process_tree, @@ -41,13 +41,13 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( - description="Verify fsynced AgentFS writes survive mount SIGKILL and remount." + description="Verify fsynced Vfs writes survive mount SIGKILL and remount." ) parser.add_argument("--write-bytes", type=positive_int, default=8192) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -84,7 +84,7 @@ def deterministic_bytes(length: int) -> bytes: out = bytearray() index = 0 while len(out) < length: - out.extend(hashlib.sha256(f"agentfs-phase8-durable-{index}".encode()).digest()) + out.extend(hashlib.sha256(f"vfs-phase8-durable-{index}".encode()).digest()) index += 1 return bytes(out[:length]) @@ -188,13 +188,13 @@ def unmount(mountpoint: Path) -> list[dict[str, Any]]: return attempts -def start_mount(agentfs_bin: str, id_or_path: Any, mountpoint: Path, env: dict[str, str], timeout: float) -> tuple[subprocess.Popen[str], dict[str, Any]]: +def start_mount(vfs_bin: str, id_or_path: Any, mountpoint: Path, env: dict[str, str], timeout: float) -> tuple[subprocess.Popen[str], dict[str, Any]]: try: mountpoint.mkdir(parents=True, exist_ok=True) except FileExistsError: pass argv = [ - agentfs_bin, + vfs_bin, "mount", str(id_or_path), str(mountpoint), @@ -302,9 +302,9 @@ def sidecar_status(db_path: Path) -> dict[str, Any]: } -def run_integrity(agentfs_bin: str, db_path: Path, cwd: Path, env: dict[str, str], timeout: float) -> dict[str, Any]: +def run_integrity(vfs_bin: str, db_path: Path, cwd: Path, env: dict[str, str], timeout: float) -> dict[str, Any]: run = run_subprocess( - [agentfs_bin, "integrity", str(db_path), "--json", "--require-portable"], + [vfs_bin, "integrity", str(db_path), "--json", "--require-portable"], cwd, env, timeout, @@ -327,7 +327,7 @@ def fsync_directory(path: Path) -> None: def default_output_path() -> Path: stamp = time.strftime("%Y%m%d-%H%M%S") - return Path(tempfile.gettempdir()) / f"agentfs-phase8-writeback-durability-{stamp}-{uuid.uuid4().hex[:8]}.json" + return Path(tempfile.gettempdir()) / f"vfs-phase8-writeback-durability-{stamp}-{uuid.uuid4().hex[:8]}.json" def main(argv: list[str]) -> int: @@ -337,10 +337,10 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-phase8-writeback-durable-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-phase8-writeback-durable-")) else: temp_manager = tempfile.TemporaryDirectory( - prefix="agentfs-phase8-writeback-durable-", + prefix="vfs-phase8-writeback-durable-", ignore_cleanup_errors=True, ) temp_root = Path(temp_manager.name) @@ -351,26 +351,26 @@ def main(argv: list[str]) -> int: exit_code = 0 result: dict[str, Any] try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) env = prepare_environment(temp_root) session = args.session or f"phase8-durable-{uuid.uuid4().hex}" base_root = temp_root / "base" create_base_fixture(base_root) base_before = tree_hash(base_root) - db_path = temp_root / ".agentfs" / f"{session}.db" + db_path = temp_root / ".vfs" / f"{session}.db" init_run = run_subprocess( - [agentfs_bin, "init", "--force", "--base", str(base_root), session], + [vfs_bin, "init", "--force", "--base", str(base_root), session], temp_root, env, args.timeout, ) if init_run["returncode"] != 0: - raise RuntimeError(f"agentfs init failed: {init_run['stderr_tail']}") + raise RuntimeError(f"vfs init failed: {init_run['stderr_tail']}") mountpoint = temp_root / "mnt" mountpoint.mkdir(parents=True, exist_ok=True) - mount_proc, mount_start = start_mount(agentfs_bin, session, mountpoint, env, args.timeout) + mount_proc, mount_start = start_mount(vfs_bin, session, mountpoint, env, args.timeout) expected = deterministic_bytes(args.write_bytes) write_path = mountpoint / "durable.bin" started_write = time.perf_counter() @@ -390,7 +390,7 @@ def main(argv: list[str]) -> int: kill_record = kill_mount(mount_proc, mountpoint) mount_proc = None - remount_proc, remount_start = start_mount(agentfs_bin, session, mountpoint, env, args.timeout) + remount_proc, remount_start = start_mount(vfs_bin, session, mountpoint, env, args.timeout) read_error = None read_bytes = b"" try: @@ -407,7 +407,7 @@ def main(argv: list[str]) -> int: clean_unmount = stop_mount_clean(remount_proc, mountpoint) remount_proc = None - integrity = run_integrity(agentfs_bin, db_path, temp_root, env, args.timeout) + integrity = run_integrity(vfs_bin, db_path, temp_root, env, args.timeout) db_inspect = inspect_db(db_path) sidecars = sidecar_status(db_path) base_after = tree_hash(base_root) @@ -432,7 +432,7 @@ def main(argv: list[str]) -> int: "benchmark": "phase8-writeback-durability", "git_commit": git_commit(repo_root), "parameters": {"write_bytes": args.write_bytes, "timeout_seconds": args.timeout}, - "agentfs": {"bin": agentfs_bin, "session": session, "db_path": str(db_path)}, + "vfs": {"bin": vfs_bin, "session": session, "db_path": str(db_path)}, "summary": { "passed": passed, "bytes_present_after_remount": remount_read["matches_expected"], diff --git a/scripts/validation/phase8-writeback-no-fsync-crash.py b/scripts/validation/phase8-writeback-no-fsync-crash.py index a0350435..5e498d5f 100755 --- a/scripts/validation/phase8-writeback-no-fsync-crash.py +++ b/scripts/validation/phase8-writeback-no-fsync-crash.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Phase 8 no-fsync writeback crash consistency gate. -Writes bytes through AgentFS without fsync, SIGKILLs the mount while the file is +Writes bytes through Vfs without fsync, SIGKILLs the mount while the file is still open, remounts the same DB, and requires portable integrity plus an unchanged base tree. The written data may be absent or a prefix of the payload, but arbitrary corrupt bytes fail the gate. @@ -38,13 +38,13 @@ def load_common() -> Any: def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( - description="Verify no-fsync AgentFS crash leaves a remountable, portable, base-preserving DB." + description="Verify no-fsync Vfs crash leaves a remountable, portable, base-preserving DB." ) parser.add_argument("--write-bytes", type=common.positive_int, default=8192) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -60,7 +60,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: def default_output_path() -> Path: stamp = time.strftime("%Y%m%d-%H%M%S") - return Path(tempfile.gettempdir()) / f"agentfs-phase8-writeback-no-fsync-{stamp}-{uuid.uuid4().hex[:8]}.json" + return Path(tempfile.gettempdir()) / f"vfs-phase8-writeback-no-fsync-{stamp}-{uuid.uuid4().hex[:8]}.json" def classify_remount_read(read_bytes: bytes, expected: bytes, read_error: Optional[str], error_kind: Optional[str]) -> dict[str, Any]: @@ -109,10 +109,10 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-phase8-writeback-no-fsync-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-phase8-writeback-no-fsync-")) else: temp_manager = tempfile.TemporaryDirectory( - prefix="agentfs-phase8-writeback-no-fsync-", + prefix="vfs-phase8-writeback-no-fsync-", ignore_cleanup_errors=True, ) temp_root = Path(temp_manager.name) @@ -123,26 +123,26 @@ def main(argv: list[str]) -> int: exit_code = 0 result: dict[str, Any] try: - agentfs_bin = common.resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = common.resolve_vfs_bin(args.vfs_bin, repo_root) env = common.prepare_environment(temp_root) session = args.session or f"phase8-no-fsync-{uuid.uuid4().hex}" base_root = temp_root / "base" common.create_base_fixture(base_root) base_before = common.tree_hash(base_root) - db_path = temp_root / ".agentfs" / f"{session}.db" + db_path = temp_root / ".vfs" / f"{session}.db" init_run = common.run_subprocess( - [agentfs_bin, "init", "--force", "--base", str(base_root), session], + [vfs_bin, "init", "--force", "--base", str(base_root), session], temp_root, env, args.timeout, ) if init_run["returncode"] != 0: - raise RuntimeError(f"agentfs init failed: {init_run['stderr_tail']}") + raise RuntimeError(f"vfs init failed: {init_run['stderr_tail']}") mountpoint = temp_root / "mnt" mountpoint.mkdir(parents=True, exist_ok=True) - mount_proc, mount_start = common.start_mount(agentfs_bin, session, mountpoint, env, args.timeout) + mount_proc, mount_start = common.start_mount(vfs_bin, session, mountpoint, env, args.timeout) expected = common.deterministic_bytes(args.write_bytes) write_path = mountpoint / "no_fsync_crash.bin" started_write = time.perf_counter() @@ -186,7 +186,7 @@ def main(argv: list[str]) -> int: "fsync_called": False, } - remount_proc, remount_start = common.start_mount(agentfs_bin, session, mountpoint, env, args.timeout) + remount_proc, remount_start = common.start_mount(vfs_bin, session, mountpoint, env, args.timeout) read_error = None error_kind = None read_bytes = b"" @@ -199,7 +199,7 @@ def main(argv: list[str]) -> int: clean_unmount = common.stop_mount_clean(remount_proc, mountpoint) remount_proc = None - integrity = common.run_integrity(agentfs_bin, db_path, temp_root, env, args.timeout) + integrity = common.run_integrity(vfs_bin, db_path, temp_root, env, args.timeout) db_inspect = common.inspect_db(db_path) sidecars = common.sidecar_status(db_path) base_after = common.tree_hash(base_root) @@ -224,7 +224,7 @@ def main(argv: list[str]) -> int: "benchmark": "phase8-writeback-no-fsync-crash", "git_commit": common.git_commit(repo_root), "parameters": {"write_bytes": args.write_bytes, "timeout_seconds": args.timeout}, - "agentfs": {"bin": agentfs_bin, "session": session, "db_path": str(db_path)}, + "vfs": {"bin": vfs_bin, "session": session, "db_path": str(db_path)}, "summary": { "passed": passed, "data_state_after_remount": remount_read["state"], diff --git a/scripts/validation/posix/pjdfstest/known-gaps.tsv b/scripts/validation/posix/pjdfstest/known-gaps.tsv index 4fc00ffc..827a0ba7 100644 --- a/scripts/validation/posix/pjdfstest/known-gaps.tsv +++ b/scripts/validation/posix/pjdfstest/known-gaps.tsv @@ -1,6 +1,6 @@ # target reason -mknod/ Unprivileged FUSE runs cannot create block/char device nodes; these failures are environment/contract gaps until AgentFS defines privileged-node support. -chown/00.t Successful ownership mutation requires root/CAP_CHOWN or a stronger AgentFS privilege model; keep only error-path chown tests in supported profiles. +mknod/ Unprivileged FUSE runs cannot create block/char device nodes; these failures are environment/contract gaps until Vfs defines privileged-node support. +chown/00.t Successful ownership mutation requires root/CAP_CHOWN or a stronger Vfs privilege model; keep only error-path chown tests in supported profiles. chown/01.t Depends on block/char mknod cases and cascades into ENOENT when device nodes are rejected. chown/02.t Depends on successful uid/gid changes requiring root/CAP_CHOWN. chown/03.t Depends on successful uid/gid changes requiring root/CAP_CHOWN. diff --git a/scripts/validation/posix/pjdfstest/phase45-ci.txt b/scripts/validation/posix/pjdfstest/phase45-ci.txt index da1996cc..af005a1e 100644 --- a/scripts/validation/posix/pjdfstest/phase45-ci.txt +++ b/scripts/validation/posix/pjdfstest/phase45-ci.txt @@ -1,4 +1,4 @@ -# Conservative unprivileged AgentFS POSIX gate for Phase 4.5. +# Conservative unprivileged Vfs POSIX gate for Phase 4.5. # # This profile intentionally excludes tests requiring root-only capabilities # such as block/char device mknod, successful chown/lchown, and alternate diff --git a/scripts/validation/posix/pjdfstest/phase5-ci.txt b/scripts/validation/posix/pjdfstest/phase5-ci.txt index a06d34a6..06a09e61 100644 --- a/scripts/validation/posix/pjdfstest/phase5-ci.txt +++ b/scripts/validation/posix/pjdfstest/phase5-ci.txt @@ -1,4 +1,4 @@ -# Expanded unprivileged AgentFS POSIX gate for Phase 5. +# Expanded unprivileged Vfs POSIX gate for Phase 5. # # This profile keeps the Phase 4.5 regression floor and adds currently-passing # core path, FIFO, symlink-loop, sparse large-file, socket-open, and rename/rmdir diff --git a/scripts/validation/posix/run-pjdfstest.sh b/scripts/validation/posix/run-pjdfstest.sh index b80153a1..93a498be 100755 --- a/scripts/validation/posix/run-pjdfstest.sh +++ b/scripts/validation/posix/run-pjdfstest.sh @@ -1,15 +1,15 @@ #!/usr/bin/env bash # -# Run pjdfstest against an AgentFS FUSE mount. +# Run pjdfstest against a Vfs FUSE mount. # # Usage: -# run-pjdfstest.sh [--pjdfstest-dir DIR] [--agentfs-bin PATH] [--profile NAME] +# run-pjdfstest.sh [--pjdfstest-dir DIR] [--vfs-bin PATH] [--profile NAME] # [--manifest FILE] [--report-dir DIR] [--partial-origin] # [--no-partial-origin] [--keep-work] # # Environment: # PJDFSTEST_DIR pjdfstest checkout root or tests directory. -# AGENTFS_BIN agentfs executable to invoke (default: agentfs). +# VFS_BIN vfs executable to invoke (default: vfs). # PJDFSTEST_PROFILE test profile to run (default: full). # PJDFSTEST_MANIFEST explicit manifest overriding --profile. # REPORT_DIR directory where logs should be written. @@ -18,7 +18,7 @@ set -Eeuo pipefail SKIP_CODE="${SKIP_CODE:-77}" -AGENTFS_BIN="${AGENTFS_BIN:-agentfs}" +VFS_BIN="${VFS_BIN:-vfs}" PJDFSTEST_DIR="${PJDFSTEST_DIR:-}" PJDFSTEST_PROFILE="${PJDFSTEST_PROFILE:-full}" PJDFSTEST_MANIFEST="${PJDFSTEST_MANIFEST:-}" @@ -33,7 +33,7 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" WORK_DIR="" MOUNT_DIR="" MOUNT_PID="" -AGENTFS_RESOLVED="" +VFS_RESOLVED="" PJDFSTEST_RESOLVED="" PJDFSTEST_TESTS="" PJDFSTEST_RESOLVED_MANIFEST="" @@ -61,11 +61,11 @@ command -v prove command -v pjdfstest ``` -AgentFS harness command from docs/TESTING.md: +Vfs harness command from docs/TESTING.md: ```bash scripts/validation/posix/run-pjdfstest.sh \ - --agentfs-bin "$PWD/target/debug/agentfs" \ + --vfs-bin "$PWD/target/debug/vfs" \ --pjdfstest-dir /path/to/pjdfstest \ --profile phase45-ci ``` @@ -78,15 +78,15 @@ skip_missing() { exit "$SKIP_CODE" } -resolve_agentfs() { - if [[ "$AGENTFS_BIN" == */* ]]; then - [[ -x "$AGENTFS_BIN" ]] || return 1 +resolve_vfs() { + if [[ "$VFS_BIN" == */* ]]; then + [[ -x "$VFS_BIN" ]] || return 1 # Absolutize: the harness cd's into its work and mount dirs before # invoking the binary, which silently breaks a relative path (the # exit-127 error only lands in init.log). - AGENTFS_RESOLVED="$(cd "$(dirname "$AGENTFS_BIN")" && pwd)/$(basename "$AGENTFS_BIN")" + VFS_RESOLVED="$(cd "$(dirname "$VFS_BIN")" && pwd)/$(basename "$VFS_BIN")" else - AGENTFS_RESOLVED="$(command -v "$AGENTFS_BIN" 2>/dev/null)" || return 1 + VFS_RESOLVED="$(command -v "$VFS_BIN" 2>/dev/null)" || return 1 fi } @@ -229,7 +229,7 @@ safe_rm_tmp() { local path="$1" [[ -n "$path" ]] || return 0 case "$path" in - /tmp/agentfs-pjdfstest-work.*|/tmp/agentfs-pjdfstest-mnt.*) + /tmp/vfs-pjdfstest-work.*|/tmp/vfs-pjdfstest-mnt.*) rm -rf -- "$path" ;; *) @@ -284,9 +284,9 @@ while [[ $# -gt 0 ]]; do PJDFSTEST_DIR="$2" shift 2 ;; - --agentfs-bin) - [[ $# -ge 2 ]] || { echo "missing value for --agentfs-bin" >&2; exit 2; } - AGENTFS_BIN="$2" + --vfs-bin) + [[ $# -ge 2 ]] || { echo "missing value for --vfs-bin" >&2; exit 2; } + VFS_BIN="$2" shift 2 ;; --report-dir) @@ -339,7 +339,7 @@ done missing=() command -v prove >/dev/null 2>&1 || missing+=("prove (perl-Test-Harness)") -resolve_agentfs || missing+=("agentfs") +resolve_vfs || missing+=("vfs") resolve_pjdfstest_tests || missing+=("pjdfstest tests") resolve_pjdfstest_binary || missing+=("pjdfstest executable") @@ -362,20 +362,20 @@ fi resolve_prove_targets if [[ -z "$REPORT_DIR" ]]; then - REPORT_DIR="$(mktemp -d /tmp/agentfs-pjdfstest-report.XXXXXX)" + REPORT_DIR="$(mktemp -d /tmp/vfs-pjdfstest-report.XXXXXX)" else mkdir -p "$REPORT_DIR" REPORT_DIR="$(cd "$REPORT_DIR" && pwd)" fi -WORK_DIR="$(mktemp -d /tmp/agentfs-pjdfstest-work.XXXXXX)" -MOUNT_DIR="$(mktemp -d /tmp/agentfs-pjdfstest-mnt.XXXXXX)" +WORK_DIR="$(mktemp -d /tmp/vfs-pjdfstest-work.XXXXXX)" +MOUNT_DIR="$(mktemp -d /tmp/vfs-pjdfstest-mnt.XXXXXX)" trap cleanup EXIT INT TERM AGENT_ID="pjdfstest-$$-$(date +%s)" -DB_PATH="$WORK_DIR/.agentfs/$AGENT_ID.db" +DB_PATH="$WORK_DIR/.vfs/$AGENT_ID.db" -printf 'AgentFS binary: %s\n' "$AGENTFS_RESOLVED" +printf 'Vfs binary: %s\n' "$VFS_RESOLVED" printf 'pjdfstest binary: %s\n' "$PJDFSTEST_RESOLVED" printf 'pjdfstest tests: %s\n' "$PJDFSTEST_TESTS" printf 'pjdfstest profile: %s\n' "$PJDFSTEST_PROFILE" @@ -413,16 +413,16 @@ fi ( cd "$WORK_DIR" - "$AGENTFS_RESOLVED" init "$AGENT_ID" + "$VFS_RESOLVED" init "$AGENT_ID" ) >"$REPORT_DIR/init.log" 2>&1 if [[ ! -f "$DB_PATH" ]]; then - printf 'FAILED: expected AgentFS database was not created at %s\n' "$DB_PATH" >&2 + printf 'FAILED: expected Vfs database was not created at %s\n' "$DB_PATH" >&2 printf 'See %s/init.log\n' "$REPORT_DIR" >&2 exit 1 fi -MOUNT_CMD=("$AGENTFS_RESOLVED" mount "$DB_PATH" "$MOUNT_DIR" --foreground) +MOUNT_CMD=("$VFS_RESOLVED" mount "$DB_PATH" "$MOUNT_DIR" --foreground) if env_flag_enabled "$PARTIAL_ORIGIN"; then MOUNT_CMD+=(--partial-origin on) fi @@ -442,7 +442,7 @@ for _ in $(seq 1 100); do done if [[ "$mounted" -ne 1 ]]; then - printf 'FAILED: AgentFS mount did not become ready at %s\n' "$MOUNT_DIR" >&2 + printf 'FAILED: Vfs mount did not become ready at %s\n' "$MOUNT_DIR" >&2 printf 'See %s/mount.log\n' "$REPORT_DIR" >&2 exit 1 fi diff --git a/scripts/validation/read-path-benchmark.py b/scripts/validation/read-path-benchmark.py index 79a0649d..ad945976 100755 --- a/scripts/validation/read-path-benchmark.py +++ b/scripts/validation/read-path-benchmark.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Phase 5.5 native-vs-AgentFS read-path profiling benchmark.""" +"""Phase 5.5 native-vs-Vfs read-path profiling benchmark.""" from __future__ import annotations @@ -232,20 +232,20 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( description=( "Compare read-heavy filesystem operations on native storage and an " - "AgentFS overlay, with cold/warm and startup/steady-state timing splits." + "Vfs overlay, with cold/warm and startup/steady-state timing splits." ), formatter_class=argparse.RawDescriptionHelpFormatter, epilog="""Examples: # Fast smoke with profile summaries - AGENTFS_PROFILE=1 scripts/validation/read-path-benchmark.py --files 8 --dirs 3 \\ + VFS_PROFILE=1 scripts/validation/read-path-benchmark.py --files 8 --dirs 3 \\ --stat-iterations 1 --readdir-iterations 1 --open-iterations 1 --timeout 60 # Larger bounded read-path run scripts/validation/read-path-benchmark.py --files 256 --dirs 32 --file-size-bytes 8192 Environment: - AGENTFS_BIN path/name of agentfs executable - AGENTFS_PROFILE set to 1 to collect AgentFS profile summaries + VFS_BIN path/name of vfs executable + VFS_PROFILE set to 1 to collect Vfs profile summaries """, ) parser.add_argument("--files", type=positive_int, default=64, help="fixture file count") @@ -305,9 +305,9 @@ def parse_args(argv: list[str]) -> argparse.Namespace: help="comma-separated modes to run: cold,warm (default: cold,warm)", ) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--timeout", @@ -318,13 +318,13 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser.add_argument( "--profile", action="store_true", - default=env_flag("AGENTFS_PROFILE"), - help="enable AGENTFS_PROFILE=1 for AgentFS invocations", + default=env_flag("VFS_PROFILE"), + help="enable VFS_PROFILE=1 for Vfs invocations", ) parser.add_argument( "--session-prefix", default=None, - help="AgentFS run session prefix (default: generated unique prefix)", + help="Vfs run session prefix (default: generated unique prefix)", ) parser.add_argument( "--keep-temp", @@ -335,7 +335,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser.add_argument( "--output", default=None, - help="write JSON result to this file; defaults to /tmp/agentfs-read-path-benchmark-*.json", + help="write JSON result to this file; defaults to /tmp/vfs-read-path-benchmark-*.json", ) parser.add_argument( "--json-indent", @@ -369,13 +369,13 @@ def extract_profile_summaries(stderr: Any) -> list[dict[str, Any]]: summaries: list[dict[str, Any]] = [] for line in text.splitlines(): line = line.strip() - if not line or "agentfs_profile_summary" not in line: + if not line or "vfs_profile_summary" not in line: continue try: value = json.loads(line) except json.JSONDecodeError: continue - if isinstance(value, dict) and value.get("event") == "agentfs_profile_summary": + if isinstance(value, dict) and value.get("event") == "vfs_profile_summary": summaries.append(value) return summaries @@ -478,20 +478,20 @@ def parse_json_stdout(run: dict[str, Any]) -> Optional[dict[str, Any]]: return None -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate_path = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate_path = Path(vfs_bin).expanduser() if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") for candidate_path in ( - repo_root / "cli" / "target" / "debug" / "agentfs", - repo_root / "cli" / "target" / "release" / "agentfs", + repo_root / "cli" / "target" / "debug" / "vfs", + repo_root / "cli" / "target" / "release" / "vfs", ): if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path) @@ -505,12 +505,12 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN to an explicit binary\n" + "failed to build repo-local vfs binary; set VFS_BIN to an explicit binary\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = repo_root / "cli" / "target" / "debug" / "agentfs" + built = repo_root / "cli" / "target" / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) @@ -535,7 +535,7 @@ def prepare_environment(temp_root: Path, profile: bool) -> dict[str, str]: env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") if profile: - env["AGENTFS_PROFILE"] = "1" + env["VFS_PROFILE"] = "1" home = temp_root / "home" for path in (home, home / ".config", home / ".cache", home / ".local" / "share"): @@ -563,13 +563,13 @@ def create_fixture(root: Path, file_count: int, dir_count: int, file_size: int) for index in range(file_count): directory = dirs[index % len(dirs)] - seed = hashlib.sha256(f"agentfs-phase55-read-{index}".encode("utf-8")).digest() + seed = hashlib.sha256(f"vfs-phase55-read-{index}".encode("utf-8")).digest() data = (seed * ((file_size // len(seed)) + 1))[:file_size] (directory / f"file_{index:05d}.dat").write_bytes(data) nested = root / "nested" / "a" / "b" nested.mkdir(parents=True, exist_ok=True) - (nested / "leaf.txt").write_text("agentfs read-path benchmark\n", encoding="utf-8") + (nested / "leaf.txt").write_text("vfs read-path benchmark\n", encoding="utf-8") def copy_fixture(source: Path, destination: Path) -> None: @@ -615,35 +615,35 @@ def split_timing(run: dict[str, Any], workload: Optional[dict[str, Any]]) -> dic } -def compare_workloads(native: Optional[dict[str, Any]], agentfs: Optional[dict[str, Any]]) -> dict[str, Any]: - if native is None or agentfs is None: +def compare_workloads(native: Optional[dict[str, Any]], vfs: Optional[dict[str, Any]]) -> dict[str, Any]: + if native is None or vfs is None: return {"checked": False, "equivalent": False, "reason": "missing JSON workload output"} equivalent = ( - native.get("digest") == agentfs.get("digest") - and native.get("counts") == agentfs.get("counts") - and native.get("parameters") == agentfs.get("parameters") + native.get("digest") == vfs.get("digest") + and native.get("counts") == vfs.get("counts") + and native.get("parameters") == vfs.get("parameters") ) return { "checked": True, "equivalent": equivalent, "native_digest": native.get("digest"), - "agentfs_digest": agentfs.get("digest"), + "vfs_digest": vfs.get("digest"), } -def mode_summary(native_run: dict[str, Any], agentfs_run: dict[str, Any]) -> dict[str, Any]: +def mode_summary(native_run: dict[str, Any], vfs_run: dict[str, Any]) -> dict[str, Any]: native_seconds = native_run["duration_seconds"] - agentfs_seconds = agentfs_run["duration_seconds"] + vfs_seconds = vfs_run["duration_seconds"] return { "native_seconds": native_seconds, - "agentfs_seconds": agentfs_seconds, - "ratio": (agentfs_seconds / native_seconds) if native_seconds > 0 else None, + "vfs_seconds": vfs_seconds, + "ratio": (vfs_seconds / native_seconds) if native_seconds > 0 else None, } def default_output_path() -> Path: stamp = time.strftime("%Y%m%d-%H%M%S") - return Path(tempfile.gettempdir()) / f"agentfs-read-path-benchmark-{stamp}-{uuid.uuid4().hex[:8]}.json" + return Path(tempfile.gettempdir()) / f"vfs-read-path-benchmark-{stamp}-{uuid.uuid4().hex[:8]}.json" def main(argv: list[str]) -> int: @@ -652,23 +652,23 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-read-path-benchmark-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-read-path-benchmark-")) else: - temp_manager = tempfile.TemporaryDirectory(prefix="agentfs-read-path-benchmark-") + temp_manager = tempfile.TemporaryDirectory(prefix="vfs-read-path-benchmark-") temp_root = Path(temp_manager.name) exit_code = 0 output_path = Path(args.output).expanduser() if args.output else default_output_path() result: dict[str, Any] try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) env = prepare_environment(temp_root, args.profile) source_root = temp_root / "source" native_root = temp_root / "native" - agentfs_base_root = temp_root / "agentfs-base" + vfs_base_root = temp_root / "vfs-base" create_fixture(source_root, args.files, args.dirs, args.file_size_bytes) copy_fixture(source_root, native_root) - copy_fixture(source_root, agentfs_base_root) + copy_fixture(source_root, vfs_base_root) base_workload = workload_argv(args) session_prefix = args.session_prefix or f"read-path-{uuid.uuid4().hex}" @@ -676,33 +676,33 @@ def main(argv: list[str]) -> int: for mode in args.modes: session = f"{session_prefix}-{mode}" native_warmup = None - agentfs_warmup = None + vfs_warmup = None if mode == "warm": native_warmup = run_subprocess(base_workload, native_root, env, args.timeout) - agentfs_warmup = run_subprocess( - [agentfs_bin, "run", "--session", session, "--no-default-allows", "--"] + base_workload, - agentfs_base_root, + vfs_warmup = run_subprocess( + [vfs_bin, "run", "--session", session, "--no-default-allows", "--"] + base_workload, + vfs_base_root, env, args.timeout, ) native_run = run_subprocess(base_workload, native_root, env, args.timeout) - agentfs_run = run_subprocess( - [agentfs_bin, "run", "--session", session, "--no-default-allows", "--"] + base_workload, - agentfs_base_root, + vfs_run = run_subprocess( + [vfs_bin, "run", "--session", session, "--no-default-allows", "--"] + base_workload, + vfs_base_root, env, args.timeout, ) native_workload = parse_json_stdout(native_run) - agentfs_workload = parse_json_stdout(agentfs_run) - equivalence = compare_workloads(native_workload, agentfs_workload) + vfs_workload = parse_json_stdout(vfs_run) + equivalence = compare_workloads(native_workload, vfs_workload) profile_summaries = [] - if agentfs_warmup is not None: - profile_summaries.extend(agentfs_warmup.get("profile_summaries", [])) - profile_summaries.extend(agentfs_run.get("profile_summaries", [])) + if vfs_warmup is not None: + profile_summaries.extend(vfs_warmup.get("profile_summaries", [])) + profile_summaries.extend(vfs_run.get("profile_summaries", [])) - if native_run["returncode"] != 0 or agentfs_run["returncode"] != 0: + if native_run["returncode"] != 0 or vfs_run["returncode"] != 0: exit_code = 1 if equivalence["checked"] and not equivalence["equivalent"]: exit_code = 1 @@ -716,22 +716,22 @@ def main(argv: list[str]) -> int: "workload": native_workload, "timing": split_timing(native_run, native_workload), }, - "agentfs": { - "warmup": agentfs_warmup, - "run": agentfs_run, - "workload": agentfs_workload, - "timing": split_timing(agentfs_run, agentfs_workload), + "vfs": { + "warmup": vfs_warmup, + "run": vfs_run, + "workload": vfs_workload, + "timing": split_timing(vfs_run, vfs_workload), "profile_summaries": profile_summaries, "profile_counters": profile_counter_summary(profile_summaries), }, - "summary": mode_summary(native_run, agentfs_run), + "summary": mode_summary(native_run, vfs_run), "steady_state": { "native_workload_seconds": native_workload.get("total_seconds") if native_workload else None, - "agentfs_workload_seconds": agentfs_workload.get("total_seconds") if agentfs_workload else None, + "vfs_workload_seconds": vfs_workload.get("total_seconds") if vfs_workload else None, "ratio": ( - agentfs_workload["total_seconds"] / native_workload["total_seconds"] + vfs_workload["total_seconds"] / native_workload["total_seconds"] if native_workload - and agentfs_workload + and vfs_workload and native_workload.get("total_seconds", 0) > 0 else None ), @@ -747,11 +747,11 @@ def main(argv: list[str]) -> int: "command": { "argv": [str(Path(__file__).resolve())] + argv, "workload_argv": base_workload, - "agentfs_prefix": [agentfs_bin, "run", "--session", "", "--no-default-allows", "--"], + "vfs_prefix": [vfs_bin, "run", "--session", "", "--no-default-allows", "--"], }, "environment": { - "AGENTFS_PROFILE": "1" if args.profile else os.environ.get("AGENTFS_PROFILE"), - "AGENTFS_BIN": args.agentfs_bin, + "VFS_PROFILE": "1" if args.profile else os.environ.get("VFS_PROFILE"), + "VFS_BIN": args.vfs_bin, }, "parameters": { "files": args.files, @@ -766,18 +766,18 @@ def main(argv: list[str]) -> int: "repeated_read_files": args.repeated_read_files, "modes": args.modes, }, - "agentfs": { - "bin": agentfs_bin, + "vfs": { + "bin": vfs_bin, "profile_enabled": args.profile, "profile_summary_count": sum( - mode["agentfs"]["profile_counters"]["summary_count"] for mode in modes + mode["vfs"]["profile_counters"]["summary_count"] for mode in modes ), }, "summary": { "native_seconds": mean([mode["summary"]["native_seconds"] for mode in modes]), - "agentfs_seconds": mean([mode["summary"]["agentfs_seconds"] for mode in modes]), + "vfs_seconds": mean([mode["summary"]["vfs_seconds"] for mode in modes]), "ratio": ( - mean([mode["summary"]["agentfs_seconds"] for mode in modes]) + mean([mode["summary"]["vfs_seconds"] for mode in modes]) / mean([mode["summary"]["native_seconds"] for mode in modes]) if mean([mode["summary"]["native_seconds"] for mode in modes]) > 0 else None diff --git a/scripts/validation/replay/replay_workload.py b/scripts/validation/replay/replay_workload.py index 4a62bc9c..05d67fb8 100755 --- a/scripts/validation/replay/replay_workload.py +++ b/scripts/validation/replay/replay_workload.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Replay a minimal filesystem workload against a temporary AgentFS mount. +Replay a minimal filesystem workload against a temporary Vfs mount. Supported normalized JSONL/TSV operations: mkdir path @@ -16,7 +16,7 @@ open/openat + read(...) for read_file Unsupported operations are summarized and skipped. Use --dry-run to parse and -summarize without creating an AgentFS database or mount. +summarize without creating a Vfs database or mount. """ from __future__ import annotations @@ -530,18 +530,18 @@ def print_summary(result: ParseResult) -> None: print(f"Ignored lines: {result.ignored_lines}") -def resolve_agentfs(agentfs_bin: str) -> str: - if os.sep in agentfs_bin: - resolved = os.path.abspath(os.path.expanduser(agentfs_bin)) +def resolve_vfs(vfs_bin: str) -> str: + if os.sep in vfs_bin: + resolved = os.path.abspath(os.path.expanduser(vfs_bin)) if os.access(resolved, os.X_OK): return resolved - raise PrerequisiteSkip(f"agentfs binary is not executable: {agentfs_bin}") + raise PrerequisiteSkip(f"vfs binary is not executable: {vfs_bin}") - resolved = shutil.which(agentfs_bin) + resolved = shutil.which(vfs_bin) if not resolved: raise PrerequisiteSkip( - "agentfs binary not found. Build/install it first, or pass --agentfs-bin PATH " - "(for example: cargo build --release -p agentfs-cli --bins && cp target/release/agentfs /usr/local/bin)." + "vfs binary not found. Build/install it first, or pass --vfs-bin PATH " + "(for example: cargo build --release -p vfs-cli --bins && cp target/release/vfs /usr/local/bin)." ) return resolved @@ -574,34 +574,34 @@ def unmount(path: str, log_file) -> None: print("No fusermount3/fusermount/umount helper found for cleanup", file=log_file) -class AgentFSMount: - def __init__(self, agentfs_bin: str, report_dir: Optional[str], keep_work: bool) -> None: - self.agentfs_bin = resolve_agentfs(agentfs_bin) +class VfsMount: + def __init__(self, vfs_bin: str, report_dir: Optional[str], keep_work: bool) -> None: + self.vfs_bin = resolve_vfs(vfs_bin) self.keep_work = keep_work - self.report_dir = report_dir or tempfile.mkdtemp(prefix="agentfs-replay-report.", dir="/tmp") + self.report_dir = report_dir or tempfile.mkdtemp(prefix="vfs-replay-report.", dir="/tmp") self.report_dir = os.path.abspath(self.report_dir) self.work_dir = "" self.mount_dir = "" self.mount_process: Optional[subprocess.Popen] = None - def __enter__(self) -> "AgentFSMount": + def __enter__(self) -> "VfsMount": try: os.makedirs(self.report_dir, exist_ok=True) - self.work_dir = tempfile.mkdtemp(prefix="agentfs-replay-work.", dir="/tmp") - self.mount_dir = tempfile.mkdtemp(prefix="agentfs-replay-mnt.", dir="/tmp") + self.work_dir = tempfile.mkdtemp(prefix="vfs-replay-work.", dir="/tmp") + self.mount_dir = tempfile.mkdtemp(prefix="vfs-replay-mnt.", dir="/tmp") agent_id = f"replay-{os.getpid()}-{int(time.time())}" - db_path = os.path.join(self.work_dir, ".agentfs", f"{agent_id}.db") + db_path = os.path.join(self.work_dir, ".vfs", f"{agent_id}.db") with open(os.path.join(self.report_dir, "init.log"), "w", encoding="utf-8") as log_file: - subprocess.run([self.agentfs_bin, "init", agent_id], cwd=self.work_dir, stdout=log_file, stderr=subprocess.STDOUT, check=True) + subprocess.run([self.vfs_bin, "init", agent_id], cwd=self.work_dir, stdout=log_file, stderr=subprocess.STDOUT, check=True) if not os.path.isfile(db_path): - raise ReplayError(f"AgentFS database was not created at {db_path}; see {self.report_dir}/init.log") + raise ReplayError(f"Vfs database was not created at {db_path}; see {self.report_dir}/init.log") mount_log_path = os.path.join(self.report_dir, "mount.log") mount_log = open(mount_log_path, "w", encoding="utf-8") self.mount_process = subprocess.Popen( - [self.agentfs_bin, "mount", db_path, self.mount_dir, "--foreground"], + [self.vfs_bin, "mount", db_path, self.mount_dir, "--foreground"], stdout=mount_log, stderr=subprocess.STDOUT, ) @@ -614,7 +614,7 @@ def __enter__(self) -> "AgentFSMount": break time.sleep(0.1) - raise ReplayError(f"AgentFS mount did not become ready at {self.mount_dir}; see {mount_log_path}") + raise ReplayError(f"Vfs mount did not become ready at {self.mount_dir}; see {mount_log_path}") except Exception: self.cleanup() raise @@ -640,8 +640,8 @@ def cleanup(self) -> None: self.mount_process.wait() if not self.keep_work: - safe_rmtree_tmp(self.work_dir, ("/tmp/agentfs-replay-work.",)) - safe_rmtree_tmp(self.mount_dir, ("/tmp/agentfs-replay-mnt.",)) + safe_rmtree_tmp(self.work_dir, ("/tmp/vfs-replay-work.",)) + safe_rmtree_tmp(self.mount_dir, ("/tmp/vfs-replay-mnt.",)) else: print(f"Kept work directory: {self.work_dir}", file=sys.stderr) print(f"Kept mount directory: {self.mount_dir}", file=sys.stderr) @@ -713,10 +713,10 @@ def replay_operations(operations: Iterable[Operation], mount_dir: str, report_di def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("logfile", help="JSONL, TSV, or strace-like workload log") - parser.add_argument("--dry-run", action="store_true", help="parse and summarize only; do not create an AgentFS mount") - parser.add_argument("--agentfs-bin", default=os.environ.get("AGENTFS_BIN", "agentfs"), help="agentfs executable for replay mode") + parser.add_argument("--dry-run", action="store_true", help="parse and summarize only; do not create a Vfs mount") + parser.add_argument("--vfs-bin", default=os.environ.get("VFS_BIN", "vfs"), help="vfs executable for replay mode") parser.add_argument("--report-dir", default=os.environ.get("REPORT_DIR"), help="directory for init/mount/replay logs") - parser.add_argument("--keep-work", action="store_true", help="keep temporary AgentFS work and mount directories after replay") + parser.add_argument("--keep-work", action="store_true", help="keep temporary Vfs work and mount directories after replay") return parser @@ -737,7 +737,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int: if result.unsupported: print("Unsupported operations will be skipped during replay.", file=sys.stderr) - active_mount: Optional[AgentFSMount] = None + active_mount: Optional[VfsMount] = None def handle_signal(signum, _frame) -> None: if active_mount is not None: @@ -747,11 +747,11 @@ def handle_signal(signum, _frame) -> None: old_int = signal.signal(signal.SIGINT, handle_signal) old_term = signal.signal(signal.SIGTERM, handle_signal) try: - with AgentFSMount(args.agentfs_bin, args.report_dir, args.keep_work) as mount: + with VfsMount(args.vfs_bin, args.report_dir, args.keep_work) as mount: active_mount = mount return replay_operations(result.operations, mount.mount_dir, mount.report_dir) except subprocess.CalledProcessError as exc: - print(f"AgentFS command failed with status {exc.returncode}; see report logs.", file=sys.stderr) + print(f"Vfs command failed with status {exc.returncode}; see report logs.", file=sys.stderr) return 1 except PrerequisiteSkip as exc: print(f"SKIP: {exc}", file=sys.stderr) diff --git a/scripts/validation/vfs-clone-benchmark.py b/scripts/validation/vfs-clone-benchmark.py index 4f3d67b0..9a1ec165 100644 --- a/scripts/validation/vfs-clone-benchmark.py +++ b/scripts/validation/vfs-clone-benchmark.py @@ -1,17 +1,17 @@ #!/usr/bin/env python3 -"""Benchmark `agentfs clone` (bulk ingest) against native `git clone`. +"""Benchmark `vfs clone` (bulk ingest) against native `git clone`. For each iteration the timed unit is the whole user-visible command: native : git clone - agentfs: agentfs clone repo + vfs: vfs clone repo -Correctness per agentfs iteration (through a fresh `agentfs exec` mount): +Correctness per vfs iteration (through a fresh `vfs exec` mount): - `git status --porcelain` is empty (fabricated index matches served stats) - `git fsck --strict` passes - sha256 over the sorted worktree equals the native clone's Usage: - agentfs-clone-benchmark.py --source [--iterations 5] + vfs-clone-benchmark.py --source [--iterations 5] """ from __future__ import annotations @@ -40,16 +40,16 @@ def require(proc: subprocess.CompletedProcess, what: str) -> None: raise RuntimeError(f"{what} failed (rc={proc.returncode}): {proc.stderr.strip()[:500]}") -def resolve_agentfs_bin(arg: str | None) -> str: +def resolve_vfs_bin(arg: str | None) -> str: if arg: return arg for candidate in ( - REPO_ROOT / "cli" / "target" / "release" / "agentfs", - REPO_ROOT / "cli" / "target" / "debug" / "agentfs", + REPO_ROOT / "cli" / "target" / "release" / "vfs", + REPO_ROOT / "cli" / "target" / "debug" / "vfs", ): if candidate.is_file(): return str(candidate) - raise RuntimeError("agentfs binary not found; build cli or pass --agentfs-bin") + raise RuntimeError("vfs binary not found; build cli or pass --vfs-bin") def content_hash_native(workdir: Path) -> str: @@ -60,32 +60,32 @@ def content_hash_native(workdir: Path) -> str: return proc.stdout.split()[0] -def verify_agentfs(agentfs_bin: str, db: Path, native_hash: str) -> None: +def verify_vfs(vfs_bin: str, db: Path, native_hash: str) -> None: script = ( "cd repo || exit 9; " "test -z \"$(git status --porcelain)\" || { echo STATUS_DIRTY; exit 10; }; " "git fsck --strict >/dev/null 2>&1 || { echo FSCK_FAIL; exit 11; }; " + CONTENT_HASH_CMD ) - proc = run([agentfs_bin, "exec", str(db), "sh", "--", "-c", script]) - require(proc, "agentfs verification") + proc = run([vfs_bin, "exec", str(db), "sh", "--", "-c", script]) + require(proc, "vfs verification") # The mount emits tracing lines on stdout; the hash is the last line. lines = [line for line in proc.stdout.strip().splitlines() if line.strip()] got = lines[-1].split()[0] if lines else "" if got != native_hash: - raise RuntimeError(f"content hash mismatch: agentfs {got} != native {native_hash}") + raise RuntimeError(f"content hash mismatch: vfs {got} != native {native_hash}") def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--source", required=True, help="git repository used to build the mirror") parser.add_argument("--iterations", type=int, default=5) - parser.add_argument("--agentfs-bin") + parser.add_argument("--vfs-bin") parser.add_argument("--output") args = parser.parse_args() - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin) - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-clone-bench-")) + vfs_bin = resolve_vfs_bin(args.vfs_bin) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-clone-bench-")) results: dict = {"iterations": [], "source": args.source} try: mirror = temp_root / "mirror.git" @@ -108,32 +108,32 @@ def main() -> int: native_s = time.perf_counter() - started shutil.rmtree(native_dst) - db = temp_root / f"agentfs-{i}.db" + db = temp_root / f"vfs-{i}.db" started = time.perf_counter() require( - run([agentfs_bin, "clone", str(db), str(mirror), "repo"]), - "agentfs clone", + run([vfs_bin, "clone", str(db), str(mirror), "repo"]), + "vfs clone", ) - agentfs_s = time.perf_counter() - started + vfs_s = time.perf_counter() - started - verify_agentfs(agentfs_bin, db, native_hash) + verify_vfs(vfs_bin, db, native_hash) db.unlink(missing_ok=True) results["iterations"].append( - {"native_seconds": native_s, "agentfs_seconds": agentfs_s, - "ratio": agentfs_s / native_s} + {"native_seconds": native_s, "vfs_seconds": vfs_s, + "ratio": vfs_s / native_s} ) print( - f"iter {i}: native={native_s:.3f}s agentfs={agentfs_s:.3f}s " - f"ratio={agentfs_s / native_s:.2f}x (verified)", + f"iter {i}: native={native_s:.3f}s vfs={vfs_s:.3f}s " + f"ratio={vfs_s / native_s:.2f}x (verified)", flush=True, ) natives = [r["native_seconds"] for r in results["iterations"]] - ours = [r["agentfs_seconds"] for r in results["iterations"]] + ours = [r["vfs_seconds"] for r in results["iterations"]] results["summary"] = { "native_median": statistics.median(natives), - "agentfs_median": statistics.median(ours), + "vfs_median": statistics.median(ours), "ratio_median": statistics.median(ours) / statistics.median(natives), "ratio_paired_median": statistics.median( r["ratio"] for r in results["iterations"] @@ -142,7 +142,7 @@ def main() -> int: } s = results["summary"] print( - f"\nmedian: native={s['native_median']:.3f}s agentfs={s['agentfs_median']:.3f}s " + f"\nmedian: native={s['native_median']:.3f}s vfs={s['vfs_median']:.3f}s " f"ratio={s['ratio_median']:.2f}x paired={s['ratio_paired_median']:.2f}x" ) if args.output: diff --git a/scripts/validation/workload-baseline.py b/scripts/validation/workload-baseline.py index 596e5206..5bf0f7f9 100755 --- a/scripts/validation/workload-baseline.py +++ b/scripts/validation/workload-baseline.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Phase 0 native-vs-AgentFS workload baseline harness.""" +"""Phase 0 native-vs-Vfs workload baseline harness.""" from __future__ import annotations @@ -104,7 +104,7 @@ def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( description=( "Compare a workload on native filesystem storage against the same " - "workload under an AgentFS copy-on-write overlay." + "workload under a Vfs copy-on-write overlay." ), formatter_class=argparse.RawDescriptionHelpFormatter, epilog="""Examples: @@ -119,8 +119,8 @@ def parse_args(argv: list[str]) -> argparse.Namespace: scripts/validation/workload-baseline.py --source /path/to/factory-mono -- cargo test -p crate Environment: - AGENTFS_BIN path/name of agentfs executable - AGENTFS_PROFILE set to 1 to emit AgentFS profiling summaries + VFS_BIN path/name of vfs executable + VFS_PROFILE set to 1 to emit Vfs profiling summaries WORKLOAD_BASELINE_COMMAND shell command to run when --command is omitted WORKLOAD_BASELINE_SOURCE source tree to copy when --source is omitted """, @@ -140,13 +140,13 @@ def parse_args(argv: list[str]) -> argparse.Namespace: "-c", "--command", default=os.environ.get("WORKLOAD_BASELINE_COMMAND"), - help="shell command to run in native and AgentFS worktrees", + help="shell command to run in native and Vfs worktrees", ) parser.add_argument( "--iterations", type=positive_int, default=positive_int(os.environ.get("WORKLOAD_BASELINE_ITERATIONS", "1")), - help="number of fresh native/AgentFS comparisons to run (default: 1)", + help="number of fresh native/Vfs comparisons to run (default: 1)", ) parser.add_argument( "--timeout", @@ -155,14 +155,14 @@ def parse_args(argv: list[str]) -> argparse.Namespace: help="per-command timeout in seconds (default: 120)", ) parser.add_argument( - "--agentfs-bin", - default=os.environ.get("AGENTFS_BIN"), - help="agentfs executable path/name (default: repo target binary, building cli if needed)", + "--vfs-bin", + default=os.environ.get("VFS_BIN"), + help="vfs executable path/name (default: repo target binary, building cli if needed)", ) parser.add_argument( "--compare-stdout", action="store_true", - help="command mode: fail if native and AgentFS stdout differ exactly", + help="command mode: fail if native and Vfs stdout differ exactly", ) parser.add_argument( "--keep-temp", @@ -223,20 +223,20 @@ def parse_args(argv: list[str]) -> argparse.Namespace: return args -def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: - if agentfs_bin: - candidate_path = Path(agentfs_bin).expanduser() +def resolve_vfs_bin(vfs_bin: Optional[str], repo_root: Path) -> str: + if vfs_bin: + candidate_path = Path(vfs_bin).expanduser() if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path.resolve()) - if os.sep not in agentfs_bin: - found = shutil.which(agentfs_bin) + if os.sep not in vfs_bin: + found = shutil.which(vfs_bin) if found: return found - raise RuntimeError(f"configured agentfs executable not found or not executable: {agentfs_bin}") + raise RuntimeError(f"configured vfs executable not found or not executable: {vfs_bin}") for candidate_path in ( - repo_root / "cli" / "target" / "debug" / "agentfs", - repo_root / "cli" / "target" / "release" / "agentfs", + repo_root / "cli" / "target" / "debug" / "vfs", + repo_root / "cli" / "target" / "release" / "vfs", ): if candidate_path.is_file() and os.access(candidate_path, os.X_OK): return str(candidate_path) @@ -250,12 +250,12 @@ def resolve_agentfs_bin(agentfs_bin: Optional[str], repo_root: Path) -> str: ) if build.returncode != 0: raise RuntimeError( - "failed to build repo-local agentfs binary; set AGENTFS_BIN to an explicit binary\n" + "failed to build repo-local vfs binary; set VFS_BIN to an explicit binary\n" f"stdout:\n{tail_text(build.stdout)}\n" f"stderr:\n{tail_text(build.stderr)}" ) - built = repo_root / "cli" / "target" / "debug" / "agentfs" + built = repo_root / "cli" / "target" / "debug" / "vfs" if built.is_file() and os.access(built, os.X_OK): return str(built) @@ -297,15 +297,15 @@ def create_synthetic_tree(root: Path) -> None: docs.mkdir(parents=True, exist_ok=True) for index in range(4): (docs / f"note_{index:02d}.md").write_text( - f"# Synthetic note {index}\n\n" + ("AgentFS baseline data.\n" * 20), + f"# Synthetic note {index}\n\n" + ("Vfs baseline data.\n" * 20), encoding="utf-8", ) (root / "pyproject.toml").write_text( - "[project]\nname = \"agentfs-baseline-synthetic\"\nversion = \"0.0.0\"\n", + "[project]\nname = \"vfs-baseline-synthetic\"\nversion = \"0.0.0\"\n", encoding="utf-8", ) - (root / ".agentfs_baseline_workload.py").write_text( + (root / ".vfs_baseline_workload.py").write_text( SYNTHETIC_WORKLOAD, encoding="utf-8", ) @@ -324,7 +324,7 @@ def copy_source_tree(source: Path, destination: Path, excludes: list[str]) -> No def prepare_environment(temp_root: Path, preserve_home: bool) -> dict[str, str]: env = os.environ.copy() - env["AGENTFS_BASELINE"] = "1" + env["VFS_BASELINE"] = "1" env.setdefault("PYTHONDONTWRITEBYTECODE", "1") env.setdefault("NO_COLOR", "1") temp_dir = temp_root / "tmp" @@ -371,13 +371,13 @@ def extract_profile_summaries(stderr: Any) -> list[dict[str, Any]]: summaries: list[dict[str, Any]] = [] for line in text.splitlines(): line = line.strip() - if not line or "agentfs_profile_summary" not in line: + if not line or "vfs_profile_summary" not in line: continue try: value = json.loads(line) except json.JSONDecodeError: continue - if isinstance(value, dict) and value.get("event") == "agentfs_profile_summary": + if isinstance(value, dict) and value.get("event") == "vfs_profile_summary": summaries.append(value) return summaries @@ -464,7 +464,7 @@ def terminate_process_tree(proc: subprocess.Popen[str]) -> None: def command_argv(args: argparse.Namespace) -> tuple[str, str, list[str]]: if args.mode == "synthetic": - return ("argv", f"{sys.executable} .agentfs_baseline_workload.py", [sys.executable, ".agentfs_baseline_workload.py"]) + return ("argv", f"{sys.executable} .vfs_baseline_workload.py", [sys.executable, ".vfs_baseline_workload.py"]) if args.command: return ("shell", args.command, ["sh", "-lc", args.command]) return ("argv", " ".join(args.argv), args.argv) @@ -472,12 +472,12 @@ def command_argv(args: argparse.Namespace) -> tuple[str, str, list[str]]: def prepare_roots(args: argparse.Namespace, iteration_dir: Path) -> tuple[Path, Path]: native_root = iteration_dir / "native" - agentfs_root = iteration_dir / "agentfs-base" + vfs_root = iteration_dir / "vfs-base" if args.mode == "synthetic": create_synthetic_tree(native_root) - create_synthetic_tree(agentfs_root) - return native_root, agentfs_root + create_synthetic_tree(vfs_root) + return native_root, vfs_root source = Path(args.source or ".").expanduser().resolve() if not source.is_dir(): @@ -487,19 +487,19 @@ def prepare_roots(args: argparse.Namespace, iteration_dir: Path) -> tuple[Path, return source, source copy_source_tree(source, native_root, args.exclude) - copy_source_tree(source, agentfs_root, args.exclude) - return native_root, agentfs_root + copy_source_tree(source, vfs_root, args.exclude) + return native_root, vfs_root def summarize_runs(iterations: list[dict[str, Any]]) -> dict[str, Any]: native_durations = [item["native"]["duration_seconds"] for item in iterations] - agentfs_durations = [item["agentfs"]["duration_seconds"] for item in iterations] + vfs_durations = [item["vfs"]["duration_seconds"] for item in iterations] native_mean = mean(native_durations) - agentfs_mean = mean(agentfs_durations) + vfs_mean = mean(vfs_durations) return { "native_seconds": native_mean, - "agentfs_seconds": agentfs_mean, - "ratio": (agentfs_mean / native_mean) if native_mean > 0 else None, + "vfs_seconds": vfs_mean, + "ratio": (vfs_mean / native_mean) if native_mean > 0 else None, } @@ -510,26 +510,26 @@ def parse_json_stdout(run: dict[str, Any]) -> Any: return json.loads(lines[-1]) -def compare_outputs(args: argparse.Namespace, native: dict[str, Any], agentfs: dict[str, Any]) -> dict[str, Any]: - if native["returncode"] != 0 or agentfs["returncode"] != 0: +def compare_outputs(args: argparse.Namespace, native: dict[str, Any], vfs: dict[str, Any]) -> dict[str, Any]: + if native["returncode"] != 0 or vfs["returncode"] != 0: return {"checked": False, "reason": "non-zero return code"} if args.mode == "synthetic": native_json = parse_json_stdout(native) - agentfs_json = parse_json_stdout(agentfs) + vfs_json = parse_json_stdout(vfs) return { "checked": True, "kind": "synthetic-json-stdout", - "equivalent": native_json == agentfs_json, + "equivalent": native_json == vfs_json, "native": native_json, - "agentfs": agentfs_json, + "vfs": vfs_json, } if args.compare_stdout: return { "checked": True, "kind": "stdout-tail", - "equivalent": native["stdout_tail"] == agentfs["stdout_tail"], + "equivalent": native["stdout_tail"] == vfs["stdout_tail"], } return {"checked": False, "reason": "command mode requires --compare-stdout or external review"} @@ -542,43 +542,43 @@ def main(argv: list[str]) -> int: temp_manager: Optional[tempfile.TemporaryDirectory[str]] = None if args.keep_temp: - temp_root = Path(tempfile.mkdtemp(prefix="agentfs-phase0-baseline-")) + temp_root = Path(tempfile.mkdtemp(prefix="vfs-phase0-baseline-")) else: - temp_manager = tempfile.TemporaryDirectory(prefix="agentfs-phase0-baseline-") + temp_manager = tempfile.TemporaryDirectory(prefix="vfs-phase0-baseline-") temp_root = Path(temp_manager.name) exit_code = 0 result: dict[str, Any] try: - agentfs_bin = resolve_agentfs_bin(args.agentfs_bin, repo_root) + vfs_bin = resolve_vfs_bin(args.vfs_bin, repo_root) env = prepare_environment(temp_root, args.preserve_home) command_kind, command_display, native_command = command_argv(args) - agentfs_command = [agentfs_bin, "run", "--no-default-allows", "--"] + native_command + vfs_command = [vfs_bin, "run", "--no-default-allows", "--"] + native_command iterations = [] for index in range(args.iterations): iteration_dir = temp_root / f"iteration-{index + 1:02d}" iteration_dir.mkdir(parents=True, exist_ok=True) - native_root, agentfs_root = prepare_roots(args, iteration_dir) + native_root, vfs_root = prepare_roots(args, iteration_dir) native = run_subprocess(native_command, native_root, env, args.timeout) - agentfs = run_subprocess(agentfs_command, agentfs_root, env, args.timeout) - equivalence = compare_outputs(args, native, agentfs) + vfs = run_subprocess(vfs_command, vfs_root, env, args.timeout) + equivalence = compare_outputs(args, native, vfs) ratio = None if native["duration_seconds"] > 0: - ratio = agentfs["duration_seconds"] / native["duration_seconds"] + ratio = vfs["duration_seconds"] / native["duration_seconds"] iterations.append( { "index": index + 1, "native_root": str(native_root), - "agentfs_base_root": str(agentfs_root), + "vfs_base_root": str(vfs_root), "native": native, - "agentfs": agentfs, + "vfs": vfs, "equivalence": equivalence, "ratio": ratio, } ) - if native["returncode"] != 0 or agentfs["returncode"] != 0: + if native["returncode"] != 0 or vfs["returncode"] != 0: exit_code = 1 if equivalence.get("checked") and not equivalence.get("equivalent"): exit_code = 1 @@ -590,12 +590,12 @@ def main(argv: list[str]) -> int: "kind": command_kind, "display": command_display, }, - "agentfs": { - "bin": agentfs_bin, - "overlay_command_prefix": [agentfs_bin, "run", "--no-default-allows", "--"], - "profile_enabled": env_flag("AGENTFS_PROFILE"), + "vfs": { + "bin": vfs_bin, + "overlay_command_prefix": [vfs_bin, "run", "--no-default-allows", "--"], + "profile_enabled": env_flag("VFS_PROFILE"), "profile_summary_count": sum( - len(item["agentfs"].get("profile_summaries", [])) for item in iterations + len(item["vfs"].get("profile_summaries", [])) for item in iterations ), }, "source": { From debbd507b8f5c91a753098e8ac32905d401485d7 Mon Sep 17 00:00:00 2001 From: factory-ain3sh Date: Fri, 24 Jul 2026 01:09:38 -0700 Subject: [PATCH 4/8] refactor(nfs): rename write handle magic to vfs Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- crates/vfs-nfs/src/adapter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/vfs-nfs/src/adapter.rs b/crates/vfs-nfs/src/adapter.rs index b64e2624..add33942 100644 --- a/crates/vfs-nfs/src/adapter.rs +++ b/crates/vfs-nfs/src/adapter.rs @@ -24,7 +24,7 @@ use vfs_core::{ /// Root directory inode number const ROOT_INO: fileid3 = 1; -const WRITE_HANDLE_MAGIC: &[u8; 8] = b"AFSWRIT\0"; +const WRITE_HANDLE_MAGIC: &[u8; 8] = b"VFSWRIT\0"; const PLAIN_HANDLE_LEN: usize = 16; const WRITE_HANDLE_LEN: usize = 32; From 07fea79e94b42a07c779f31370aeab362f1d1e21 Mon Sep 17 00:00:00 2001 From: factory-ain3sh Date: Mon, 27 Jul 2026 17:49:36 -0700 Subject: [PATCH 5/8] fix(mount): gate wait_for_mount to Linux --- crates/vfs-mount/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/vfs-mount/src/lib.rs b/crates/vfs-mount/src/lib.rs index ac56f835..6a74e11e 100644 --- a/crates/vfs-mount/src/lib.rs +++ b/crates/vfs-mount/src/lib.rs @@ -508,6 +508,10 @@ async fn shutdown_signal() -> std::io::Result<()> { } /// Wait for a path to become a mountpoint. +/// +/// Only the Linux FUSE backend polls for mount readiness; the macOS NFS +/// backend learns readiness from its own server loop. +#[cfg(target_os = "linux")] pub(crate) fn wait_for_mount(path: &Path, timeout: Duration) -> bool { let start = std::time::Instant::now(); let interval = Duration::from_millis(50); From 814d7254e58076f660f6622d914f900b8e82f011 Mon Sep 17 00:00:00 2001 From: factory-ain3sh Date: Mon, 27 Jul 2026 17:49:36 -0700 Subject: [PATCH 6/8] chore: repair Icon glob and ignore legacy .agentfs dirs --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 1277e05e..1f14afde 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,7 @@ __MACOSX/ .AppleDouble .LSOverride -Icon[ -] +Icon? # Thumbnails ._* @@ -27,6 +26,7 @@ Temporary Items .apdisk .vfs +.agentfs target/ # Python bytecode cache From 11830370cd9306b19db0d8abb978332042e916c4 Mon Sep 17 00:00:00 2001 From: factory-ain3sh Date: Mon, 27 Jul 2026 18:01:05 -0700 Subject: [PATCH 7/8] fix(mount): restore public unmount and idiomatic MountOpts construction for macOS --- crates/vfs-cli/src/cmd/run/darwin.rs | 12 ++++++++---- crates/vfs-mount/src/lib.rs | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/vfs-cli/src/cmd/run/darwin.rs b/crates/vfs-cli/src/cmd/run/darwin.rs index a818c075..612f5750 100644 --- a/crates/vfs-cli/src/cmd/run/darwin.rs +++ b/crates/vfs-cli/src/cmd/run/darwin.rs @@ -359,10 +359,14 @@ pub async fn run(options: RunOptions) -> Result<()> { let fs: Arc = Arc::new(overlay); - let mut mount_opts = MountOpts::new(session.mountpoint.clone(), Backend::Nfs); - mount_opts.fsname = format!("vfs:{}", session.session_id); - mount_opts.lazy_unmount = true; - mount_opts.timeout = std::time::Duration::from_secs(10); + let mount_opts = MountOpts { + mountpoint: session.mountpoint.clone(), + backend: Backend::Nfs, + fsname: format!("vfs:{}", session.session_id), + lazy_unmount: true, + timeout: std::time::Duration::from_secs(10), + ..MountOpts::default() + }; let mount_handle = mount_fs(fs, mount_opts).await?; print_welcome_banner(&session, encrypted); diff --git a/crates/vfs-mount/src/lib.rs b/crates/vfs-mount/src/lib.rs index 6a74e11e..a758bb96 100644 --- a/crates/vfs-mount/src/lib.rs +++ b/crates/vfs-mount/src/lib.rs @@ -440,7 +440,7 @@ impl Drop for MountHandle { /// /// This function handles unmounting for both FUSE and NFS backends. /// If `lazy` is true, uses lazy unmount which detaches immediately even if busy. -fn unmount(mountpoint: &Path, backend: Backend, lazy: bool) -> Result<()> { +pub fn unmount(mountpoint: &Path, backend: Backend, lazy: bool) -> Result<()> { match backend { #[cfg(target_os = "linux")] Backend::Fuse => fuse::unmount_fuse(mountpoint, lazy), From 863e5a7349ca813476e178b719a7f041e067615b Mon Sep 17 00:00:00 2001 From: factory-ain3sh Date: Mon, 27 Jul 2026 18:06:35 -0700 Subject: [PATCH 8/8] fix(cli): gate Linux-only run helpers out of macOS builds --- crates/vfs-cli/src/cmd/profiling.rs | 1 + crates/vfs-cli/src/cmd/ps.rs | 8 ++++++++ crates/vfs-cli/src/knobs.rs | 2 ++ 3 files changed, 11 insertions(+) diff --git a/crates/vfs-cli/src/cmd/profiling.rs b/crates/vfs-cli/src/cmd/profiling.rs index 5dec2d8b..4a038ece 100644 --- a/crates/vfs-cli/src/cmd/profiling.rs +++ b/crates/vfs-cli/src/cmd/profiling.rs @@ -35,6 +35,7 @@ pub fn emit_cli_report() { emit_profile_summary("cli"); } +#[cfg(target_os = "linux")] pub(crate) fn report_checkpoint() { if let Some(payload) = vfs_core::telemetry::checkpoint_payload(PROFILE_SUMMARY_EVENT) { eprintln!("{payload}"); diff --git a/crates/vfs-cli/src/cmd/ps.rs b/crates/vfs-cli/src/cmd/ps.rs index bdb4d0ac..304f6bdc 100644 --- a/crates/vfs-cli/src/cmd/ps.rs +++ b/crates/vfs-cli/src/cmd/ps.rs @@ -23,17 +23,23 @@ pub struct ProcInfo { } /// Get the path to the procs directory for a session. +/// +/// Proc files are written only by the Linux `vfs run` path; other platforms +/// read sessions via [`list_sessions`] without these helpers. +#[cfg(target_os = "linux")] pub(crate) fn procs_dir(session_id: &str) -> PathBuf { let home = dirs::home_dir().expect("home directory"); home.join(".vfs").join("run").join(session_id).join("procs") } /// Get the path to a proc file. +#[cfg(target_os = "linux")] fn proc_file(session_id: &str, pid: u32) -> PathBuf { procs_dir(session_id).join(format!("{}.json", pid)) } /// Write a proc file for the current process. +#[cfg(target_os = "linux")] pub(crate) fn write_proc_file( session_id: &str, owner: bool, @@ -60,6 +66,7 @@ pub(crate) fn write_proc_file( } /// Remove the proc file for the current process. +#[cfg(target_os = "linux")] pub(crate) fn remove_proc_file(session_id: &str) { let pid = std::process::id(); let path = proc_file(session_id, pid); @@ -90,6 +97,7 @@ struct SessionInfo { } /// Get the set of active session IDs. +#[cfg(target_os = "linux")] pub(crate) fn active_session_ids() -> std::collections::HashSet { list_sessions().into_iter().map(|s| s.session_id).collect() } diff --git a/crates/vfs-cli/src/knobs.rs b/crates/vfs-cli/src/knobs.rs index 6bcf0cdd..3b8fbdb2 100644 --- a/crates/vfs-cli/src/knobs.rs +++ b/crates/vfs-cli/src/knobs.rs @@ -91,6 +91,8 @@ const DEFAULT_URING_DEPTH: usize = 4; #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum KnobClass { ProductConfig, + /// Constructed only by Linux FUSE kill-switch knobs today. + #[cfg_attr(not(target_os = "linux"), allow(dead_code))] KillSwitch, Sunset, }