Skip to content

feat(sandbox-code-runner): run Node and Python in iii-sandbox microVMs, with the iii SDK inside the guest - #728

Merged
andersonleal merged 11 commits into
mainfrom
feat/code-runner
Aug 6, 2026
Merged

feat(sandbox-code-runner): run Node and Python in iii-sandbox microVMs, with the iii SDK inside the guest#728
andersonleal merged 11 commits into
mainfrom
feat/code-runner

Conversation

@andersonleal

@andersonleal andersonleal commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Adds sandbox-code-runner, a worker that runs Node.js and Python inside iii-sandbox
microVMs, and gives the code running in those VMs a real iii SDK client.

Related to MOT-3971 ("Run code in a sandboxed environment"). Not a duplicate, and
this PR does not close it: MOT-3971's technical notes describe eval::* hosted on the
shell worker
, session-keyed on fs_scope.root, with a warm pool and no guest network.
This is a standalone worker with a different surface and no session binding. It does
deliver the "later stage" that ticket anticipates — "lets the running code call other iii
functions directly"
. Happy to retarget this at a dedicated ticket if you'd prefer one.

What's here

The workersandbox-code-runner::run, ::register_function, ::teardown.
run is one-shot by default (boot, run, destroy, no runtime_id back); keep: true
mints a runtime you own; passing runtime_id reuses that VM's filesystem.
register_function publishes a bus function whose handler executes in a persistent
runtime, one per (namespace, lang). It executes nothing itself — every run and every
handler call becomes sandbox::* calls over the bus.

The guest iii global — evaluated code and registered handlers get the real
iii-sdk client, lazily connected (nothing dials
the engine until first use, so code that never touches iii pays nothing). Node runtimes
get the SDK planted from a bundle embedded in the binary — no registry, works offline;
Python runtimes pip install iii-sdk at creation, degrading with a clear first-use error
if PyPI is unreachable. Two semantics worth knowing: SDK-side registerFunction
registrations are ephemeral (they die with the guest process — use
sandbox-code-runner::register_function via iii.trigger to persist), and a handler that
triggers a function on its own runtime stalls on that runtime's one-exec-at-a-time slot.

Runtimes are networked. The guest's engine link rides the sandbox gateway, so
npm install / pip install work everywhere. Guest calls carry the guest's own worker
identity — the same trust model as a worker process you run yourself. That is documented
plainly in the worker README's "Identity and reach".

Console UI — purpose-built cards for the three ops, with runtime_id redacted at every
raw display exit (a runtime_id is a capability: it can eval into or tear down that VM).

Verification

  • 148 tests (116 lib, 26 runner, 4 schema-golden, manifest, gated e2e), cargo fmt --check
    and cargo clippy --all-features clean.
  • Live e2e against real microVMs (SANDBOX_CODE_RUNNER_E2E=1, ~10s): one-shot/keep/reuse,
    register + bus invocation, teardown, guest iii.trigger in both languages (the Python path
    performs a real pip install in the VM), ephemeral-vs-persistent registration, and an
    error-UX guard.
  • Every wire surface is pinned by committed golden files; guest scripts are golden-pinned
    byte-for-byte.

Naming

The worker was code-runner with a ::eval function until this branch; the rename to
sandbox-code-runner / ::run is in here as five reviewed commits. Guest filenames were
realigned so each matches the function it serves — the handler runner is invoke.{mjs,py},
the run wrapper is run.{mjs,py}. Note for future refactors: RUN_MJS/RUN_PY
previously named the handler runner and now name the wrapper, so a naive find-and-replace
across those constants can cross the two.

Deliberately not renamed: the CodeRunner* / CODE_RUNNER_* Rust identifiers (~110
occurrences, crate is publish = false, zero blast radius) and bare English "eval" in test
identifiers. The TypeScript twin was renamed, so those layers disagree on purpose.

Also in this PR

