Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/client/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ fn wasm_command_mounts() -> Vec<MountConfig> {

/// Locate the materialized coreutils package under the in-repo registry build.
pub fn coreutils_package_dir() -> Option<PathBuf> {
let registry_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../software/coreutils/dist/package");
let registry_dir =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../software/coreutils/dist/package");
if registry_dir.join("agentos-package.json").is_file() {
return std::fs::canonicalize(registry_dir).ok();
}
Expand Down
3 changes: 1 addition & 2 deletions crates/client/tests/shell_pty_packages_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ fn coreutils_package_path() -> Option<PathBuf> {
}
}
for dir in [
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../software/coreutils/dist/package"),
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../software/coreutils/dist/package"),
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../../node_modules/@agentos-software/coreutils/dist/package"),
] {
Expand Down
165 changes: 117 additions & 48 deletions crates/execution/assets/runners/wasm-runner.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const WASI_ERRNO_FAULT = 21;
const WASI_RIGHT_FD_WRITE = 64n;
const WASI_FILETYPE_UNKNOWN = 0;
const WASI_FILETYPE_CHARACTER_DEVICE = 2;
const WASI_FILETYPE_DIRECTORY = 3;
const WASI_FILETYPE_REGULAR_FILE = 4;
const WASI_OFLAGS_CREAT = 1;
const WASI_OFLAGS_DIRECTORY = 2;
Expand Down Expand Up @@ -428,7 +429,8 @@ const passthroughHandles = new Map([
]);
const retainedSyntheticHandlesByDisplayFd = new Map();
const retainedSpawnOutputHandlesByFd = new Map();
let nextSyntheticFd = 64;
const FIRST_SYNTHETIC_FD = 1 << 20;
let nextSyntheticFd = FIRST_SYNTHETIC_FD;
let nextSyntheticPipeId = 1;
const syntheticWaitArray = new Int32Array(new SharedArrayBuffer(4));
let delegateWriteScratch = { base: 0, capacity: 0 };
Expand Down Expand Up @@ -1160,13 +1162,19 @@ function fsOpenFlagForPathOpen(oflags, rightsBase, fdflags) {
return 'r+';
}

function allocateSyntheticFd() {
let fd = nextSyntheticFd;
while (
function syntheticFdInUse(fd) {
return (
syntheticFdEntries.has(fd) ||
passthroughHandles.has(fd) ||
retainedSpawnOutputHandlesByFd.has(fd) ||
retainedSyntheticHandlesByDisplayFd.has(fd) ||
delegateManagedFdRefCounts.has(fd)
) {
);
}

function allocateSyntheticFd(minFd = nextSyntheticFd) {
let fd = Math.max(FIRST_SYNTHETIC_FD, Number(minFd) >>> 0);
while (syntheticFdInUse(fd)) {
fd += 1;
}
nextSyntheticFd = fd + 1;
Expand Down Expand Up @@ -1226,8 +1234,9 @@ function openManagedPathIoFd(guestPath, rightsBase, fdflags) {
return null;
}
try {
const hostPath = resolveHostFsPath(guestPath) ?? guestPath;
return fsModule.openSync(
guestPath,
hostPath,
fsOpenNumericFlagsForManagedPath(rightsBase, fdflags),
0o666,
);
Expand All @@ -1243,16 +1252,35 @@ function retainPathOpenDelegateFd(openedFdPtr, guestPath, fdflags, rightsBase) {

try {
const openedFd = new DataView(instanceMemory.buffer).getUint32(Number(openedFdPtr), true);
let retainedFd = openedFd;
if (openedFd > 2 && syntheticFdInUse(openedFd)) {
if (typeof delegateManagedFdRenumber !== 'function') {
return WASI_ERRNO_FAULT;
}
retainedFd = allocateSyntheticFd(openedFd + 1);
const renumberResult = delegateManagedFdRenumber(openedFd, retainedFd);
if (renumberResult !== WASI_ERRNO_SUCCESS) {
return renumberResult;
}
const writeResult = writeGuestUint32(openedFdPtr, retainedFd);
if (writeResult !== WASI_ERRNO_SUCCESS) {
return writeResult;
}
traceHostProcess('path-open-delegate-renumber', {
openedFd,
retainedFd,
});
}
const append = (Number(fdflags) & WASI_FDFLAGS_APPEND) !== 0;
retainDelegateFd(openedFd);
if (openedFd > 2 && !passthroughHandles.has(openedFd)) {
retainDelegateFd(retainedFd);
if (retainedFd > 2 && !passthroughHandles.has(retainedFd)) {
const ioFd = openManagedPathIoFd(guestPath, rightsBase, fdflags);
closedPassthroughFds.delete(openedFd);
passthroughHandles.set(openedFd, {
closedPassthroughFds.delete(retainedFd);
passthroughHandles.set(retainedFd, {
kind: 'passthrough',
targetFd: openedFd,
targetFd: retainedFd,
ioFd,
displayFd: openedFd,
displayFd: retainedFd,
refCount: 0,
open: true,
readOnly:
Expand Down Expand Up @@ -1331,6 +1359,19 @@ function writeGuestFilestat(ptr, stats, filetype = WASI_FILETYPE_REGULAR_FILE) {
}
}

function wasiFiletypeFromStats(stats) {
if (typeof stats?.isDirectory === 'function' && stats.isDirectory()) {
return WASI_FILETYPE_DIRECTORY;
}
if (typeof stats?.isCharacterDevice === 'function' && stats.isCharacterDevice()) {
return WASI_FILETYPE_CHARACTER_DEVICE;
}
if (typeof stats?.isFile === 'function' && stats.isFile()) {
return WASI_FILETYPE_REGULAR_FILE;
}
return WASI_FILETYPE_UNKNOWN;
}

function writeGuestFdstat(ptr, filetype, flags, rightsBase, rightsInheriting) {
if (!(instanceMemory instanceof WebAssembly.Memory)) {
return WASI_ERRNO_FAULT;
Expand Down Expand Up @@ -4226,8 +4267,8 @@ const hostProcessImport = {
readHandleCount: 0,
writeHandleCount: 0,
};
const readFd = nextSyntheticFd++;
const writeFd = nextSyntheticFd++;
const readFd = allocateSyntheticFd();
const writeFd = allocateSyntheticFd();
syntheticFdEntries.set(readFd, createPipeHandle('pipe-read', pipe, readFd));
syntheticFdEntries.set(writeFd, createPipeHandle('pipe-write', pipe, writeFd));
if (writeGuestUint32(retReadFdPtr, readFd) !== WASI_ERRNO_SUCCESS) {
Expand All @@ -4244,20 +4285,7 @@ const hostProcessImport = {
if (!handle) {
return WASI_ERRNO_BADF;
}
let duplicatedFd = 0;
while (
duplicatedFd <= 2 &&
(
syntheticFdEntries.has(duplicatedFd) ||
passthroughHandles.has(duplicatedFd) ||
delegateManagedFdRefCounts.has(duplicatedFd)
)
) {
duplicatedFd += 1;
}
if (duplicatedFd > 2) {
duplicatedFd = nextSyntheticFd++;
}
const duplicatedFd = allocateSyntheticFd(0);
syntheticFdEntries.set(duplicatedFd, handle);
traceHostProcess('fd-dup', {
fd: Number(fd) >>> 0,
Expand Down Expand Up @@ -4329,15 +4357,7 @@ const hostProcessImport = {
return WASI_ERRNO_BADF;
}

let duplicatedFd = minimumFdNumber >>> 0;
while (
syntheticFdEntries.has(duplicatedFd) ||
passthroughHandles.has(duplicatedFd) ||
delegateManagedFdRefCounts.has(duplicatedFd)
) {
duplicatedFd += 1;
}
nextSyntheticFd = Math.max(nextSyntheticFd, duplicatedFd + 1);
const duplicatedFd = allocateSyntheticFd(minimumFdNumber);

syntheticFdEntries.set(duplicatedFd, handle);
traceHostProcess('fd-dup-min', {
Expand Down Expand Up @@ -5213,11 +5233,13 @@ wasiImport.fd_read = (fd, iovs, iovsLen, nreadPtr) => {
}

if (handle?.kind === 'passthrough') {
return delegateManagedFdRead
? __agentOSWasiMeasurePhase('fd_read', 'delegate_call', () =>
delegateManagedFdRead(handle.targetFd, iovs, iovsLen, nreadPtr)
)
: WASI_ERRNO_BADF;
if (!delegateManagedFdRead) {
return WASI_ERRNO_BADF;
}
const result = __agentOSWasiMeasurePhase('fd_read', 'delegate_call', () =>
delegateManagedFdRead(handle.targetFd, iovs, iovsLen, nreadPtr)
);
return result;
}

if (rejectClosedPassthroughFd(numericFd)) {
Expand Down Expand Up @@ -5291,9 +5313,10 @@ wasiImport.fd_pread = (fd, iovs, iovsLen, offset, nreadPtr) => {
return mapSyntheticFsError(error);
}
}
return delegateFdPread
? delegateFdPread(handle.targetFd, iovs, iovsLen, offset, nreadPtr)
: WASI_ERRNO_BADF;
if (!delegateFdPread) {
return WASI_ERRNO_BADF;
}
return delegateFdPread(handle.targetFd, iovs, iovsLen, offset, nreadPtr);
}

if (rejectClosedPassthroughFd(fd)) {
Expand All @@ -5308,6 +5331,9 @@ wasiImport.fd_pread = (fd, iovs, iovsLen, offset, nreadPtr) => {
wasiImport.fd_pwrite = (fd, iovs, iovsLen, offset, nwrittenPtr) => {
const handle = lookupFdHandle(fd);
if (handle?.kind === 'guest-file') {
if (handle.readOnly === true) {
return WASI_ERRNO_ROFS;
}
try {
const bytes = collectGuestIovBytes(iovs, iovsLen);
const written = fsModule.writeSync(
Expand Down Expand Up @@ -5512,11 +5538,50 @@ wasiImport.fd_fdstat_get = (fd, statPtr) => {
);
}

if (handle?.kind === 'guest-file') {
try {
const stat = fsModule.fstatSync(handle.targetFd);
return writeGuestFdstat(
statPtr,
wasiFiletypeFromStats(stat),
0,
WASI_RIGHT_FD_READ |
WASI_RIGHT_FD_SEEK |
WASI_RIGHT_FD_TELL |
WASI_RIGHT_FD_FILESTAT_GET |
WASI_RIGHT_FD_WRITE |
WASI_RIGHT_FD_SYNC,
0n,
);
} catch (error) {
return mapSyntheticFsError(error);
}
}

if (handle && handle.kind !== 'passthrough') {
return WASI_ERRNO_BADF;
}

if (handle?.kind === 'passthrough') {
if (typeof handle.ioFd === 'number') {
try {
const stat = fsModule.fstatSync(handle.ioFd);
return writeGuestFdstat(
statPtr,
wasiFiletypeFromStats(stat),
0,
WASI_RIGHT_FD_READ |
WASI_RIGHT_FD_SEEK |
WASI_RIGHT_FD_TELL |
WASI_RIGHT_FD_FILESTAT_GET |
WASI_RIGHT_FD_WRITE |
WASI_RIGHT_FD_SYNC,
0n,
);
} catch (error) {
return mapSyntheticFsError(error);
}
}
return delegateManagedFdFdstatGet
? __agentOSWasiMeasurePhase('fd_fdstat_get', 'delegate_call', () =>
delegateManagedFdFdstatGet(handle.targetFd, statPtr)
Expand Down Expand Up @@ -5574,9 +5639,10 @@ wasiImport.fd_filestat_get = (fd, statPtr) => {
return mapSyntheticFsError(error);
}
}
return delegateManagedFdFilestatGet
? delegateManagedFdFilestatGet(handle.targetFd, statPtr)
: WASI_ERRNO_BADF;
if (!delegateManagedFdFilestatGet) {
return WASI_ERRNO_BADF;
}
return delegateManagedFdFilestatGet(handle.targetFd, statPtr);
}

if (rejectClosedPassthroughFd(fd)) {
Expand Down Expand Up @@ -5726,6 +5792,9 @@ wasiImport.fd_write = (fd, iovs, iovsLen, nwrittenPtr) => {
}

if (handle?.kind === 'guest-file') {
if (handle.readOnly === true) {
return WASI_ERRNO_ROFS;
}
try {
const bytes = __agentOSWasiMeasurePhase('fd_write', 'guest_iov_collect', () =>
collectGuestIovBytes(iovs, iovsLen)
Expand Down
10 changes: 2 additions & 8 deletions crates/native-sidecar/tests/projection_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,14 +614,8 @@ fn projection_bench() {
"PROJ_BENCH_COREUTILS_TAR",
"software/coreutils/dist/package.tar",
);
let tar_tar = source_tar_path(
"PROJ_BENCH_TAR_TAR",
"software/tar/dist/package.tar",
);
let git_tar = source_tar_path(
"PROJ_BENCH_GIT_TAR",
"software/git/dist/package.tar",
);
let tar_tar = source_tar_path("PROJ_BENCH_TAR_TAR", "software/tar/dist/package.tar");
let git_tar = source_tar_path("PROJ_BENCH_GIT_TAR", "software/git/dist/package.tar");

println!("\n# agentOS package load benchmark (.aospkg)");
println!(
Expand Down
4 changes: 3 additions & 1 deletion crates/vfs/src/posix/overlay_fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2005,7 +2005,9 @@ mod tests {
.expect("rename replacement over whiteout");

assert_eq!(
overlay.read_file("/archive.zip").expect("read renamed file"),
overlay
.read_file("/archive.zip")
.expect("read renamed file"),
b"new"
);
assert!(!overlay.exists("/workspace-temp"));
Expand Down
19 changes: 18 additions & 1 deletion docs-internal/registry-parity-worklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ actual backing:
| **curl** | DONE | our custom driver over a libcurl fork | real `curl` CLI (upstream `src/tool_*.c`) |
| **wget** | DONE | our 174-line `wget.c` (dropped) | real GNU Wget vs our sysroot — stub `getrlimit`/`getgroups`, then build |
| **http-get** | DONE | our 95-line `http_get.c` | dropped; real curl covers HTTP fetches |
| **git** | TODO | our hand-rolled git from `sha1`+`flate2` | **real git** (upstream C), patched for WASI — **NOT gitoxide** |
| **git** | DONE | our hand-rolled git from `sha1`+`flate2` | **real git** (upstream C), patched for WASI — **NOT gitoxide** |
| **fd** | DONE | our `secureexec-fd` on raw `regex` (not sharkdp/fd) | real **fd** (sharkdp) |
| **findutils** (`find`,`xargs`) | TODO | our hand-rolled on `regex`/shims | real GNU findutils, or `uutils/findutils` |
| **tree** | DONE | our hand-rolled, zero deps | real `tree`, or an established one |
Expand Down Expand Up @@ -165,6 +165,23 @@ ripgrep crates); **(b) gnulib `getrlimit`/`getgroups`** → sysroot stubs, alrea
documented for wget (item #8) (hits GNU grep/findutils). Subprocess spawn already
works (`wasi-spawn` broker), so `xargs` is not a blocker.

- **git — DONE.** Replaced the custom Rust `sha1`+`flate2` implementation with real
upstream Git 2.55.0 built by the C toolchain and staged as `git` plus helper
command aliases. The WASI changes stayed below Git's behavior surface:
`run-command.c` uses `posix_spawn` so helper subprocesses go through the existing
wasi-spawn broker, the sysroot exposes Git's missing C compatibility surface, and
the runner now allocates synthetic fds in a high range so managed pipe/file fds
cannot collide with delegate-opened WASI fds. Smart HTTP remains intentionally
disabled in this build (`NO_CURL`), so HTTPS clone fails with the real Git helper
error instead of a custom transport. Proof: clean upstream Git rebuild passes in
`2026-07-08T11-28-00-0700-git-clean-rebuild-after-high-synthetic-fd.log`;
package build stages 6 commands in
`2026-07-08T11-33-00-0700-git-package-build-clean-binary-after-install.log`;
native sidecar rebuild passes in
`2026-07-08T11-34-00-0700-sidecar-rebuild-after-git-clean-package.log`;
full Git e2e passes 18/18 in
`2026-07-08T11-51-00-0700-git-full-e2e-high-synthetic-fd-clean-binary-after-test-fix.log`.
Rev: `tmvlxlvk` — `fix(git): build upstream git`.
- **sqlite3 CLI — DONE.** Engine was already the real amalgamation
(`libs/sqlite3/sqlite3.c`); the command now builds the official upstream
`shell.c` from the same fetched zip as `sqlite3`. The local 558-line
Expand Down
Binary file modified packages/runtime-core/commands/git
Binary file not shown.
Binary file added packages/runtime-core/commands/git-receive-pack
Binary file not shown.
Binary file modified packages/runtime-core/commands/git-remote-http
Binary file not shown.
Binary file modified packages/runtime-core/commands/git-remote-https
Binary file not shown.
Binary file added packages/runtime-core/commands/git-upload-archive
Binary file not shown.
Binary file added packages/runtime-core/commands/git-upload-pack
Binary file not shown.
7 changes: 5 additions & 2 deletions software/git/agentos-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
],
"aliases": {
"git-remote-http": "git",
"git-remote-https": "git"
"git-remote-https": "git",
"git-upload-pack": "git",
"git-receive-pack": "git",
"git-upload-archive": "git"
},
"registry": {
"title": "git",
"description": "Git version control (clone, commit, push, pull).",
"description": "Upstream Git version control for AgentOS VMs.",
"priority": 90,
"image": "/images/registry/git.svg",
"category": "developer-tools"
Expand Down
Binary file modified software/git/bin/git
Binary file not shown.
Binary file modified software/git/bin/git-remote-http
Binary file not shown.
Binary file modified software/git/bin/git-remote-https
Binary file not shown.
14 changes: 0 additions & 14 deletions software/git/native/crates/cmd-git/Cargo.toml

This file was deleted.

Loading