Problem
Registered bindings produce their kernel command stubs in the VM, but executing one fails with exec-time ENOENT — the shell reports command not found, process.exec returns 127 — so an agent can never invoke a binding. This breaks the core bindings flow (the crash-course example shape, passed via bindings: [group]).
The stubs demonstrably exist and are executable:
$ which agentos-weather → /bin/agentos-weather
$ ls /bin/agentos* → /bin/agentos /bin/agentos-weather
$ head -c 40 /bin/agentos-weather → #!/bin/sh
# kernel command stub
$ ls -la /bin/agentos-weather → -rwxr-xr-x 1 0 0 32 …
$ agentos-weather forecast --city Paris → error: command not found: agentos-weather
sh maps a missing-interpreter/registration exec ENOENT to command not found — the stub file resolves, its execution doesn't. (Side note: the stubs land in /bin, while the bindings docs say /usr/local/bin/agentos-{name}.)
Reproduced on 0.2.15 and 0.2.16-rc.1, macOS arm64, Node 25, on both the agentOS() actor path and the Core path (AgentOs.create()), with explicit full-allow permissions including binding: 'allow'. Config forwarding was ruled out: bindings is in agentOsOptionKeys, ensureVm spreads it into AgentOs.create, and collectBindingBootstrapCommands emits agentos + agentos-<group> into bootstrapCommands.
Reproduction
import { AgentOs } from '@rivet-dev/agentos';
import { z } from 'zod';
const group = {
name: 'weather',
description: 'Weather data bindings',
bindings: {
forecast: { description: 'Get a forecast', inputSchema: z.object({ city: z.string() }).strict(), execute: async () => ({ temperature: 22 }) },
},
};
const vm = await AgentOs.create({
bindings: [group],
permissions: { fs: 'allow', childProcess: 'allow', process: 'allow', env: 'allow', binding: 'allow', network: 'deny' } as never,
});
const r = await vm.process.exec('agentos-weather forecast --city Paris', { captureStdio: true });
console.log(r.exitCode); // 127 — expected: 0 with {"ok":true,"result":{...}} on stdout
Expected behavior
- Executing a registered binding stub dispatches to the host
execute() and returns its JSON envelope (exit 0), per the bindings docs.
- If registration requires something beyond
bindings: [...] + binding: 'allow', the failure should be loud at VM creation, not ENOENT at call time inside the agent's turn.
- Docs and implementation agree on the shim path (
/bin vs /usr/local/bin).
Problem
Registered bindings produce their kernel command stubs in the VM, but executing one fails with exec-time ENOENT — the shell reports
command not found,process.execreturns 127 — so an agent can never invoke a binding. This breaks the core bindings flow (the crash-course example shape, passed viabindings: [group]).The stubs demonstrably exist and are executable:
shmaps a missing-interpreter/registration exec ENOENT tocommand not found— the stub file resolves, its execution doesn't. (Side note: the stubs land in/bin, while the bindings docs say/usr/local/bin/agentos-{name}.)Reproduced on
0.2.15and0.2.16-rc.1, macOS arm64, Node 25, on both theagentOS()actor path and the Core path (AgentOs.create()), with explicit full-allow permissions includingbinding: 'allow'. Config forwarding was ruled out:bindingsis inagentOsOptionKeys,ensureVmspreads it intoAgentOs.create, andcollectBindingBootstrapCommandsemitsagentos+agentos-<group>intobootstrapCommands.Reproduction
Expected behavior
execute()and returns its JSON envelope (exit 0), per the bindings docs.bindings: [...]+binding: 'allow', the failure should be loud at VM creation, not ENOENT at call time inside the agent's turn./binvs/usr/local/bin).