feat(console): redact worker-declared secrets at every raw display exit — the shared
redaction contract this worker's cards depend on. It is a separate first commit and is
useful independently (node-engine's runtime_id needs it too).

Heads-up

  • Conflicts with main in .github/workflows/create-tag.yml and release.yml — both are
    the worker-list / tag-pattern edits this branch made, against entries main added since.
    Mechanical to resolve; say the word and I'll merge main in and re-run the suite and e2e.
  • Upstream bug found while building this (iii-sandbox, unreported): guest
    sandbox::fs::writes persist into the shared rootfs cache
    (~/.iii/cache/docker.io-iiidev-*/) — files planted in one VM showed up in later fresh
    VMs, and a stale file broke an import until the cache was purged by hand. That is
    cross-run image pollution in the daemon, not in this worker; worth its own issue.

Summary by CodeRabbit

  • New Features

    • Added the sandbox code runner for executing Node.js and Python code in isolated microVMs.
    • Added persistent runtimes, function registration, runtime teardown, timeouts, networking, and SDK access.
    • Added a dedicated console interface for execution, registration, and teardown results.
    • Added raw-payload redaction for function calls, trace data, displayed values, and copied content.
    • Added release workflow support and documentation for the new worker.
  • Bug Fixes

    • Sensitive values are withheld when redaction fails, preventing accidental disclosure.

@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview Aug 6, 2026 2:46pm
workers-tech-spec Ready Ready Preview Aug 6, 2026 2:46pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@andersonleal, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 34 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4359a8eb-10c3-4c49-b72f-4c99dc160b71

📥 Commits

Reviewing files that changed from the base of the PR and between d54d849 and e027cef.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • sandbox-code-runner/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (77)
  • .github/release-workers.yaml
  • README.md
  • console/SKILL.md
  • console/web/src/components/chat/MessageList.tsx
  • console/web/src/components/function-trigger/FunctionTriggerCard.tsx
  • console/web/src/components/function-trigger/redact-raw.test.tsx
  • console/web/src/components/function-trigger/renderer-registry.tsx
  • console/web/src/lib/function-trigger-copy.test.ts
  • console/web/src/lib/function-trigger-copy.ts
  • console/web/src/pages/TracesV2/components/SpanBaggageTab.tsx
  • console/web/src/pages/TracesV2/components/SpanErrorsTab.test.tsx
  • console/web/src/pages/TracesV2/components/SpanErrorsTab.tsx
  • console/web/src/pages/TracesV2/components/SpanLinksTab.tsx
  • console/web/src/pages/TracesV2/components/SpanLogsTab.test.tsx
  • console/web/src/pages/TracesV2/components/SpanLogsTab.tsx
  • console/web/src/pages/TracesV2/components/SpanOtelLogsTab.tsx
  • console/web/src/pages/TracesV2/components/SpanPanel.redaction-coverage.test.ts
  • console/web/src/pages/TracesV2/components/SpanPanel.tsx
  • console/web/src/pages/TracesV2/components/SpanTagsTab.test.tsx
  • console/web/src/pages/TracesV2/components/SpanTagsTab.tsx
  • console/web/src/pages/TracesV2/lib/functionTriggerFromSpan.test.ts
  • console/web/src/pages/TracesV2/lib/functionTriggerFromSpan.ts
  • console/web/src/pages/TracesV2/lib/redactAttributes.test.ts
  • console/web/src/pages/TracesV2/lib/redactAttributes.ts
  • console/web/src/types/injectable-ui.ts
  • docs/sops/injectable-console-ui.md
  • packages/console-ui/index.d.ts
  • pnpm-workspace.yaml
  • sandbox-code-runner/Cargo.toml
  • sandbox-code-runner/README.md
  • sandbox-code-runner/build.rs
  • sandbox-code-runner/config.yaml
  • sandbox-code-runner/iii.worker.yaml
  • sandbox-code-runner/src/config.rs
  • sandbox-code-runner/src/engine.rs
  • sandbox-code-runner/src/error.rs
  • sandbox-code-runner/src/functions/inject_guidance.rs
  • sandbox-code-runner/src/functions/mod.rs
  • sandbox-code-runner/src/functions/register.rs
  • sandbox-code-runner/src/functions/run.rs
  • sandbox-code-runner/src/functions/teardown.rs
  • sandbox-code-runner/src/lib.rs
  • sandbox-code-runner/src/main.rs
  • sandbox-code-runner/src/manager.rs
  • sandbox-code-runner/src/manifest.rs
  • sandbox-code-runner/src/runner.rs
  • sandbox-code-runner/src/ui.rs
  • sandbox-code-runner/tests/golden/runners/iii.mjs
  • sandbox-code-runner/tests/golden/runners/invoke.mjs
  • sandbox-code-runner/tests/golden/runners/invoke.py
  • sandbox-code-runner/tests/golden/runners/run.mjs
  • sandbox-code-runner/tests/golden/runners/run.py
  • sandbox-code-runner/tests/golden/runners/sandbox_code_runner_iii.py
  • sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.inject-guidance.json
  • sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.register_function.json
  • sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.run.json
  • sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.teardown.json
  • sandbox-code-runner/tests/integration.rs
  • sandbox-code-runner/tests/manifest.rs
  • sandbox-code-runner/tests/runner_exec.rs
  • sandbox-code-runner/tests/schemas.rs
  • sandbox-code-runner/tests/support/mod.rs
  • sandbox-code-runner/ui/build.mjs
  • sandbox-code-runner/ui/package.json
  • sandbox-code-runner/ui/page.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/index.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/redact-runtime-ids.test.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/register-function.test.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/register-function.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/run.test.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/run.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/teardown.test.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/teardown.tsx
  • sandbox-code-runner/ui/src/lib/shared.test.tsx
  • sandbox-code-runner/ui/src/lib/shared.tsx
  • sandbox-code-runner/ui/styles.css
  • sandbox-code-runner/ui/tsconfig.json
📝 Walkthrough

Walkthrough

This PR adds the sandbox-code-runner worker, its build and UI assets, runtime execution and teardown logic, public schemas and tests, and shared console redaction support for raw function-trigger payloads and selected trace tabs.

Changes

Sandbox code runner and console redaction

Layer / File(s) Summary
Shared raw redaction contracts and function-trigger views
console/SKILL.md, console/web/src/components/function-trigger/..., console/web/src/components/chat/MessageList.tsx, console/web/src/lib/function-trigger-copy.ts, console/web/src/types/injectable-ui.ts, docs/sops/injectable-console-ui.md, packages/console-ui/index.d.ts
FunctionTriggerRenderer now supports optional redactRaw. The renderer registry selects and fences redactors. Function-trigger panes, inline previews, clipboard text, and assistant copy text now use redacted raw values.
Span-derived redaction in trace tabs
console/web/src/pages/TracesV2/components/..., console/web/src/pages/TracesV2/lib/...
SpanPanel now derives a function-specific raw redactor from the current span and passes it to tags, logs, and errors tabs. Shared helpers apply redaction to values and attributes. Coverage tests require each tab to declare or wire its redaction behavior.
Worker package, contracts, and startup wiring
.github/workflows/*, README.md, pnpm-workspace.yaml, sandbox-code-runner/..., sandbox-code-runner/src/{config,engine,error,functions,manifest,lib,main}.rs, sandbox-code-runner/tests/{manifest,schemas,support}*, sandbox-code-runner/tests/golden/schemas/*
The repository now includes the sandbox-code-runner worker package, release and tag wiring, configuration, manifest output, error and engine abstractions, public run/register/teardown contracts, the inject-guidance hook, static function registration, schema generation, and related tests and documentation.
Sandbox runtime execution and lifecycle management
sandbox-code-runner/src/{manager,runner}.rs, sandbox-code-runner/tests/golden/runners/*, sandbox-code-runner/tests/{integration,runner_exec}.rs
The worker now manages sandbox-backed Node and Python runtimes, supports direct runs, persistent runtimes, namespace-based function registration, teardown, guest iii SDK injection, sentinel-framed execution results, and end-to-end runtime and wrapper test coverage.
Worker console UI and runtime-id redaction
sandbox-code-runner/src/ui.rs, sandbox-code-runner/ui/*, sandbox-code-runner/ui/src/...
The worker now ships an injectable console UI with embedded assets, shared runtime-id redaction utilities, and custom renderers for run, register_function, and teardown. The UI includes scoped styles and extensive renderer tests for states, fallbacks, and redaction.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Console
  participant sandbox-code-runner
  participant RuntimeManager
  participant iii-sandbox

  Caller->>sandbox-code-runner: trigger sandbox-code-runner::run
  sandbox-code-runner->>RuntimeManager: run(RunRequest)
  RuntimeManager->>iii-sandbox: create or reuse runtime
  RuntimeManager->>iii-sandbox: plant files and execute wrapper
  iii-sandbox-->>RuntimeManager: logs and framed result
  RuntimeManager-->>sandbox-code-runner: RunResponse
  sandbox-code-runner-->>Console: function-trigger message
Loading

Possibly related PRs

  • iii-hq/workers#579: Both PRs change the injectable console UI and FunctionTriggerRenderer infrastructure, including renderer registration and shared console contracts.
  • iii-hq/workers#609: Both PRs use the same worker UI packaging and build integration pattern with pnpm-workspace.yaml, build.rs, ui/page.tsx, and embedded UI assets.
  • iii-hq/workers#515: Both PRs change TracesV2 function-trigger and span-derived redaction behavior.

Suggested labels: no-ticket

Poem

A bunny packed a tiny VM den,
With code to run, then rest again.
Raw secrets got a careful veil,
Trace tabs now follow the redaction trail.
Hop, clip, and ship — what a tidy tale.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary change: executing Node.js and Python code in iii-sandbox microVMs with the guest iii SDK.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch feat/code-runner
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/code-runner

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

An injected function-trigger renderer can hide a capability inside its own
card and still leak it: the card's `raw json` tab renders `message.input` /
`message.output` verbatim, the copy buttons put the same value on the
clipboard, an assistant-turn copy re-serializes every call's arguments, and
the trace span tabs show the same payload from the other side.

Adds `redactRaw?(value: unknown): unknown` to the `FunctionTriggerRenderer`
contract: the claiming renderer declares how to redact, and the host applies
it once at each of those exits. It runs inside the host's render, so it must
be pure and total; a throw is fenced and fails CLOSED — the pane shows a
placeholder rather than the raw value.

Trace-side tabs get the same treatment, and
`SpanPanel.redaction-coverage.test.ts` enforces a closed world: every tab
either wires a redactor or carries a written reason why it cannot leak.

Needed by any worker whose function arguments carry a capability — the first
is code-runner, whose `runtime_id` addresses a live microVM.
A worker that executes nothing itself: every eval and every registered
handler call becomes `sandbox::*` calls to the iii-sandbox daemon, on the
`node` and `python` preset images. That buys Python, npm/pip, and a real OS
per call, and keeps the host filesystem untouched.

Three functions, split by lifetime:

- `code-runner::eval` is one-shot — `sandbox::run` boots, runs, and stops the
  VM in a single call, returning no `runtime_id` because nothing survives to
  address. `keep: true` mints one; passing an existing `runtime_id` reuses
  that VM and leaves it running.
- `code-runner::register_function` is persistent: it creates one runtime per
  `(namespace, lang)` and publishes the source as a bus function.
- `code-runner::teardown` takes a `runtime_id` or a whole namespace.

A `runtime_id` is a capability — it addresses a live VM — so it never reaches
a caller that does not already hold it: the error types redact it, and the
injected console UI declares `redactRaw` so the card, its raw pane, and the
clipboard are covered too.

The handler-to-runner protocol frames results with a per-call sentinel
carried in a stdin envelope, never argv. The sentinel is a framing device,
not a security boundary — the handler loads into the runner's own process and
can intercept stdout to forge a frame; the doc comment says so plainly.

Missing iii-sandbox is not fatal: the worker warns at boot and keeps serving,
failing each call with a clear message.
Four name-only leftovers from the code-runner -> sandbox-code-runner
rename, caught by a path-safe repo grep gate (the original gate's
filter matched the whole path:line, which hid every hit under
sandbox-code-runner/ itself):

- README.md:156 documented the guest worker identity as
  code-runner:eval / code-runner:<function_id>; the real values
  (verified against src/manager.rs) are sandbox-code-runner:run and
  sandbox-code-runner:<function_id>.
- build.rs:1 doc comment.
- ui/build.mjs:35 and :80 comments.

Prose-only; surrounding lines rewrapped to the existing width where the
longer name pushed past it. CODE_RUNNER_GUIDANCE (a Rust identifier) is
deliberately left as-is, deferred with the rest of the CodeRunner*/
CODE_RUNNER_* identifier family to the whole-branch review.
…rding after the rename

Final review fix wave for the eval->run rename: two README claims that
contradicted the code (the namespace runtime's network access, and a dead
invalid_request cause), stale "eval" wording left in the guest-script
string constants that get planted into every tenant microVM, a doc comment
naming the one Python filename the design forbids (iii.py), a stale test
rationale comment plus a redundant needle, two awkward published function
descriptions, a test doc/body contradiction, missing plant-table test
coverage on the Node arm, and an undocumented size-ceiling tension in the
guest SDK build script.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (4)
sandbox-code-runner/tests/integration.rs (1)

73-81: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Isolate the engine state directory from the developer's real home.

The test creates a scratch home and removes it on drop, but it only passes that directory as the config path. The engine still resolves its state directory from the process environment. State then lands outside home, survives Cleanup, and can leak between runs. Set the home environment for the spawned engine.

♻️ Proposed change
     std::process::Command::new(&iii_bin)
         .arg("-c")
         .arg(&cfg_path)
         .arg("--no-update-check")
+        .env("HOME", home)
         .stdout(std::process::Stdio::null())
         .stderr(std::process::Stdio::null())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sandbox-code-runner/tests/integration.rs` around lines 73 - 81, Update the
spawned engine command in the integration test to set its home-directory
environment variable to the scratch home used by the test before calling spawn.
Keep the existing config argument and cleanup flow unchanged so all engine state
is created under that temporary directory.
sandbox-code-runner/src/manager.rs (1)

1249-1298: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename the remaining eval identifiers in the tests.

The public op is run, but the test helpers and section headers still use eval (eval_req, an_ephemeral_eval_..., "eval: the boot paths"). Renaming them keeps the test vocabulary aligned with the wire surface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sandbox-code-runner/src/manager.rs` around lines 1249 - 1298, Rename the
remaining test-only eval terminology to run, including the eval_req helper,
an_ephemeral_eval_* test names, and the “eval: the boot paths” section header.
Keep the test behavior and public operation unchanged while aligning identifiers
and comments with the run wire operation.
sandbox-code-runner/tests/runner_exec.rs (1)

106-118: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Write stdin on a thread so a full stdout pipe cannot hang the suite.

write_all runs before wait_bounded starts the stdout and stderr drain threads. If a future test feeds an envelope larger than the pipe buffer while the child writes enough output to fill its own stdout pipe, both sides block and write_all has no deadline. The 15 s cap in wait_bounded is never reached in that case.

♻️ Proposed change
-    child
-        .stdin
-        .take()
-        .unwrap()
-        .write_all(stdin.as_bytes())
-        .unwrap();
+    let mut sink = child.stdin.take().unwrap();
+    let bytes = stdin.as_bytes().to_vec();
+    let feeder = std::thread::spawn(move || sink.write_all(&bytes));
     let out = wait_bounded(child, 15);
+    feeder.join().unwrap().unwrap();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sandbox-code-runner/tests/runner_exec.rs` around lines 106 - 118, Update the
test process flow around wait_bounded so writing the child’s stdin occurs on a
separate thread, allowing stdout and stderr draining to begin concurrently. Join
or otherwise handle the stdin writer while preserving the existing 15-second
bounded wait and error propagation for write failures.
sandbox-code-runner/ui/src/lib/shared.tsx (1)

391-409: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Make errorInfo return a real string for every input shape.

isErrorOutput only checks that the error key exists. If rec.error is undefined, JSON.stringify(err) returns undefined, so message is typed string but is undefined at runtime. ErrorCard then calls redactRuntimeIds(message), which throws a TypeError. The host fences the throw, so the card is replaced by an error chip instead of the intended redacted card.

♻️ Proposed fix
   const message =
     typeof err === 'string'
       ? err
       : typeof errObj?.message === 'string'
         ? errObj.message
-        : JSON.stringify(err)
+        : (JSON.stringify(err) ?? 'the response carried an error with no message')
   return { message }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sandbox-code-runner/ui/src/lib/shared.tsx` around lines 391 - 409, Update
errorInfo so its returned message is always a string, including when rec.error
is undefined or JSON.stringify(err) produces undefined. Preserve the existing
string and object-message handling, and add a safe fallback before returning
message so ErrorCard can always pass it to redactRuntimeIds.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sandbox-code-runner/src/config.rs`:
- Around line 59-65: Update clamp_timeout so the default_timeout_ms path is also
capped at max_timeout_ms, ensuring omitted and explicit timeout requests never
exceed the configured maximum.

In `@sandbox-code-runner/src/functions/register.rs`:
- Around line 23-25: The documentation in
sandbox-code-runner/src/functions/register.rs lines 23-25 must state that Node
and Python registrations may share a namespace, with runtime reuse scoped to the
same (namespace, language) pair. Update sandbox-code-runner/src/functions/mod.rs
lines 48-55 to remove the claim that the first registration fixes the namespace
language and describe reuse as scoped to the namespace and language; no
behavioral code change is required.

In
`@sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.register_function.json`:
- Line 34: Align the per-(namespace, lang) runtime rule across the documented
and tested surfaces: update the generated lang description in
sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.register_function.json#L34-L34
to say later registrations must keep the same namespace and lang, refresh the
schema output, and change the wording in sandbox-code-runner/README.md#L98-L100
to match that rule. In sandbox-code-runner/tests/integration.rs#L411-L424, add
or adjust the e2e assertion so a single namespace can register multiple runtimes
when the lang differs, including a case like ce-e2e-iii::describe with lang set
to python.

In `@sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.teardown.json`:
- Around line 7-24: Update the TeardownRequest JSON Schema definition and its
golden snapshot to add a oneOf constraint requiring exactly one of runtime_id or
namespace. Ensure validation rejects requests with neither selector or both
selectors while preserving the existing nullable string property definitions.

In `@sandbox-code-runner/tests/integration.rs`:
- Around line 129-136: Update the sandbox collection logic in the integration
test helper around the resp["sandboxes"] pipeline so `stopped` is read with
`as_bool()` before filtering, and only keep entries whose boolean value is
explicitly false. If `stopped` is missing or not a boolean, make the test fail
with the existing response-shape expectation instead of treating it as live, and
keep the `sandbox_id` extraction path unchanged.

In `@sandbox-code-runner/ui/styles.css`:
- Around line 59-65: Update the reduced-motion styles near .cr-ui-msg-note.pulse
by adding a prefers-reduced-motion: reduce media rule that sets animation: none
for the selector, while preserving the existing pulse animation for users
without the preference.

---

Nitpick comments:
In `@sandbox-code-runner/src/manager.rs`:
- Around line 1249-1298: Rename the remaining test-only eval terminology to run,
including the eval_req helper, an_ephemeral_eval_* test names, and the “eval:
the boot paths” section header. Keep the test behavior and public operation
unchanged while aligning identifiers and comments with the run wire operation.

In `@sandbox-code-runner/tests/integration.rs`:
- Around line 73-81: Update the spawned engine command in the integration test
to set its home-directory environment variable to the scratch home used by the
test before calling spawn. Keep the existing config argument and cleanup flow
unchanged so all engine state is created under that temporary directory.

In `@sandbox-code-runner/tests/runner_exec.rs`:
- Around line 106-118: Update the test process flow around wait_bounded so
writing the child’s stdin occurs on a separate thread, allowing stdout and
stderr draining to begin concurrently. Join or otherwise handle the stdin writer
while preserving the existing 15-second bounded wait and error propagation for
write failures.

In `@sandbox-code-runner/ui/src/lib/shared.tsx`:
- Around line 391-409: Update errorInfo so its returned message is always a
string, including when rec.error is undefined or JSON.stringify(err) produces
undefined. Preserve the existing string and object-message handling, and add a
safe fallback before returning message so ErrorCard can always pass it to
redactRuntimeIds.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ad4e945-6803-464a-824f-02ea1e9fbd72

📥 Commits

Reviewing files that changed from the base of the PR and between b35ee10 and ea56acb.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • sandbox-code-runner/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (78)
  • .github/workflows/create-tag.yml
  • .github/workflows/release.yml
  • README.md
  • console/SKILL.md
  • console/web/src/components/chat/MessageList.tsx
  • console/web/src/components/function-trigger/FunctionTriggerCard.tsx
  • console/web/src/components/function-trigger/redact-raw.test.tsx
  • console/web/src/components/function-trigger/renderer-registry.tsx
  • console/web/src/lib/function-trigger-copy.test.ts
  • console/web/src/lib/function-trigger-copy.ts
  • console/web/src/pages/TracesV2/components/SpanBaggageTab.tsx
  • console/web/src/pages/TracesV2/components/SpanErrorsTab.test.tsx
  • console/web/src/pages/TracesV2/components/SpanErrorsTab.tsx
  • console/web/src/pages/TracesV2/components/SpanLinksTab.tsx
  • console/web/src/pages/TracesV2/components/SpanLogsTab.test.tsx
  • console/web/src/pages/TracesV2/components/SpanLogsTab.tsx
  • console/web/src/pages/TracesV2/components/SpanOtelLogsTab.tsx
  • console/web/src/pages/TracesV2/components/SpanPanel.redaction-coverage.test.ts
  • console/web/src/pages/TracesV2/components/SpanPanel.tsx
  • console/web/src/pages/TracesV2/components/SpanTagsTab.test.tsx
  • console/web/src/pages/TracesV2/components/SpanTagsTab.tsx
  • console/web/src/pages/TracesV2/lib/functionTriggerFromSpan.test.ts
  • console/web/src/pages/TracesV2/lib/functionTriggerFromSpan.ts
  • console/web/src/pages/TracesV2/lib/redactAttributes.test.ts
  • console/web/src/pages/TracesV2/lib/redactAttributes.ts
  • console/web/src/types/injectable-ui.ts
  • docs/sops/injectable-console-ui.md
  • packages/console-ui/index.d.ts
  • pnpm-workspace.yaml
  • sandbox-code-runner/Cargo.toml
  • sandbox-code-runner/README.md
  • sandbox-code-runner/build.rs
  • sandbox-code-runner/config.yaml
  • sandbox-code-runner/iii.worker.yaml
  • sandbox-code-runner/src/config.rs
  • sandbox-code-runner/src/engine.rs
  • sandbox-code-runner/src/error.rs
  • sandbox-code-runner/src/functions/inject_guidance.rs
  • sandbox-code-runner/src/functions/mod.rs
  • sandbox-code-runner/src/functions/register.rs
  • sandbox-code-runner/src/functions/run.rs
  • sandbox-code-runner/src/functions/teardown.rs
  • sandbox-code-runner/src/lib.rs
  • sandbox-code-runner/src/main.rs
  • sandbox-code-runner/src/manager.rs
  • sandbox-code-runner/src/manifest.rs
  • sandbox-code-runner/src/runner.rs
  • sandbox-code-runner/src/ui.rs
  • sandbox-code-runner/tests/golden/runners/iii.mjs
  • sandbox-code-runner/tests/golden/runners/invoke.mjs
  • sandbox-code-runner/tests/golden/runners/invoke.py
  • sandbox-code-runner/tests/golden/runners/run.mjs
  • sandbox-code-runner/tests/golden/runners/run.py
  • sandbox-code-runner/tests/golden/runners/sandbox_code_runner_iii.py
  • sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.inject-guidance.json
  • sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.register_function.json
  • sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.run.json
  • sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.teardown.json
  • sandbox-code-runner/tests/integration.rs
  • sandbox-code-runner/tests/manifest.rs
  • sandbox-code-runner/tests/runner_exec.rs
  • sandbox-code-runner/tests/schemas.rs
  • sandbox-code-runner/tests/support/mod.rs
  • sandbox-code-runner/ui/build.mjs
  • sandbox-code-runner/ui/package.json
  • sandbox-code-runner/ui/page.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/index.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/redact-runtime-ids.test.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/register-function.test.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/register-function.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/run.test.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/run.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/teardown.test.tsx
  • sandbox-code-runner/ui/src/function-trigger-message/teardown.tsx
  • sandbox-code-runner/ui/src/lib/shared.test.tsx
  • sandbox-code-runner/ui/src/lib/shared.tsx
  • sandbox-code-runner/ui/styles.css
  • sandbox-code-runner/ui/tsconfig.json

Comment thread sandbox-code-runner/src/config.rs
Comment on lines +23 to +25
/// Which runner backs this namespace: "node" or "python". A namespace's
/// language is fixed by its first registration; a later id under the
/// same namespace but a different lang is refused.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Allow both languages in one namespace.

These descriptions state that the first registration fixes a namespace language. The worker contract uses a persistent runtime per (namespace, language). TeardownResponse also supports more than one runtime for a namespace. This publishes an incorrect rejection rule to callers.

  • sandbox-code-runner/src/functions/register.rs#L23-L25: State that Node and Python registrations can share a namespace, with reuse scoped to the same (namespace, language) pair.
  • sandbox-code-runner/src/functions/mod.rs#L48-L55: Remove the claim that later IDs must use the first registration language. Describe reuse as scoped to the same namespace and language.
📍 Affects 2 files
  • sandbox-code-runner/src/functions/register.rs#L23-L25 (this comment)
  • sandbox-code-runner/src/functions/mod.rs#L48-L55
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sandbox-code-runner/src/functions/register.rs` around lines 23 - 25, The
documentation in sandbox-code-runner/src/functions/register.rs lines 23-25 must
state that Node and Python registrations may share a namespace, with runtime
reuse scoped to the same (namespace, language) pair. Update
sandbox-code-runner/src/functions/mod.rs lines 48-55 to remove the claim that
the first registration fixes the namespace language and describe reuse as scoped
to the namespace and language; no behavioral code change is required.

Comment on lines +7 to +24
"properties": {
"namespace": {
"default": null,
"type": [
"string",
"null"
]
},
"runtime_id": {
"default": null,
"type": [
"string",
"null"
]
}
},
"title": "TeardownRequest",
"type": "object"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate relevant files =="
git ls-files | rg 'sandbox-code-runner|teardown|schema' || true

echo
echo "== target JSON =="
if [ -f sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.teardown.json ]; then
  cat -n sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.teardown.json
fi

echo
echo "== references to teardown request/schema/description =="
rg -n "TeardownRequest|teardown\.json|runtime_id|namespace.*runtime_id|Exactly one|selector" sandbox-code-runner -S || true

Repository: iii-hq/workers

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== teardown source =="
cat -n sandbox-code-runner/src/functions/teardown.rs

echo
echo "== golden schema validation reference =="
sed -n '1,220p' sandbox-code-runner/tests/schemas.rs
cat -n sandbox-code-runner/tests/schemas.rs | sed -n '1,220p'

echo
echo "== schema validation behavior probe from Rust jsonschema dependency if locked; otherwise inspect Cargo.toml =="
sed -n '1,220p' sandbox-code-runner/Cargo.toml
rg -n 'jsonschema|validate' sandbox-code-runner/Cargo.toml sandbox-codeRunner/Cargo.lock .github/scripts/tests . 2>/dev/null | head -n 200 || true

echo
echo "== JSON object examples matching current schema =="
python3 - <<'PY'
import json
schema = json.load(open('sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.teardown.json'))
props = schema['request_schema']['properties']
examples = [{}, {"runtime_id":"r","namespace":"n"},{"namespace":"n"},{"runtime_id":"r"},{"namespace":None},{"runtime_id":None}]
for ex in examples:
    valid = set(ex) <= set(props) and all(e in (props[k]['type'] if isinstance(props[k]['type'], list) else [props[k]['type']]) for k,e in ex.items())
    print(json.dumps(ex, separators=(',', ':')), "schema_valid_by_type_and_additional_items_without_required_or_oneOf=", valid)
PY

Repository: iii-hq/workers

Length of output: 33855


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== teardown implementation =="
sed -n '1,260p' sandbox-code-runner/src/functions/teardown.rs

echo
echo "== search for invalid request handling near teardown =="
rg -n "TeardownRequest|InvalidRequest|runtime_id|namespace|both|neither|exactly one|teardown" sandbox-code-runner/src -S

echo
echo "== registry/SDK validation path references =="
rg -n "request_schema|response_schema|validate_payload|Validate|validate_request|JsonSchema|Schema|unknown.*schema|invalid_request|invalid" sandbox-code-runner src crates iii-sdk 2>/dev/null | head -n 240 || true

Repository: iii-hq/workers

Length of output: 50370


Enforce the exclusive selector requirement in the teardown request schema.

TeardownRequest serializes { runtime_id: Option<String>, namespace: Option<String> }, so the generated request_schema accepts no selector and both selectors. Add the matching oneOf constraint to the emitted schema snapshot and TeardownRequest's derived JSON Schema shape so schema validation rejects those requests before runtime or client handling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sandbox-code-runner/tests/golden/schemas/sandbox-code-runner.teardown.json`
around lines 7 - 24, Update the TeardownRequest JSON Schema definition and its
golden snapshot to add a oneOf constraint requiring exactly one of runtime_id or
namespace. Ensure validation rejects requests with neither selector or both
selectors while preserving the existing nullable string property definitions.

Comment on lines +129 to +136
resp["sandboxes"]
.as_array()
.expect("sandboxes is an array")
.iter()
.filter(|s| s["stopped"] == false)
.filter_map(|s| s["sandbox_id"].as_str().map(str::to_string))
.collect()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect the sandbox::list response shape for the `stopped` field.
set -uo pipefail
rg -nP -C4 '"?stopped"?' --type=rust -g '!sandbox-code-runner/**' | head -80
rg -nP -C6 'sandbox::list' --type=rust | head -80

Repository: iii-hq/workers

Length of output: 152


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "Files named integration.rs:"
fd -a 'integration\.rs$' . | sed 's#^\./##'

echo
echo "sandbox-code-runner tests around relevant lines:"
if [ -f sandbox-code-runner/tests/integration.rs ]; then
  wc -l sandbox-code-runner/tests/integration.rs
  sed -n '110,145p' sandbox-code-runner/tests/integration.rs | cat -n -v
fi

echo
echo "Search for sandbox::list and stopped in repository:"
rg -n -C3 'sandbox::list|\.stopped|\"stopped\"|stopped:' . --glob '!target/**' --glob '!node_modules/**' | head -240

Repository: iii-hq/workers

Length of output: 19380


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "Locate sandbox crate files:"
git ls-files | rg '(^|/)sandbox|manager\.rs$|registry|sandbox-code-runner' | sed -n '1,200p'

echo
echo "Search sandbox::list implementation in sandbox-code-runner (bounded):"
rg -n -C5 'sandbox::list|List|list|sandboxes|stopped' sandbox-code-runner/src || true

echo
echo "Show manager.rs relevant definitions around sandbox registration/listing (bounded):"
rg -n 'struct .*Sandbox|SandboxRegistry|fn .*list|let .*stopped|stopped' sandbox-code-runner/src/*.rs | head -200

echo
echo "Inspect candidate manager.rs sections:"
sed -n '1,260p' sandbox-code-runner/src/manager.rs | cat -n -v
sed -n '260,460p' sandbox-code-runner/src/manager.rs | cat -n -v

Repository: iii-hq/workers

Length of output: 50370


Consume stopped as a boolean before filtering.

s["stopped"] == false also matches a missing field (Null) because Null != false turns the filter off. Check as_bool() explicitly and fail on an unexpected response shape so the leak assertions do not read an empty live-id set.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sandbox-code-runner/tests/integration.rs` around lines 129 - 136, Update the
sandbox collection logic in the integration test helper around the
resp["sandboxes"] pipeline so `stopped` is read with `as_bool()` before
filtering, and only keep entries whose boolean value is explicitly false. If
`stopped` is missing or not a boolean, make the test fail with the existing
response-shape expectation instead of treating it as live, and keep the
`sandbox_id` extraction path unchanged.

Comment on lines +59 to +65
[data-iii-ui="sandbox-code-runner"] .cr-ui-msg-note.pulse {
animation: cr-ui-pulse 1.6s ease-in-out infinite;
}
@keyframes cr-ui-pulse {
0%, 100% { opacity: 1 }
50% { opacity: 0.35 }
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | sed -n '1,200p'

echo "== target file excerpt =="
if [ -f sandbox-code-runner/ui/styles.css ]; then
  wc -l sandbox-code-runner/ui/styles.css
  sed -n '1,120p' sandbox-code-runner/ui/styles.css | cat -n
else
  fd -a styles.css .
fi

echo "== pulse/iii-ui references =="
rg -n "data-iii-ui|cr-ui-msg-note|cr-ui-pulse|prefers-reduced-motion|animation" sandbox-code-runner/ui sandbox-code-runner 2>/dev/null | sed -n '1,200p'

Repository: iii-hq/workers

Length of output: 30115


Honor the reduced-motion user preference for .cr-ui-msg-note.pulse.

The infinite cr-ui-pulse animation runs while messages are pending. Add a prefers-reduced-motion: reduce rule that sets animation: none for this selector so users who request reduced motion do not see the pulse.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sandbox-code-runner/ui/styles.css` around lines 59 - 65, Update the
reduced-motion styles near .cr-ui-msg-note.pulse by adding a
prefers-reduced-motion: reduce media rule that sets animation: none for the
selector, while preserving the existing pulse animation for users without the
preference.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 55 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

@andersonleal
andersonleal merged commit 04c07e3 into main Aug 6, 2026
20 checks passed
@andersonleal
andersonleal deleted the feat/code-runner branch August 6, 2026 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant