feat: Phase 47 β Tool Execution With Sandboxing (v4.0.0-alpha.7) - #54
Merged
Merged
Conversation
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.
Summary
Closes the boundary v2 Β§30 opened (tool calls are emitted, never executed) and
v3 Β§46 extended (multi-step chains, still emit-only): the model's tool calls
can now be actually executed by aarambh-studio itself β but only inside a
strict, closed-world sandbox. This is the highest-risk phase before Phase 51
and is scoped conservatively on purpose: there is no generic "run a shell
command" or "eval this code" executor anywhere in the crate, by design.
Targets milestone
v4.0.0-alpha.7perROADMAP_V4.mdPhase 47.What changed
crates/aarambh-studio-agent(extended β no new crate, no new dependency)src/sandbox.rsβ the closed-world execution sandbox:ToolExecutortrait: one specific, named capability per implementor(
name() -> &'static str+execute(&ValidatedArgs, &ExecContext) -> Result<ToolResultContent, ExecError>).ToolSandbox::execute()enforces the fullARCHITECTURE_V4.mdΒ§61pipeline, in order:
ExecError::UnknownTool(hard refusal, no attempt).AuthorizationScopeβ elseExecError::Unauthorized(distinct fromUnknownTool).max_args_bytes, default 8 KiB).recv_timeoutwall-clock ceiling (timeout_ms, default 5 s) with cooperative cancellation via anAtomicBoolflag; detached-on-timeout since safe Rust cannot force-kill a thread.max_output_bytes, default 64 KiB).ToolResult{status:Error, error:...}β the chain records the refusal and continues.SandboxedToolProviderimplementsToolResultProvider, so execution plugs into the existingToolChainwith zero chain changes β results re-enter via the unchangedresult_ingestionpath. Execution is purely additive to v3 Β§46.ReadFileInWorkdir(the milestone read-only, workdir-confined file lookup β refuses absolute paths/..traversal, caps bytes, no network/write access) andStaticLookup(in-memory keyβtext for deterministic tests).src/authorization.rsβAuthorizationScope: the operator's closed set ofenabled tool names (operator decision, not model decision).
intersect()supports Phase 48 sub-agent scope narrowing (a sub-agent's scope can only be
a subset of its orchestrator's).
src/lib.rsβ exports the new modules/types.aarambh-studio(CLI)src/cmd/agent.rsβ new flags:--execute-tools,--allow-tool <NAME>(repeatable),
--exec-timeout-ms,--exec-max-output-bytes,--exec-workdir <DIR>. NewCliResultProvider::Sandboxvariant.validate_sandbox_config()runs before model load so operator configerrors surface immediately;
build_sandbox_provider()constructs the sandboxafter the tool definitions are loaded.
Version + docs
Cargo.toml/Cargo.lock:4.0.0-alpha.6β4.0.0-alpha.7.ROADMAP_V4.md: Phase 47 tasks marked[x], milestone tag updated, status note added.CHANGELOG.md: new[4.0.0-alpha.7]section.README.md: v4 feature paragraph, "Current Boundaries" tool-chain bullet, docs link, citation version.docs/phase47_sandbox.md: new runbook.docs/README.md: phase 47 entry.ARCHITECTURE_V4.mdΒ§61 +SELF_LEARNING_V4.mdΒ§47: implementation notes.Tests + smoke
sandbox.rs(real bodies) + supporting tests inauthorization.rs:unlisted_tool_name_is_hard_refused_never_attemptedunauthorized_but_declared_tool_is_refused_at_execution_not_declarationexecution_timeout_kills_a_hanging_tool_callexecution_respects_configured_memory_and_cpu_ceilingmalformed_tool_call_json_is_never_executedexecution_result_re_ingests_correctly_into_the_next_chain_step(drives the realToolChain+FakeDecoder+SandboxedToolProvider+StaticLookup)scripts/phase47_smoke.sh: agent-crate sandbox/authorization unit tests + CLI flag checks + two operator-error-path checks + scorecard atartifacts/phase47_sandbox_smoke.json.data/tools_sandbox_smoke.json: theread_file_in_workdirtool definition.Design decisions
invariant and the rustsec audit footprint unchanged. Only
std::thread/std::sync(mpsc,atomic,Arc) + existingserde/thiserrorare used.SandboxedToolProvider: ToolResultProvidermeansthe existing
ToolChainis untouched; execution is purely additive to v3 Β§46'sresult_ingestionpath.request any tool, but only operator-enabled names ever execute.
an unrecognised name is a hard refusal, never a best-effort fallback.
Honesty boundary
Phase 47's sandbox is pure-Rust and CPU-only: wall-clock timeout (cooperative
cancellation + thread-detachment on timeout, since safe Rust cannot force-kill a
thread), output/argument-size ceilings, closed-world allowlist, operator
authorization, and schema re-validation. OS-level isolation (seccomp/cgroups/
namespaces) is out of scope for the source release, consistent with the project's
CPU-first posture. The safety-relevant property β a runaway or hung call never
blocks the chain and always produces a fail-closed result β holds under every
tested failure condition. A general-purpose code-execution sandbox remains
explicitly out of scope: Phase 47 is strictly closed-world, named-capability tool
execution, never arbitrary code or shell execution.
CI gates verified locally
cargo fmt --all --checkcargo check --workspace --all-targets --lockedcargo clippy --workspace --all-targets --locked -- -D warnings -D clippy::undocumented_unsafe_blockscargo +1.89.0 check --workspace --all-targets --locked(MSRV)RUSTDOCFLAGS="-D warnings -D missing_docs" cargo doc --workspace --no-deps --lockedscripts/phase28_release_audit.sh(passes for4.0.0-alpha.7: 20 packages, one version, no TODO/FIXME markers, no model artifacts, nocargo publishin workflows)bash -non all scriptscargo build --release --locked -p aarambh-studio(26 MB binary)--helpsmoke (incl.agent --helpsurfacing the 5 new flags)scripts/phase47_smoke.sh(9 sandbox + 6 authorization unit tests; flag checks; operator error-path checks; scorecard)cargo test -p aarambh-studio-agent --lib(22/22)cargo test -p aarambh-studio --lib(6/6)