feat(iii-worker): champion-tier agent DX for sandbox::*#1699
Conversation
Ships the full sandbox::* AI-agent DX work in one squash: TTHW=1
meta-function, structured S-code errors with resubmittable fix
payloads, multi-shape exec input, expanded catalog discovery, and
end-to-end handler-boundary tracing.
- sandbox::run: meta-function that composes create + fs::write +
exec + stop into one call. Workflow TTHW (time-to-hello-world)
drops from 4 tool calls to 1. Auto-stops the VM on both success
and failure unless keep_sandbox: true.
- sandbox::catalog::list: NEW function returning the daemon's
image catalog (bundled presets + operator-registered
custom_images). Closes the "what custom images exist here"
loop without requiring operator hand-off.
- sandbox::exec.cmd now accepts THREE shapes:
* cmd + args (classic POSIX)
* argv array
* shell-line cmd (shlex-split when args/argv empty)
- sandbox::exec.env and sandbox::create.env accept BOTH
Vec<"K=V"> and { K: V } map shapes (untagged enum).
- is_valid_env_name pins env-var names to [A-Za-z_][A-Za-z0-9_]*
(lowercase fine; digits-leading or `/`/`-`/`=` rejected S001).
- sandbox::fs::read returns body inline as a UTF-8 string for
text under 1 MiB; large/binary still streams via StreamChannelRef.
Removes the "always stream" friction for typical text reads.
- Every sandbox::* function returns a structured envelope:
{ code, type, message, docs_url, retryable, fix, fix_note }
- docs_url is anchored at the in-repo README (e.g. #S001, #S211).
- fix is a non-null JSON payload the agent can resubmit verbatim
when the recovery is unambiguous. fix_note describes how to use
the fix or, when fix is null, explains why no auto-fix exists.
- New FsParentNotFound variant: parent-missing S211s carry
fix: { "parents": true } that the agent merges into the
original request. Plain target-missing FsNotFound still returns
null (caller-intent-dependent recovery).
- New RunStepFailed variant: when sandbox::run sub-step fails,
inner_code surfaces transparently and fix.context names the
failing step (e.g. "during sandbox::run step `fs::write (code)`")
plus fix.sandbox_id when keep_sandbox: true.
- SandboxErrorWire wrapper makes Display emit the JSON payload
through iii_sdk's async handler boundary, so the structured
envelope round-trips without custom serialization in each
handler.
- DEFAULT_EXEC_TIMEOUT_MS: 30_000 -> 300_000 (5 minutes). Sized
for cold `npm install` / `pip install` / `cargo build`. The
previous 30s default fired as an opaque engine-gate denial
before the daemon could return a structured timed_out:true
response; the 5m default lets typical install commands complete
without manual timeout_ms override.
- log_handler_result emits tracing::info! on BOTH success and
error with a stable field set: function_id, sandbox_id, success,
error_code, error_type, retryable, duration_ms.
- Wired into every sandbox handler: create, exec, stop, list,
catalog::list, run, and all 10 sandbox::fs::* functions.
- Operators can dashboard sandbox::* usage without grepping logs.
- All 10 sandbox::fs::* functions migrated from the legacy
RegisterFunctionMessage shape to RegisterFunction::new_async.
Full JSON Schema with field docs and worked examples on every
request/response struct.
- FsEntry, FsMatch, FsSedFileResult, FsReadMeta gain JsonSchema
derives so schemars round-trips cleanly through the SDK.
- iii-sdk channels (StreamChannelRef, ChannelDirection) gain
JsonSchema derives for the same reason.
- README per-S-code anchored subsections (<a id="Sxxx"></a>) plus
an Agent Quickstart that maps real questions to the right
function in <60 seconds.
- SANDBOX_AGENT_GUIDE pub const: paste-into-system-prompt cheat
sheet covering the workflow, cmd shapes, env shapes, error
parsing, and S-code recovery table. Doctest pins the contents
against the wire format.
- Three new tests pin doc/wire invariants:
* sandbox_docs_anchor_stability.rs — every SandboxErrorCode has
a README anchor
* sandbox_fs_decode_error_wire.rs — decode-error wire shape
* sandbox_fs_read_buffer_boundary.rs — byte-loss boundary check
for the inline-UTF-8 read fast path
- Shell worker (workers/shell) regression-checked: 20/20 sandbox
dispatch tests pass unchanged. Wire shape stays additive:
missing argv/env fields default to empty, existing Vec env
shape still deserializes, error code propagation still works
via map_iii_err's three-path matching.
- iii-worker: 770+ lib tests pass (incl. 5 new for catalog::list,
RunStepFailed wire shape, FsParentNotFound fix payload, and
handler-boundary trace shape).
- iii-worker integration: 1228 / 1228 tests pass (7 ignored,
all pre-existing).
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
skill-check — docs2 verified, 321 skipped.
Three for three. Nicely done. |
Terraform plan —
|
📝 WalkthroughWalkthroughAdds JsonSchema derives and schemars dependency; implements EnvShape and argv handling with shlex-based cmd splitting; raises exec timeout to 300s; introduces sandbox::run and sandbox::catalog::list; refactors sandbox::fs handlers to typed RegisterFunction::new_async with schema/examples; adds hybrid fs::read inline UTF‑8 fast‑path; expands errors with structured fix payloads, docs, and tests. ChangesSandbox Protocol Expansion and API Surface
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (4)
crates/iii-worker/src/sandbox_daemon/README.md (2)
444-460: 💤 Low valueConsider adding descriptions to filesystem error codes for consistency.
Error codes S211-S216 have minimal documentation (just titles), while other error codes like S001, S100, and S200 include detailed descriptions and guidance. For a more consistent agent experience, consider adding brief descriptions to each filesystem error:
- S211: Clarify when this occurs (read/write/stat on missing path)
- S212: Explain the operation attempted and the conflicting file type found
- S213: Note which operations trigger this (mkdir, write with exclusive flag)
- S214: Clarify this applies to
rmoperations- S215: Mention common causes (chmod restrictions, ownership)
- S216: Distinguish from other error types
Even 1-2 sentences per code would help agents understand recovery paths.
🤖 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 `@crates/iii-worker/src/sandbox_daemon/README.md` around lines 444 - 460, Add 1–2 sentence descriptions for the filesystem error codes S211, S212, S213, S214, S215, and S216 in the README where the error list is defined so they match the level of detail of S001/S100/S200; for each code include when it occurs and typical causes or recovery hints (e.g., S211: occurs on read/write/stat of a missing path; S212: operation attempted vs. conflicting file type; S213: triggered by mkdir or write with exclusive flag; S214: applies to rm failures; S215: caused by permission/ownership/chmod issues; S216: explain how it differs from other errors). Update the entries named S211–S216 in the README so each has a short descriptive sentence and a brief recovery suggestion.
12-86: ⚡ Quick winConsider moving example code to docs/ and linking from README.
The Agent Quickstart section includes extensive JSON code examples demonstrating
sandbox::run, the surgical workflow, cmd shapes, env shapes, and error handling. As per coding guidelines, READMEs should link examples from docs/ rather than duplicate them inline.Consider:
- Moving these examples to
docs/api-reference/sandbox.mdxor a dedicated quickstart guide- Replacing the inline examples with brief descriptions and links to the full examples in docs/
This reduces duplication and ensures examples stay synchronized across documentation surfaces.
As per coding guidelines: "**/README.md: Ensure that READMEs do not contain example code that is already in the docs/. READMEs should link these examples in the docs/."
🤖 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 `@crates/iii-worker/src/sandbox_daemon/README.md` around lines 12 - 86, The README's "Agent Quickstart" contains large inline JSON/code examples (including sandbox::run usage, the surgical workflow, cmd shapes, env shapes, and error handling) that should be moved into a docs page and replaced with links; create a new docs file (e.g., docs/api-reference/sandbox.mdx or docs/quickstart/sandbox.mdx) containing the full examples and explanations, then edit the sandbox_daemon README.md's Agent Quickstart section to remove the duplicated examples and add concise descriptions plus links to the new docs page and to the specific examples for sandbox::run, surgical workflow, cmd/env shapes, and error handling so the README points readers to the canonical docs.crates/iii-worker/src/sandbox_daemon/fs/adapter.rs (1)
27-42: ⚡ Quick winNormalize the S211 message before parent-path detection.
This branch is currently case-sensitive, so small message-format shifts can downgrade
FsParentNotFoundto generic not-found and drop the intended recovery hint.Proposed change
"S211" => { + let msg_lc = message.to_ascii_lowercase(); // The in-VM shell uses S211 for both "the target path // doesn't exist" and "an intermediate parent directory // doesn't exist". Split them here so the daemon-side @@ - if message.contains("parent not found") - || message.contains("parent directory not found") + if msg_lc.contains("parent not found") + || msg_lc.contains("parent directory not found") { SandboxError::FsParentNotFound { path: message } } else { SandboxError::fs_not_found(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 `@crates/iii-worker/src/sandbox_daemon/fs/adapter.rs` around lines 27 - 42, The S211 branch's parent-path detection is case-sensitive and can miss variants of the error text; normalize the message (e.g., compute a lowercase/trimmed version) before checking for "parent not found" or "parent directory not found" so the condition reliably detects parent-missing errors, while still returning the original message text to SandboxError::FsParentNotFound { path: ... } or passing the original message into SandboxError::fs_not_found(...) to preserve the original payload.crates/iii-worker/src/sandbox_daemon/exec.rs (1)
231-233: 💤 Low valueUnreachable error path:
argvis already confirmed non-empty.The outer
if !argv.is_empty()on line 223 ensures at least one element exists, soit.next()will always returnSome. Theok_or_elseclosure is dead code. While harmless, consider removing it for clarity or adding a comment explaining it's defensive.🤖 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 `@crates/iii-worker/src/sandbox_daemon/exec.rs` around lines 231 - 233, The ok_or_else branch is unreachable because argv is already checked non-empty earlier; replace the needless ok_or_else on the iterator (`let head = it.next().ok_or_else(...)?`) with a direct unwrap/expect (e.g., `it.next().unwrap()` or `it.next().expect("argv non-empty")`) to clarify intent, or if you prefer to keep defensive code, add a short comment referencing the earlier `if !argv.is_empty()` check; make the change around the `argv`, `it.next()` and `SandboxError::InvalidRequest` usage in exec.rs.
🤖 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 `@crates/iii-worker/src/sandbox_daemon/create.rs`:
- Around line 172-177: BootingVm is being emitted before validating env, so if
req.env.into_kv_vec() fails clients get a spurious BootingVm; call
req.env.clone().into_kv_vec() and assign to env_vec before calling
on_event(SandboxCreateEvent::BootingVm), propagating the error as currently
done, then proceed to use env_vec for booting; refer to
SandboxCreateEvent::BootingVm, req.env, into_kv_vec(), env_vec, and on_event to
locate and reorder these operations.
In `@crates/iii-worker/src/sandbox_daemon/errors.rs`:
- Around line 288-291: Update the doc comment in errors.rs to match the actual
env-var validation used by is_valid_env_name in exec.rs: change the described
POSIX portable name pattern from `[A-Z_][A-Z0-9_]*` to the implemented
`[A-Za-z_][A-Za-z0-9_]*` (or equivalent wording), so the comment accurately
reflects the accepted uppercase and lowercase letters and digits; reference the
is_valid_env_name function to ensure consistency.
- Around line 18-19: The DOCS_BASE constant and the env-var validation mismatch
need addressing: either update the validation in exec.rs (function
is_valid_env_name) to enforce the documented regex by using is_ascii_uppercase
for the first char and is_ascii_uppercase/is_ascii_digit/_ for subsequent chars,
or update the error/doc text (and any README/regex mentions) to reflect the
current implementation that allows lowercase letters; also verify DOCS_BASE
(symbol DOCS_BASE) points to the correct README anchors used by
sandbox_docs_anchor_stability.rs and adjust the URL or comment if the repo path
assumption is inaccurate so SandboxErrorCode anchors remain valid.
In `@crates/iii-worker/src/sandbox_daemon/exec.rs`:
- Around line 52-55: The error text in the SandboxError::InvalidRequest call
incorrectly documents the expected env var pattern; update the message string to
reflect the actual validation used by is_valid_env_name by changing the regex in
the message to `[A-Za-z_][A-Za-z0-9_]*` so the error shown to users matches the
validator implementation.
- Around line 36-41: Update the doc comment on EnvShape::into_kv_vec to reflect
the actual validated env-name pattern: change the stated regex from
`[A-Z_][A-Z0-9_]*` to `[A-Za-z_][A-Za-z0-9_]*`, since the implementation in
is_valid_env_name permits lowercase for npm/pip/hg compatibility; keep the rest
of the doc (purpose, return type, SandboxError behavior) unchanged and ensure
the regex in the comment matches the behavior of into_kv_vec and
is_valid_env_name.
In `@crates/iii-worker/src/sandbox_daemon/README.md`:
- Around line 484-486: The README contains a forward reference to
docs/api-reference/sandbox.mdx#s-codes (and an inline TODO) that points to a
non-existent docs page; either create the referenced docs page
(docs/api-reference/sandbox.mdx with anchor s-codes) in this PR, or remove the
link and the TODO from the README and instead link to the relevant section of
this README; update the README entry that currently references
"docs/api-reference/sandbox.mdx#s-codes" so all references resolve to existing
documentation.
In `@crates/iii-worker/src/sandbox_daemon/run.rs`:
- Around line 123-136: The comment in interpreter_for is misleading and the
mapping incorrectly treats "bash" as /bin/sh; update the comment and mapping in
interpreter_for to reflect actual behavior: either remove the "-lc" mention from
the shell comment (since we pass a file path, not -lc) or change the
implementation so "bash" maps to "/bin/bash" if sandbox images provide bash;
alternatively remove "bash" from the explicit match so it falls to the
catch-all. Ensure you update the tuple returned for the "shell" branch and the
comment above it to be consistent with whichever choice you make, referencing
the interpreter_for function and the match arms for "shell" | "sh" | "bash".
In `@crates/iii-worker/tests/sandbox_docs_anchor_stability.rs`:
- Around line 44-48: The test currently accepts a lowercase-heading fallback
which contradicts the file-level contract that lowercase-renamed anchors should
fail; update the check in the test so it only accepts the explicit HTML id
anchor (use html_anchor and readme, remove the lowercase_heading check and its
creation) and adjust the accompanying comment to state that only the HTML id
anchor is allowed, ensuring that readme.contains is only tested against
html_anchor (refer to variables html_anchor, lowercase_heading, code_str, and
readme to locate and remove the lowercase fallback).
In `@crates/iii-worker/tests/sandbox_fs_read_buffer_boundary.rs`:
- Around line 113-152: The test
invalid_utf8_under_threshold_falls_through_to_stream doesn’t call handle_read so
it doesn’t verify buffer-boundary or stream-fallback behavior; either (A)
convert this unit test into an integration test that constructs a real
iii_sdk::III (or a test harness that can create_channel) and invokes handle_read
end-to-end using SandboxRegistry, FakeRunnerStream and ReadRequest to assert the
produced ReadContent variant, or (B) refactor the buffer/decision logic out of
handle_read into a pure helper function (e.g., decide_read_content_from_reader
or similar) that accepts the reader/metadata and returns a ReadContent, then
write unit tests that call that helper directly with deterministic
FakeRunnerStream/fixtures (including invalid_utf8_999kib) to assert Stream vs
Utf8 outcomes; pick one approach and update both tests (lines ~113-152 and
154-169) to exercise the chosen path.
In `@docs/changelog/index.mdx`:
- Line 63: Replace incorrect repository path in the docs URLs: update the JSON
"docs_url" value(s) and any textual breaking-change links that point to
"MotiaDev/motia" to the correct "iii-hq/iii" repository and proper file path;
specifically change URLs like
"https://github.com/MotiaDev/motia/.../README.md#Sxxx" to
"https://github.com/iii-hq/iii/blob/main/crates/iii-worker/src/sandbox_daemon/README.md#Sxxx"
wherever the "docs_url" key and the breaking change explanation link reference
the old repo.
---
Nitpick comments:
In `@crates/iii-worker/src/sandbox_daemon/exec.rs`:
- Around line 231-233: The ok_or_else branch is unreachable because argv is
already checked non-empty earlier; replace the needless ok_or_else on the
iterator (`let head = it.next().ok_or_else(...)?`) with a direct unwrap/expect
(e.g., `it.next().unwrap()` or `it.next().expect("argv non-empty")`) to clarify
intent, or if you prefer to keep defensive code, add a short comment referencing
the earlier `if !argv.is_empty()` check; make the change around the `argv`,
`it.next()` and `SandboxError::InvalidRequest` usage in exec.rs.
In `@crates/iii-worker/src/sandbox_daemon/fs/adapter.rs`:
- Around line 27-42: The S211 branch's parent-path detection is case-sensitive
and can miss variants of the error text; normalize the message (e.g., compute a
lowercase/trimmed version) before checking for "parent not found" or "parent
directory not found" so the condition reliably detects parent-missing errors,
while still returning the original message text to
SandboxError::FsParentNotFound { path: ... } or passing the original message
into SandboxError::fs_not_found(...) to preserve the original payload.
In `@crates/iii-worker/src/sandbox_daemon/README.md`:
- Around line 444-460: Add 1–2 sentence descriptions for the filesystem error
codes S211, S212, S213, S214, S215, and S216 in the README where the error list
is defined so they match the level of detail of S001/S100/S200; for each code
include when it occurs and typical causes or recovery hints (e.g., S211: occurs
on read/write/stat of a missing path; S212: operation attempted vs. conflicting
file type; S213: triggered by mkdir or write with exclusive flag; S214: applies
to rm failures; S215: caused by permission/ownership/chmod issues; S216: explain
how it differs from other errors). Update the entries named S211–S216 in the
README so each has a short descriptive sentence and a brief recovery suggestion.
- Around line 12-86: The README's "Agent Quickstart" contains large inline
JSON/code examples (including sandbox::run usage, the surgical workflow, cmd
shapes, env shapes, and error handling) that should be moved into a docs page
and replaced with links; create a new docs file (e.g.,
docs/api-reference/sandbox.mdx or docs/quickstart/sandbox.mdx) containing the
full examples and explanations, then edit the sandbox_daemon README.md's Agent
Quickstart section to remove the duplicated examples and add concise
descriptions plus links to the new docs page and to the specific examples for
sandbox::run, surgical workflow, cmd/env shapes, and error handling so the
README points readers to the canonical docs.
🪄 Autofix (Beta)
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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 49697d57-ba39-46e8-82eb-e1d819dc5c2b
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (33)
crates/iii-shell-proto/Cargo.tomlcrates/iii-shell-proto/src/lib.rscrates/iii-worker/Cargo.tomlcrates/iii-worker/src/cli/sandbox.rscrates/iii-worker/src/cli/sandbox_daemon.rscrates/iii-worker/src/sandbox_daemon/README.mdcrates/iii-worker/src/sandbox_daemon/adapters.rscrates/iii-worker/src/sandbox_daemon/catalog.rscrates/iii-worker/src/sandbox_daemon/create.rscrates/iii-worker/src/sandbox_daemon/errors.rscrates/iii-worker/src/sandbox_daemon/exec.rscrates/iii-worker/src/sandbox_daemon/fs/adapter.rscrates/iii-worker/src/sandbox_daemon/fs/chmod.rscrates/iii-worker/src/sandbox_daemon/fs/grep.rscrates/iii-worker/src/sandbox_daemon/fs/ls.rscrates/iii-worker/src/sandbox_daemon/fs/mkdir.rscrates/iii-worker/src/sandbox_daemon/fs/mv.rscrates/iii-worker/src/sandbox_daemon/fs/read.rscrates/iii-worker/src/sandbox_daemon/fs/rm.rscrates/iii-worker/src/sandbox_daemon/fs/sed.rscrates/iii-worker/src/sandbox_daemon/fs/stat.rscrates/iii-worker/src/sandbox_daemon/fs/write.rscrates/iii-worker/src/sandbox_daemon/mod.rscrates/iii-worker/src/sandbox_daemon/run.rscrates/iii-worker/tests/common/sandbox_fakes.rscrates/iii-worker/tests/fake_sandbox_relay.rscrates/iii-worker/tests/sandbox_docs_anchor_stability.rscrates/iii-worker/tests/sandbox_fs_decode_error_wire.rscrates/iii-worker/tests/sandbox_fs_read_buffer_boundary.rscrates/iii-worker/tests/sandbox_lifecycle_integration.rscrates/iii-worker/tests/sandbox_workflow_integration.rsdocs/changelog/index.mdxsdk/packages/rust/iii/src/channels.rs
Drop the untagged `ReadContent { Utf8 | Stream }` enum that
serializes `content` as a bare string for small text — that change
broke peers (notably `workers/shell`) that statically type
`content` as `StreamChannelRef`.
Restore `ReadResponse.content: StreamChannelRef` (always set,
always serves bytes through the channel) and surface the inline
UTF-8 fast path through an additive `body: Option<String>` field.
Both fields point at the same bytes for small text so legacy
subscribers keep working; new callers see `body.is_some()` and
short-circuit the channel subscription.
- `handle_read` routes the UTF-8 success branch through
`stream_via_channel` with `body: Some(s)` and a Cursor over the
buffered bytes. Other branches pass `body: None`.
- `stream_via_channel` takes an optional `body` parameter.
- `INLINE_BUFFER_CAP` doc + handler description updated.
- README `sandbox::fs::read` section rewritten for the new shape.
- Changelog drops the "Breaking" classification on this entry and
describes the change as additive.
- Test `sandbox_fs_read_buffer_boundary.rs` drops `ReadContent`
imports; structural assertions retargeted at the `body` field.
- 1228/1228 integration tests pass.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
docs/changelog/index.mdx (1)
65-65:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix repository name in documentation URL.
The
docs_urlusesMotiaDev/motiabut this PR is for theiii-hq/iiirepository. The URL should be:https://github.com/iii-hq/iii/blob/main/crates/iii-worker/src/sandbox_daemon/README.md#Sxxx📝 Proposed fix
- - `docs_url` anchors directly at the in-repo `S`-code subsection. **Breaking:** the base URL flipped from `https://iii.dev/docs/errors/sandbox/Sxxx` to `https://github.com/MotiaDev/motia/blob/main/crates/iii-worker/src/sandbox_daemon/README.md#Sxxx` while the canonical `iii.dev` error pages are still pending. Bookmarks and scrapers built on the old URL need to follow the new anchors. + - `docs_url` anchors directly at the in-repo `S`-code subsection. **Breaking:** the base URL flipped from `https://iii.dev/docs/errors/sandbox/Sxxx` to `https://github.com/iii-hq/iii/blob/main/crates/iii-worker/src/sandbox_daemon/README.md#Sxxx` while the canonical `iii.dev` error pages are still pending. Bookmarks and scrapers built on the old URL need to follow the new anchors.🤖 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 `@docs/changelog/index.mdx` at line 65, Update the docs_url value to reference the correct repository name: replace any occurrences of "MotiaDev/motia" with "iii-hq/iii" so the link becomes https://github.com/iii-hq/iii/blob/main/crates/iii-worker/src/sandbox_daemon/README.md#Sxxx; specifically search for the docs_url constant/string in the changelog or docs generator code and update the base GitHub path while keeping the README.md#Sxxx anchor intact to preserve existing bookmarks.crates/iii-worker/src/sandbox_daemon/README.md (1)
99-99:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove or update forward references to non-existent documentation.
Multiple lines reference
docs/api-reference/sandbox.mdxwhich doesn't exist:
- Line 99: "See
S300indocs/api-reference/sandbox.mdx"- Lines 487-489: "see
docs/api-reference/sandbox.mdx#s-codes" with inline TODO- Line 494: Link to
docs/api-reference/sandbox.mdxAs per coding guidelines, "Ensure all references to primitives reflect those defined in docs/" — references should point to actual documentation.
Since this README already defines all S-code anchors (lines 396-486), update these references to point to the relevant sections within this README instead:
- Line 99: "See S300 below for the full diagnostic flow."
- Lines 487-489: Remove the forward reference and TODO; the anchors are already documented in this file
- Line 494: Either remove this link or change it to reference the Errors section of this README
As per coding guidelines: "
**/*.md*: Ensure all references to primitives reflect those defined in docs/"Also applies to: 487-489, 494-494
🤖 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 `@crates/iii-worker/src/sandbox_daemon/README.md` at line 99, Replace external references to docs/api-reference/sandbox.mdx with internal README anchors: change the text that currently says "See `S300` in `docs/api-reference/sandbox.mdx`" to "See [S300](`#S300`) below" (referencing the S300 anchor already defined in this README); remove the forward-reference TODO and any phrase "see `docs/api-reference/sandbox.mdx#s-codes`" (the S-code anchors are already documented in this file), and update or remove the link that points to `docs/api-reference/sandbox.mdx` so it instead points to this README's Errors/S-codes section (e.g., "Errors" or the appropriate `#S300`/`#errors` anchor) to ensure all S-code references are internal and consistent with the anchors defined in this README.
🧹 Nitpick comments (2)
crates/iii-worker/src/sandbox_daemon/fs/read.rs (1)
144-147:std::io::Cursoris compatible withtokio::io::AsyncReadhere
Tokio 1.x implementstokio::io::AsyncReadforstd::io::Cursor<T>, so boxingstd::io::Cursor<Vec<u8>>asBox<dyn tokio::io::AsyncRead + Unpin + Send>is valid. The.chain(reader)call aligns with Tokio’sAsyncReadExt::chain(given the boxedAsyncReadtypes), notstd::io::Read::chain. [Optional] If those code paths are currently skipped, add/enable coverage to exercise them.🤖 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 `@crates/iii-worker/src/sandbox_daemon/fs/read.rs` around lines 144 - 147, The code is valid because std::io::Cursor<Vec<u8>> implements tokio::io::AsyncRead, so keep creating bytes_for_channel, wrap it in std::io::Cursor::new(...) and box it as Box<dyn tokio::io::AsyncRead + Unpin + Send> (the chained variable) and call stream_via_channel(iii, chained, meta, Some(s), path). Ensure tokio::io::AsyncReadExt is in scope if you later use .chain(...) on AsyncRead objects (so the AsyncReadExt::chain method is used, not std::io::Read::chain), and add/enable test coverage to exercise this code path if it’s currently skipped.docs/changelog/index.mdx (1)
58-58: ⚡ Quick winUse the full path in the example
docs_urlfor consistency.Line 58 uses
...as a placeholder in the example URL:"docs_url": "https://github.com/iii-hq/iii/.../README.md#S211"For consistency with line 65 (which shows the full path) and to provide an accurate example, show the complete path:
"docs_url": "https://github.com/iii-hq/iii/blob/main/crates/iii-worker/src/sandbox_daemon/README.md#S211"This matches what the actual error payload contains and helps users understand the exact URL format.
🤖 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 `@docs/changelog/index.mdx` at line 58, Replace the placeholder docs_url example value so it shows the full repository path instead of "..." — update the JSON example for the "docs_url" key (the example string on the line containing "docs_url": "https://github.com/iii-hq/iii/.../README.md#S211") to the complete path used in real payloads, e.g. "https://github.com/iii-hq/iii/blob/main/crates/iii-worker/src/sandbox_daemon/README.md#S211", ensuring the example matches the full path shown elsewhere in the changelog.
🤖 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.
Duplicate comments:
In `@crates/iii-worker/src/sandbox_daemon/README.md`:
- Line 99: Replace external references to docs/api-reference/sandbox.mdx with
internal README anchors: change the text that currently says "See `S300` in
`docs/api-reference/sandbox.mdx`" to "See [S300](`#S300`) below" (referencing the
S300 anchor already defined in this README); remove the forward-reference TODO
and any phrase "see `docs/api-reference/sandbox.mdx#s-codes`" (the S-code
anchors are already documented in this file), and update or remove the link that
points to `docs/api-reference/sandbox.mdx` so it instead points to this README's
Errors/S-codes section (e.g., "Errors" or the appropriate `#S300`/`#errors`
anchor) to ensure all S-code references are internal and consistent with the
anchors defined in this README.
In `@docs/changelog/index.mdx`:
- Line 65: Update the docs_url value to reference the correct repository name:
replace any occurrences of "MotiaDev/motia" with "iii-hq/iii" so the link
becomes
https://github.com/iii-hq/iii/blob/main/crates/iii-worker/src/sandbox_daemon/README.md#Sxxx;
specifically search for the docs_url constant/string in the changelog or docs
generator code and update the base GitHub path while keeping the README.md#Sxxx
anchor intact to preserve existing bookmarks.
---
Nitpick comments:
In `@crates/iii-worker/src/sandbox_daemon/fs/read.rs`:
- Around line 144-147: The code is valid because std::io::Cursor<Vec<u8>>
implements tokio::io::AsyncRead, so keep creating bytes_for_channel, wrap it in
std::io::Cursor::new(...) and box it as Box<dyn tokio::io::AsyncRead + Unpin +
Send> (the chained variable) and call stream_via_channel(iii, chained, meta,
Some(s), path). Ensure tokio::io::AsyncReadExt is in scope if you later use
.chain(...) on AsyncRead objects (so the AsyncReadExt::chain method is used, not
std::io::Read::chain), and add/enable test coverage to exercise this code path
if it’s currently skipped.
In `@docs/changelog/index.mdx`:
- Line 58: Replace the placeholder docs_url example value so it shows the full
repository path instead of "..." — update the JSON example for the "docs_url"
key (the example string on the line containing "docs_url":
"https://github.com/iii-hq/iii/.../README.md#S211") to the complete path used in
real payloads, e.g.
"https://github.com/iii-hq/iii/blob/main/crates/iii-worker/src/sandbox_daemon/README.md#S211",
ensuring the example matches the full path shown elsewhere in the changelog.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 8f7aa9be-4dd1-44e4-8fd0-520f23c0650c
📒 Files selected for processing (4)
crates/iii-worker/src/sandbox_daemon/README.mdcrates/iii-worker/src/sandbox_daemon/fs/read.rscrates/iii-worker/tests/sandbox_fs_read_buffer_boundary.rsdocs/changelog/index.mdx
- Validate env in sandbox::create BEFORE emitting BootingVm so a bad env never triggers a phantom boot-start event (CodeRabbit). - Fix DOCS_BASE and changelog URLs from MotiaDev/motia to iii-hq/iii (canonical repo name; GitHub redirect could break). - Update env-name docs and the user-facing InvalidRequest message in exec.rs to the actually-validated pattern `[A-Za-z_][A-Za-z0-9_]*`. - Drop the lowercase-heading fallback in sandbox_docs_anchor_stability so a lowercase rename breaks CI (the test header already claimed this contract). - Replace the misleading "/bin/sh -lc" comment in run.rs::interpreter_for and document why bash falls back to /bin/sh. - Remove the dead forward reference to docs/api-reference/sandbox.mdx#s-codes from the README; the "See also" block below still links the page. - Drop the schemars doc comment in iii-shell-proto/Cargo.toml per reviewer request. 1228/1228 integration tests pass; cargo fmt clean.
Ignore .serena/ (Serena tooling cache: memories, project config) and CLAUDE.md (project-level agent guidance with host-specific gbrain setup notes). Per-contributor state, not shared.
…Summary
Adds `config: Value` to `RegisteredTriggerSummary` (the API response
shape) alongside the existing `config_summary` display string.
`config_summary` is kept for backward compatibility but is a
truncated/lossy representation; consumers that need the structured
payload (api_path, http_method, topic, expression, …) now have a
canonical field to read from.
Frontend (console-frontend) consumes it defensively with `?? {}`
fallbacks so older / leaner summaries that omit the field don't
crash list rendering or invocation code.
- engine_fn/mod.rs: RegisteredTriggerSummary gains `config: Value`,
populated in both list paths. Unit test asserts the field is
serialized to JSON.
- console-frontend `TriggerInfo.config` is now optional; triggers
list view, flows normalisation, and the invocation forms all
narrow with `?? {}` before reading nested fields.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@console/packages/console-frontend/src/routes/triggers.tsx`:
- Around line 443-447: The invokeHttp function should validate required trigger
config fields before making network calls: in invokeHttp, check that
config.api_path is present and non-empty (after trimming slashes) and that the
resolved method is valid, and if missing/invalid return or throw a clear
client-side error instead of proceeding to call root "/"; likewise, for the
event invoke path (the code around lines handling event invokes e.g., the
function that uses topic in lines ~555-558), validate that the topic is
non-empty before sending and surface an immediate error; locate these checks by
the invokeHttp function name and the event-invoke block and add guard clauses
that fail fast with descriptive errors when required fields are absent.
🪄 Autofix (Beta)
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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 0a4a4b39-7aca-491a-82ae-088dbd93f06e
📒 Files selected for processing (5)
.gitignoreconsole/packages/console-frontend/src/api/events/functions.tsconsole/packages/console-frontend/src/api/flows/flows.tsconsole/packages/console-frontend/src/routes/triggers.tsxengine/src/workers/engine_fn/mod.rs
✅ Files skipped from review due to trivial changes (1)
- .gitignore
Summary
Ships the full
sandbox::*AI-agent DX work in one commit. Reduces workflow TTHW (time-to-hello-world) from 4 tool calls to 1, returns structured/resubmittable errors from every sandbox function, and wires handler-boundary tracing across all 17 sandbox handlers.Highlights
sandbox::run— meta-function composingcreate + fs::write + exec + stopinto a single call. Auto-stops the VM on success/failure unlesskeep_sandbox: true.sandbox::catalog::list— new function returning the daemon's image catalog (bundled presets + operator-registeredcustom_images). Closes the "what images exist here" discovery loop.exec.cmdacceptscmd + args,argvarray, or shell-linecmd(shlex-split).exec.env/create.envaccept bothVec<"K=V">and{ K: V }map shapes.is_valid_env_namepins names to[A-Za-z_][A-Za-z0-9_]*(rejects digit-leading //---=withS001).fs::readinline body — returns UTF-8 string for text under 1 MiB; large/binary still streams viaStreamChannelRef.sandbox::*returns{ code, type, message, docs_url, retryable, fix, fix_note }.docs_urlanchored at in-repo README (#S001,#S211, ...).fixis a non-null JSON payload the agent can resubmit verbatim when recovery is unambiguous (e.g.FsParentNotFoundcarries{ "parents": true }).RunStepFailed— when asandbox::runsub-step fails,inner_codesurfaces transparently andfix.contextnames the failing step plusfix.sandbox_idwhenkeep_sandbox: true.SandboxErrorWirewrapper makesDisplayemit the JSON payload through theiii_sdkasync handler boundary so envelopes round-trip without per-handler serialization.npm install/pip install/cargo build. The old 30s default fired as an opaque engine-gate denial before the daemon could return a structuredtimed_out:trueresponse.log_handler_resultemitstracing::info!on both success and error with a stable field set (function_id,sandbox_id,success,error_code,error_type,retryable,duration_ms). Wired into all 17 sandbox handlers.sandbox::fs::*functions migrated from legacyRegisterFunctionMessagetoRegisterFunction::new_asyncwith full JSON Schema, field docs, and worked examples.FsEntry,FsMatch,FsSedFileResult,FsReadMeta,StreamChannelRef,ChannelDirectionall gainJsonSchemaderives.S-code anchored subsections (<a id="Sxxx"></a>) plus an Agent Quickstart.SANDBOX_AGENT_GUIDEis a paste-into-system-prompt cheat sheet (doctest pins contents against wire format).New tests
sandbox_docs_anchor_stability.rs— everySandboxErrorCodehas a README anchor.sandbox_fs_decode_error_wire.rs— decode-error wire shape.sandbox_fs_read_buffer_boundary.rs— byte-loss boundary check for the inline-UTF-8 read fast path.Backwards compatibility
Wire shape stays additive: missing
argv/envfields default to empty, existingVecenv shape still deserializes, error code propagation still works viamap_iii_err's three-path matching. Shell worker (workers/shell) regression-checked: 20/20 sandbox dispatch tests pass unchanged.Test plan
iii-workerlib tests pass (770+, incl. 5 new:catalog::list,RunStepFailedwire shape,FsParentNotFoundfix payload, handler-boundary trace shape)iii-workerintegration tests pass (1228 / 1228, 7 pre-existing ignored)workers/shellsandbox dispatch tests pass (20/20)SANDBOX_AGENT_GUIDEcontents against the wire formatsandbox::runend-to-end with a real workloadsandbox::catalog::listagainst a daemon withcustom_imagesregisteredSummary by CodeRabbit
New Features
Breaking Changes