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.
FIVE model-facing bindings — shell_run, shell_background, shell_logs,
shell_type, shell_stop — bound BARE in the flat Python sandbox next to
git / cat / grep. One tool is one verb with one satisfiable schema and
one result shape: a single op-discriminated tool made every precondition
conditional prose, and the model discovered the contract by failing a call.
await shell_run(["ls"]) — bash -lc in the workspace root, bounded by an
internal deadline. 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_background(["npm run dev"], id="dev") — spawned under a REAL
pty, its merged output pumped into a bounded ring buffer, registered as a
session RESOURCE. Prefer this for long builds, test suites, servers,
watchers, and interactive commands; reserve shell_run for short bounded
work.
await shell_logs("dev") snapshots the ring buffer 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: waiting is control
flow, and control flow belongs in a bounded python_execution loop that can
break on what it actually read.
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.
FIVE model-facing bindings — `shell_run`, `shell_background`, `shell_logs`,
`shell_type`, `shell_stop` — bound BARE in the flat Python sandbox next to
`git` / `cat` / `grep`. One tool is one verb with one satisfiable schema and
one result shape: a single `op`-discriminated tool made every precondition
conditional prose, and the model discovered the contract by failing a call.
1. `await shell_run(["ls"])` — `bash -lc` in the workspace root, bounded by an
internal deadline. 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_background(["npm run dev"], id="dev")` — spawned under a REAL
pty, its merged output pumped into a bounded ring buffer, registered as a
session RESOURCE. Prefer this for long builds, test suites, servers,
watchers, and interactive commands; reserve `shell_run` for short bounded
work.
3. `await shell_logs("dev")` snapshots the ring buffer 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: waiting is control
flow, and control flow belongs in a bounded `python_execution` loop that can
break on what it actually read.
`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-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.
(shell-background commands)(shell-background commands opts)await shell_background(["npm run dev"], id="dev") — spawn a PTY under id
and return immediately. Re-issuing the same script returns the LIVE shell instead
of spawning a second one. Watch it with shell_logs.
`await shell_background(["npm run dev"], id="dev")` — spawn a PTY under `id` and return immediately. Re-issuing the same script returns the LIVE shell instead of spawning a second one. Watch it with `shell_logs`.
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_run,
shell_background, shell_logs, shell_type or shell_stop, each with its
own satisfiable schema, 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_run`, `shell_background`, `shell_logs`, `shell_type` or `shell_stop`, each with its own satisfiable schema, 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") — snapshot a background shell's retained output 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")` — snapshot a background shell's retained output 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-run commands)(shell-run commands opts)await shell_run(["git status"]) — run bash lines in order and return their
output. Bounded by an internal deadline so a foreground call cannot hang the
turn; long or interactive work is shell_background.
`await shell_run(["git status"])` — run bash lines in order and return their output. Bounded by an internal deadline so a foreground call cannot hang the turn; long or interactive work is `shell_background`.
(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 |