Repository navigation
Conversation
Workspace Trust is off in a tab, so a setting without a scope was read from the folder's .vscode/settings.json as if the user wrote it. Measured in the real workbench: levelcode.cloud.endpoint, levelcode.ai.baseURL, agent.autopilot, verify.command and commandTimeout all came back as the folder's values, and the session token goes to whatever https host the endpoint names. The browser manifest now makes every setting application-scoped except a short, double-entered list of display preferences. The desktop manifest is untouched.
runAgent built the system prompt as (skills ? buildSystem(menu) : SYSTEM_BASE). buildSystem already answers a run with no menu with the HOST's base prompt, but the fallback bypassed it: in the browser build, with levelcode.ai.skills.enabled off, the model was told about run_command, background:true and 'EVERY run_command' while run_command itself was withheld. No effect on the desktop (SYSTEM_BASE_HOST is SYSTEM_BASE there).
…and a shell's file changes through the review The browser build's shell is another extension (levelcode.levelcode-shell), activated on first need. host.js gets spawn / shellInfo / registerShell / connectShell, and caps.shell becomes a getter. agent.js reads the answer once per run, so the prompt, the tool list and the verify step agree for the whole run; the three load-time constants that baked the capability in become per-run. run_command is offered as what it is (no background argument, no read_command_output), refused with an instructive message when a model asks for background anyway, and its file changes are handed to shellCommit.js, which sends them through the same applyEdit / applyDelete as edit_file. The desktop path is unchanged: the same spawn options, the whole tool list, the same prompt (pinned by shellSeam.test.js; agentRunCommand.test.js is untouched).
The tool result was (output + status).slice(0, 8000): for long output the end — where the failure and the '[exit 1]' line are — was exactly what got cut, so the model could not tell a command had failed. Output that fits is returned byte for byte as before; longer output keeps a quarter from the start and the rest from the end, with the gap said out loud, and the status line is never what is cut. DESKTOP-VISIBLE behaviour change (outputs over 8000 characters only), in its own commit so it can be dropped on its own.
…the card has room for The registry that read_command_output serves (ring, byte count, port and readiness sniffing, preview) was fed from the chunk callback that feeds the live card, and that stops at the card's 100000-character cap: after that a server's 'listening on :4000' left port=null, ready=false and totalBytes frozen. It is fed by its own callback now, after the card's, so posts keep their order. DESKTOP-VISIBLE behaviour change (output past 100000 characters), in its own commit.
…mpt says what the shell is shellCommit.js is rewritten around one rule. A command's change set is validated as a WHOLE and applied only if every change is acceptable; there is no unreviewed path (the first draft wrote 'build output' and new binaries straight to disk, which is a way round everything edit_file refuses). Refused whole: outside the folders; .git, .vscode, .levelcode, CI workflows and instruction files (AGENTS.md, CLAUDE.md, .cursorrules); not plain UTF-8 text, a BOM, over 1 MiB; line endings the review would rewrite; unsaved editor changes; a file that changed on disk since the command read it; case-only renames; over 300 files. A review call that throws is reported, never swallowed, and the command does not exit 0. host.connectShell no longer caches a failure: an early return ran the finally block before the promise was stored, so every later run got a settled stale answer. It is bounded (10 s) and asks again next run. The shell prompt block is rewritten: the program list is the shell's own, what exists and what does not, the limits and the exit codes, that a stopped command changes nothing, what is refused, and that tool output is data. Stop/limit/timeout apply nothing (the staged writes are discarded), so the kill semantics in host.js say so.
Each SKILL.md in the browser build gets one preamble: the shell is a simulated bash with no git, npm, node, python or test runner and cannot run the code, so the steps that say to run them are skipped, no result is claimed for them, and the summary says which checks were not run. The steps and the menu entries are unchanged; the desktop's files are untouched. Also: the verify.command example means what it says (`! grep -rn TODO src`).
…dependently verified)
LevelCode Shell stage 1 — a JavaScript bash over vscode.workspace.fs, for the visitor's terminal and the
agent's run_command; no programs, no network.
engine/ just-bash 3.6.0 vendored unmodified (sha256 pinned) and patched in memory at build time
(anchor-checked); a copy-on-write overlay over a read-only RPC; compat wrappers; limits; the
worker entry. 535 golden cases recorded from GNU bash 5.1 / coreutils 8.32.
src/ runtime (engine worker lifecycle, read-only fs RPC, sessions, the agent's ChildLike, commit),
commit (the visitor's terminal), info, sanitize, extension.
src/terminal/ Pseudoterminal: input parser, line editor (checked against real xterm.js), completeness,
output allow-list filter, history, prompt, agent tab, shell integration.
Known defects found by the verifiers are fixed in the commits that follow.
…s the model's label Approval and command cards show the command and its explanation through host.displaySafe: the desktop's identity, and in a tab the shell's forDisplay, which makes bidi overrides, zero-width and other invisible characters visible so that what is approved is what runs. The shell is still handed the real string. SpawnOptions carries the model's label (at most 300 characters) for the tab that mirrors the agent's commands.
…re-verified; docs, README, e2e-shell Each role's findings were fixed with tests that failed before, then attacked again by an independent verifier. Known open findings are in the round-2 reports and are fixed in the next commit. agent.js: the review's commit hook honours the shell's cancelled(); extension.js: agent.forDisplay.
… is still open) Engine: 25 patches, cp/mv trailing-slash regression and the redirect content-write regression fixed and pinned at run level, printf %(..)T zone, grep BRE, du limit, sed labels, 682 golden cases against GNU 5.1. Runtime: planMounts is the engine's own algorithm, safeMessage, one bounded gate for the commit, Stop settles at once, resolve/stat. agent.js: the review stops between changes after a Stop, and the prompt names RE2, UTC and /tmp.
…t scales, the review's give-up cancels, /tmp folders counted)
…ize, joiners kept, linear regexes); README for the commit/Stop/mount contract; e2e-shell: height resize, emoji paste
… README to the real contract; CI sets LEVELCODE_REQUIRE_WORKER; suites that exit silently fail
…r happens (exit guard)
…pe.Unknown, a link to nothing); links are made to files only
…wners left them; engine-compat and engine-patches suites are mid-edit
…r every step, the lowest redraw over every count of dropped rows), links accept only a file, measured wording in README/NOTES
…ommand, relative dates, diff -r, option tables against GNU getopt, xargs -x -s, sort -k signs, TZ as a zoneinfo path)
…en cases, 371 runtime mutations)
…faults inside $(( )), clustered value options, for x; do, --, stdin as -, strftime flags, find -exec status and time tests, diff quoting, 11 more patches); 0 regressions over 30,270 differential units
…ry between touch and date (the third figure is rounded with an hour of slack)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LevelCode Shell — stage 1 of running things in the tab
A terminal panel and the agent's
run_commandin the browser edition, with no programs and no network: a bashinterpreter in JavaScript (the vendored, patched
just-bash3.6.0,Apache-2.0) over
vscode.workspace.fs. It parses bash and runs 82 coreutils-like commands (ls cat grep sed awk jq find sort uniq xargs diff tree …).npm testis "command not found", 127, and the agent is told so in its prompt.Stacked on #106 (the look), which is stacked on #105 (the edition). Draft. Nothing changes for the desktop app: the
commits that touch shared
extensions/levelcode-aicode are separable (host.jsseam,agent.js,shellCommit.js),and the desktop path is byte-for-byte what it was (pinned by
shellSeam.test.js;agentRunCommand.test.jsuntouched).What a visitor gets
folder and exported variables, history, Tab completion, Ctrl+C that really stops a runaway loop (the engine runs in a
nested Worker and is
terminate()d), andpath:line:colin output is a link to the file at that line.tab, and what they change goes through the same review as
edit_file: Keep and Undo.The safety rules (what to look at in review)
set, everything reviewed or the whole command refused (
extensions/levelcode-ai/shellCommit.jsfor theagent,
web/shell/src/commit.jsfor the visitor's own terminal): outside the folders, protected paths (.git .vscode .levelcode .cursor .husky .github/workflows AGENTS.md CLAUDE.md …), binary/BOM/>1 MiB, line-ending changes, dirtybuffers, changed on disk, case-only renames, >300 files — one refusal and none of the command's changes are
applied. A stopped, timed-out or limit-hit command applies nothing. The commit is bounded in time and a Stop ends
it at once.
allow-list filter and a card only through
forDisplay; replies xterm would type into the prompt are swallowed;OSC 633 only from one module with a per-terminal nonce.
levelcode.*setting isapplication-scopedexcept an explicit display allow-list — a folder's
.vscode/settings.jsoncan no longer redirect the session tokenor turn Autopilot on (found while reviewing this, measured in the real workbench).
Verification — what was run, and what was not
./scripts/test-extensions.sh, 117 files): macOS Node 24 with the real xterm.js 6.1 (headless) as theoracle for the line editor and
LEVELCODE_REQUIRE_XTERM=1/LEVELCODE_REQUIRE_WORKER=1; macOS Node 18.20.5; and, on anearlier commit of this branch, Linux Node 24 and 18 in docker (the Docker engine became unavailable before the last
engine commit, so those two were not re-run on it). The runtime suite runs the real engine in a worker thread;
CI sets
LEVELCODE_REQUIRE_WORKER=1.listed with a reason in a shrink-only table), 1,778 cp/mv operand shapes, 568 xargs groups, 596 time zones; a seeded
change-set consistency fixture; 4,600 fuzz scripts under three process time zones. Final independent corpus:
30,270 differential units, 0 regressions against the last verified checkpoint, 207 wrong-to-right.
Linux Node 18; engine mutants for every patch and wrapper branch.
regressions that every suite passed (
cp -r d d2/,xargsover 16k files, a swallowedteeerror, a check budgetscaled without the write budget); each has a red-before-fix test now.
web/test/e2e-shell.mjs, 38 checks, 39 with the extension host on its own origin): lazyactivation, a terminal from the palette, typing, cd, 127, Ctrl+C on
sleep 60(~0.3 s), a link click opening thefile at
Ln 2, Col 3, pasted multi-line buffers through width, height and width-and-height resizes, an emoji name,hostile output that does not type into the prompt or rename the terminal,
run_commandoffered withoutbackground, a file written through the review with Keep/Undo, a.vscodewrite refused whole, a timeout thatapplies nothing, the mirror tab.
e2e.mjs --dist(now assertsrun_commandis offered),e2e-look.mjs,e2e-signin-tab.mjs,e2e-byok.mjsgreen.a provider call that hangs behind a permission prompt; long sessions and 2,000-file commands in a real tab.
Known differences from a real shell (all in
web/shell/engine/NOTES.md§4–§5, and the model is told the main ones)grep -rdoes not enter.git; back-references insed/grep -Efail closed (RE2); there is one file time(
-atime/-ctimeread the modification time);date -dtakes a defined subset of GNU's relative grammar; aredirection writes to the path when it ends, not an open descriptor; resizing a terminal while a multi-line command
is pending can leave a stale copy of the prompt (never lose earlier output in 33,600 measured sessions; the rate and
method are in
src/terminal/NOTES.md).Owner decisions
.github/workflowsand CI files areever allowed.
connect-src 'self'and, for threads, COOP/COEP on the editor origin. Stage 3 (real Node/npm) needsWebContainers or BrowserPod: not open source, needs a StackBlitz agreement. Neither is started.
onetime.Layout of the diff
web/shell/(engine, runtime, terminal;README.mdis the contract,engine/NOTES.mdandsrc/terminal/NOTES.mdgo deeper),
extensions/levelcode-ai(the seam:host.js,agent.js,shellCommit.js),web/ai-extension(manifest scoping, skills, copy),
scripts/build-web.mjs,web/lib/config.mjs,docs/WEB.md,CLAUDE.md, testsand fixtures. The engine is ~1.5 MB raw / ~445 KB gzip, fetched only when the shell is first used.