Callback, override only the hooks for the moments you care about, and pass instances to SmolVM(..., callbacks=[...]).
There are three hooks:
on_pre_run— runs before a command reaches the guest. This is the only hook that can block a command.on_post_run— runs after a command finishes successfully.on_run_error— runs when a command fails to execute.
ctx in the examples below. This is a run context — a single object that describes the command in flight: what was run, on which sandbox, and (afterwards) its result. You read from it to decide what to do. The run context section lists every field; for now, just know that ctx.command is the command string and ctx.vm_id is the sandbox ID.
When to use callbacks
- Block unsafe commands — Stop destructive commands like
rm -rf /before they reach the guest. - Audit and log — Record every command an agent runs, along with its exit code and output.
- Observe failures — Collect telemetry when a command errors out, without changing the rest of your code.
Block unsafe commands before they run
The pre-run hook is the only hook that can stop a command. Ifon_pre_run raises, the command is aborted and the exception is raised to the caller. Raise CommandBlockedError for an explicit, typed block.
run() call uses the same connection.
Example: Block prompt injection with a classifier
You can use the sameon_pre_run hook to plug in an ML classifier and block commands that look like prompt injection or jailbreak attempts. This is useful when an LLM-driven agent generates shell commands from untrusted input (web pages, user messages, tool output) and you want to stop a malicious instruction before it ever reaches the sandbox.
The example below uses axiotic/ogma-prompt-injection, a binary classifier that labels text as benign or malicious. The callback loads the model once, scores each command in on_pre_run, and raises CommandBlockedError when the score crosses a threshold.
- Threshold — Raise
threshold(closer to1.0) to reduce false positives, lower it to be stricter. - Block labels — The example accepts both
maliciousandlabel_1because different model versions emit different label names. Adjust the set if you swap models. - Where to load the model — Load the pipeline once at startup, not inside the hook. The hook runs on every
vm.run()call.
Log every command an agent runs
The post-run hook fires after a command completes. The context object carries the result, so you can log the exit code, stdout, and stderr.on_post_run and on_run_error are passive observers. If they raise, the exception is logged and swallowed so a buggy logger never breaks a command that already ran.
Attach callbacks to an existing sandbox
You can also attach callbacks to a sandbox you have already created.add_callback() returns the sandbox so calls can be chained.
Read command details from the run context
Every hook receives a singleRunContext object. Read from it to decide what to do.
For the full API, see the Callback reference.
Scope and limitations
Callbacks fire around the synchronousSmolVM.run() method. They cover both the SSH and vsock transports, because the hooks run on the facade before any transport is selected.
This first release intentionally covers command hooks only. Lifecycle hooks (start, stop, snapshot), file-transfer hooks, and the async run() path are not wired up yet.