shell/ compatibility extension — a DROPPABLE classpath plug-in (drop the
jar, drop the feature). Bound only when the user-owned shell toggle is ON
(default ON; flip it OFF in Settings or in vis.yml via toggles: {shell: false}
to drop the tools). The OS process jail is the containment layer while active.
ONE model-facing tool — shell — bound BARE in the flat Python sandbox
next to git / cat / grep. wait says how long the CALLER waits, and it
is the only difference there has ever been between a "run" and a "background"
shell, so there is one verb with one schema and one result shape. It is ONE
budget for the whole ordered batch, never one per command: three commands under
a 5s wait answer in 5 seconds, not in 15.
await shell(["ls"]) — bash -lc in the workspace root, waited for up
to 120s. Output is bounded at READ time to a head+tail budget per stream, so
only the MIDDLE of a huge stream is dropped, never its start or end (a
chatty-then-killed command cannot balloon the heap). A non-zero exit is DATA
the model reads, not an error.
await shell(["npm run dev"], {"wait": 0, "id": "dev"}) — the wait
that expires immediately: spawned under a REAL pty, its merged output
streamed verbatim to a log FILE, registered as a session RESOURCE, and
answered in the same run shape with timed_out true. Prefer it for servers,
watchers, long builds and interactive commands. Re-issuing a LIVE id returns
that shell instead of a second copy of it.
A run that outstays a non-zero wait is not lost either: the process keeps running under the id the call already answered with, and the commands after it stay unstarted — the batch is ordered, so nothing later can have run.
shell_logs / shell_type / shell_stop are bare sandbox verbs, NOT native
tools: they operate on a handle the caller already holds, which is control flow,
and control flow belongs in a bounded python_execution loop that can break on
what it actually read. await shell_logs("dev") reads the log from a byte
OFFSET and returns NOW, await shell_type("dev", "y") types into the pty,
await shell_stop("dev") kills the tree. NOTHING blocks on the model's behalf.
shell-dispatch survives as the INTERNAL grammar the Python-extension entry
points use, since those hand-author an options map and genuinely need an op.
Every stage answers a stage-SCOPED total key set ([[result-core]] plus that one
stage's own keys): a key the stage owns is nil / false / 0 / [] instead of
absent, so model Python indexes it without a KeyError, while another stage's
keys are simply not there to carry nothing. A run always has one result entry
per command under commands, holding that command line and its bytes.
The shell toggle is registered HERE, extension-owned under the vis namespace.
`shell/` compatibility extension — a DROPPABLE classpath plug-in (drop the
jar, drop the feature). Bound only when the user-owned `shell` toggle is ON
(default ON; flip it OFF in Settings or in `vis.yml` via `toggles: {shell: false}`
to drop the tools). The OS process jail is the containment layer while active.
ONE model-facing tool — `shell` — bound BARE in the flat Python sandbox
next to `git` / `cat` / `grep`. `wait` says how long the CALLER waits, and it
is the only difference there has ever been between a "run" and a "background"
shell, so there is one verb with one schema and one result shape. It is ONE
budget for the whole ordered batch, never one per command: three commands under
a 5s wait answer in 5 seconds, not in 15.
1. `await shell(["ls"])` — `bash -lc` in the workspace root, waited for up
to 120s. Output is bounded at READ time to a head+tail budget per stream, so
only the MIDDLE of a huge stream is dropped, never its start or end (a
chatty-then-killed command cannot balloon the heap). A non-zero exit is DATA
the model reads, not an error.
2. `await shell(["npm run dev"], {"wait": 0, "id": "dev"})` — the wait
that expires immediately: spawned under a REAL pty, its merged output
streamed verbatim to a log FILE, registered as a session RESOURCE, and
answered in the same run shape with `timed_out` true. Prefer it for servers,
watchers, long builds and interactive commands. Re-issuing a LIVE id returns
that shell instead of a second copy of it.
3. A run that outstays a non-zero wait is not lost either: the process keeps
running under the id the call already answered with, and the commands after
it stay unstarted — the batch is ordered, so nothing later can have run.
`shell_logs` / `shell_type` / `shell_stop` are bare sandbox verbs, NOT native
tools: they operate on a handle the caller already holds, which is control flow,
and control flow belongs in a bounded `python_execution` loop that can break on
what it actually read. `await shell_logs("dev")` reads the log from a byte
OFFSET and returns NOW, `await shell_type("dev", "y")` types into the pty,
`await shell_stop("dev")` kills the tree. NOTHING blocks on the model's behalf.
`shell-dispatch` survives as the INTERNAL grammar the Python-extension entry
points use, since those hand-author an options map and genuinely need an `op`.
Every stage answers a stage-SCOPED total key set ([[result-core]] plus that one
stage's own keys): a key the stage owns is nil / false / 0 / [] instead of
absent, so model Python indexes it without a KeyError, while another stage's
keys are simply not there to carry nothing. A run always has one result entry
per command under `commands`, holding that command line and its bytes.
The `shell` toggle is registered HERE, extension-owned under the vis namespace.(jailed-shell env opts)Run vis.jailed_shell through a strict policy read from the latest merged
on-disk configuration at every process spawn. Works with or without a session;
invalid current config refuses that spawn instead of using a snapshot.
Run `vis.jailed_shell` through a strict policy read from the latest merged on-disk configuration at every process spawn. Works with or without a session; invalid current config refuses that spawn instead of using a snapshot.
(run-argv env argv)(run-argv env argv opts)Run ONE literal argv through the SAME bounded machinery shell runs its own
commands with: cwd authorization, process-jail policy, head+tail capped
capture, timeout and kill-tree. Returns that command's own total entry — the
SAME command-result map (command, stdout, stderr, exit, duration_ms,
timed_out, *_omitted_chars) shell puts under commands, carrying the
request's :dir/:timeout-secs as metadata, so there is one command shape for
both tools and no envelope to unwrap.
No shell is involved — each element reaches the process verbatim, so nothing
needs quoting. The git tool is a USER of this: every git command is a
bounded shell command, so both tools share one runner, one jail and one
capture policy.
Run ONE literal argv through the SAME bounded machinery `shell` runs its own commands with: cwd authorization, process-jail policy, head+tail capped capture, timeout and kill-tree. Returns that command's own total entry — the SAME `command-result` map (`command`, `stdout`, `stderr`, `exit`, `duration_ms`, `timed_out`, `*_omitted_chars`) `shell` puts under `commands`, carrying the request's `:dir`/`:timeout-secs` as metadata, so there is one command shape for both tools and no envelope to unwrap. No shell is involved — each element reaches the process verbatim, so nothing needs quoting. The `git` tool is a USER of this: every git command is a bounded shell command, so both tools share one runner, one jail and one capture policy.
(session-jailed-shell env opts)Run vis.jailed_shell_session through the invoking session's immutable jail
snapshot. Requires a live session and never re-reads configuration.
Run `vis.jailed_shell_session` through the invoking session's immutable jail snapshot. Requires a live session and never re-reads configuration.
(shell commands)(shell commands opts)await shell(["git status"]) — run bash lines in order and return their
output. wait is how long the CALLER waits (default 120s): the call returns
EARLY with the exit code when the command finishes first, and wait=0 returns
at once with the shell running under a pty. Either way the result carries an
id, so nothing is ever lost to a deadline.
`await shell(["git status"])` — run bash lines in order and return their output. `wait` is how long the CALLER waits (default 120s): the call returns EARLY with the exit code when the command finishes first, and `wait=0` returns at once with the shell running under a pty. Either way the result carries an `id`, so nothing is ever lost to a deadline.
(shell-attach-command _parsed residual)vis-agent extension shell attach <id> — the human-side passthrough: join a live
background shell's PTY in your OWN terminal (finish a browser OAuth, answer a
prompt only a person can), then Ctrl-] to detach with the child untouched.
--socket PATH targets an explicit socket; otherwise the newest shell whose
id matches. Returns the attach exit code.
`vis-agent extension shell attach <id>` — the human-side passthrough: join a live background shell's PTY in your OWN terminal (finish a browser OAuth, answer a prompt only a person can), then Ctrl-] to detach with the child untouched. `--socket PATH` targets an explicit socket; otherwise the newest shell whose id matches. Returns the attach exit code.
CLI surface mounted under vis-agent extension shell. Only attach for now — the human
passthrough onto a background PTY the agent spawned.
CLI surface mounted under `vis-agent extension shell`. Only `attach` for now — the human passthrough onto a background PTY the agent spawned.
(shell-dispatch env opts)INTERNAL shell lifecycle grammar, kept for the Python-extension entry points
(trusted-extension-shell, jailed-shell, session-jailed-shell) whose
caller authors an options map by hand and therefore genuinely needs an op
discriminator. The MODEL never reaches this: it calls shell — one tool
whose wait decides whether it blocks — and the bare verbs shell_logs,
shell_type and shell_stop on the id it already holds, so one schema with
five mutually-exclusive shapes is never presented as a contract to
disambiguate.
INTERNAL shell lifecycle grammar, kept for the Python-extension entry points (`trusted-extension-shell`, `jailed-shell`, `session-jailed-shell`) whose caller authors an options map by hand and therefore genuinely needs an `op` discriminator. The MODEL never reaches this: it calls `shell` — one tool whose `wait` decides whether it blocks — and the bare verbs `shell_logs`, `shell_type` and `shell_stop` on the id it already holds, so one schema with five mutually-exclusive shapes is never presented as a contract to disambiguate.
(shell-logs id)(shell-logs id opts)await shell_logs("dev") — read a background shell's log from a byte offset
and return NOW. Nothing blocks on your behalf: a wait is a bounded loop you
write in python_execution and break on what you actually read.
`await shell_logs("dev")` — read a background shell's log from a byte offset
and return NOW. Nothing blocks on your behalf: a wait is a bounded loop you
write in `python_execution` and break on what you actually read.(shell-stop id)await shell_stop("dev") — kill the process tree and drop the retained logs and
session resource.
`await shell_stop("dev")` — kill the process tree and drop the retained logs and
session resource.(shell-type id text)(shell-type id text opts)await shell_type("dev", "y") — type keystrokes at a background shell's stdin.
is_enter (default true) submits the line, which is what an interactive prompt
waits for; read the response with shell_logs.
`await shell_type("dev", "y")` — type keystrokes at a background shell's stdin.
`is_enter` (default true) submits the line, which is what an interactive prompt
waits for; read the response with `shell_logs`.(trusted-extension-shell env opts)Run a trusted Python extension's ordinary vis.shell request without applying
a process jail. The extension context already has direct, unrestricted
subprocess access; this is the result-shaped convenience API for the same trust
boundary. Foreground calls therefore work outside a session too.
Run a trusted Python extension's ordinary `vis.shell` request without applying a process jail. The extension context already has direct, unrestricted subprocess access; this is the result-shaped convenience API for the same trust boundary. Foreground calls therefore work outside a session too.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |