Fix stale labels, grammar, and comments from sandbox rename

- Fix display label `image:` → `name:` for SnapshotPulling/SnapshotPulled
  in format_event_detail
- Fix grammar: "an Sandbox" → "a Sandbox" in README and parallel.rs
- Fix typo: "sandboxs" → "sandboxes" in parallel.rs
- Update remaining "execution environment" comments to "sandbox"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-03-02 11:43:34 -05:00
parent d0c8bd3eed
commit 79d97ebb5c
15 changed files with 81 additions and 19 deletions

View file

@ -0,0 +1,60 @@
import { describe, test, expect, beforeEach, mock } from "bun:test";
import { apiJson } from "./api-client";
const originalFetch = globalThis.fetch;
beforeEach(() => {
globalThis.fetch = originalFetch;
});
describe("apiJson", () => {
test("returns parsed JSON on 200", async () => {
globalThis.fetch = mock(() =>
Promise.resolve(new Response(JSON.stringify({ id: 1, name: "test" }), {
status: 200,
headers: { "Content-Type": "application/json" },
}))
);
const result = await apiJson<{ id: number; name: string }>("/items/1");
expect(result).toEqual({ id: 1, name: "test" });
});
test("throws Response with status 404 and null body on not found", async () => {
globalThis.fetch = mock(() =>
Promise.resolve(new Response("Not Found: /items/999", { status: 404 }))
);
try {
await apiJson("/items/999");
expect.unreachable("should have thrown");
} catch (thrown) {
expect(thrown).toBeInstanceOf(Response);
const res = thrown as Response;
expect(res.status).toBe(404);
expect(res.body).toBeNull();
}
});
test("throws Response with status 500 and null body, stripping sensitive details", async () => {
globalThis.fetch = mock(() =>
Promise.resolve(
new Response(
"Internal error: database connection string is postgres://admin:secret@db.internal:5432/prod",
{ status: 500 }
)
)
);
try {
await apiJson("/items/1");
expect.unreachable("should have thrown");
} catch (thrown) {
expect(thrown).toBeInstanceOf(Response);
const res = thrown as Response;
expect(res.status).toBe(500);
expect(res.body).toBeNull();
}
});
});

View file

@ -51,6 +51,6 @@ export async function apiFetch(
*/
export async function apiJson<T>(path: string, init?: RequestInit): Promise<T> {
const res = await apiFetch(path, init);
if (!res.ok) throw new Response(await res.text(), { status: res.status });
if (!res.ok) throw new Response(null, { status: res.status });
return res.json() as Promise<T>;
}

View file

@ -6,6 +6,7 @@
"build": "react-router build",
"dev": "react-router dev",
"start": "react-router-serve ./build/server/index.js",
"test": "ARC_API_BASE_URL=http://localhost:9999 bun test",
"typecheck": "react-router typegen && tsc"
},
"dependencies": {

View file

@ -5,6 +5,7 @@
"**/.client/**/*",
".react-router/types/**/*"
],
"exclude": ["**/*.test.ts"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["node", "vite/client"],

View file

@ -9,7 +9,7 @@ The crate is organized around a central `Session` that drives an agentic loop:
1. **User input** is appended to a conversation `History`
2. The session builds a `Request` with system prompt, history, and tools
3. An LLM generates a response (text and/or tool calls) via `unified-llm`
4. Tool calls are executed through a `ToolRegistry` against an `Sandbox`
4. Tool calls are executed through a `ToolRegistry` against a `Sandbox`
5. Results are recorded and the loop continues until the LLM responds with text only (natural completion), a turn limit is reached, or the session is aborted
```

View file

@ -382,7 +382,7 @@ pub async fn run_with_args(args: AgentArgs) -> anyhow::Result<()> {
eprintln!("{}Using model: {model}{}", styles.dim, styles.reset,);
let mut profile = build_profile(provider, model, Some(client.clone()));
// Build execution environment
// Build sandbox
let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let cwd_str = cwd.to_string_lossy().to_string();
let env: Arc<dyn crate::Sandbox> = Arc::new(LocalSandbox::new(cwd));

View file

@ -15,7 +15,7 @@ use std::collections::HashMap;
use std::time::Instant;
use tokio_util::sync::CancellationToken;
/// Configuration for a Docker-based execution environment.
/// Configuration for a Docker-based sandbox.
pub struct DockerSandboxConfig {
/// Docker image to use. Default: `"arc-agent:latest"`.
pub image: String,
@ -53,7 +53,7 @@ impl Default for DockerSandboxConfig {
}
}
/// Execution environment that runs all operations inside a Docker container.
/// Sandbox that runs all operations inside a Docker container.
///
/// The host working directory is bind-mounted at `container_mount_point`. All file
/// operations, commands, grep, and glob execute inside the container via `docker exec`.

View file

@ -170,7 +170,7 @@ impl Session {
let today = chrono::Local::now().format("%Y-%m-%d").to_string();
let model_name = self.provider_profile.model().to_string();
// Detect git info via execution environment
// Detect git info via sandbox
let git_branch = self
.sandbox
.exec_command("git rev-parse --abbrev-ref HEAD", 5000, None, None, None)

View file

@ -199,7 +199,7 @@ impl Sandbox for MockSandbox {
// --- MutableMockSandbox ---
/// A mock execution environment with Mutex-protected files for tests that need
/// A mock sandbox with Mutex-protected files for tests that need
/// write operations to be visible to subsequent reads (e.g., `apply_patch` tests).
pub struct MutableMockSandbox {
pub files: Mutex<HashMap<String, String>>,

View file

@ -124,7 +124,7 @@ pub fn parse_v4a_patch(text: &str) -> Result<Vec<PatchOperation>, String> {
Ok(ops)
}
/// Applies a list of patch operations using the given execution environment.
/// Applies a list of patch operations using the given sandbox.
///
/// # Errors
/// Returns an error if any file operation fails.

View file

@ -35,7 +35,7 @@ pub struct ServeArgs {
#[arg(long)]
pub dry_run: bool,
/// Execution environment for agent tools
/// Sandbox for agent tools
#[arg(long, value_enum)]
pub sandbox: Option<SandboxProvider>,

View file

@ -110,7 +110,7 @@ pub struct RunArgs {
#[arg(short, long, action = clap::ArgAction::Count)]
pub verbose: u8,
/// Execution environment for agent tools
/// Sandbox for agent tools
#[arg(long, value_enum)]
pub sandbox: Option<SandboxProvider>,
@ -868,10 +868,10 @@ pub fn format_event_detail(event: &WorkflowRunEvent, styles: &Styles) -> String
format!("{d}── SANDBOX_CLEANUP_FAILED ──────────────────{r}\n {d}provider:{r} {provider}\n {d}error:{r} {error}\n")
}
SandboxEvent::SnapshotPulling { name } => {
format!("{d}── SANDBOX_SNAPSHOT_PULLING ───────────────────{r}\n {d}image:{r} {name}\n")
format!("{d}── SANDBOX_SNAPSHOT_PULLING ───────────────────{r}\n {d}name:{r} {name}\n")
}
SandboxEvent::SnapshotPulled { name, duration_ms } => {
format!("{d}── SANDBOX_SNAPSHOT_PULLED ────────────────────{r}\n {d}image:{r} {name}\n {d}duration_ms:{r} {duration_ms}\n")
format!("{d}── SANDBOX_SNAPSHOT_PULLED ────────────────────{r}\n {d}name:{r} {name}\n {d}duration_ms:{r} {duration_ms}\n")
}
SandboxEvent::SnapshotEnsuring { name } => {
format!("{d}── SANDBOX_SNAPSHOT_ENSURING ───────────────{r}\n {d}name:{r} {name}\n")

View file

@ -376,7 +376,7 @@ pub async fn run_command(args: RunArgs, styles: &'static Styles) -> anyhow::Resu
if let Ok(handle) = rt {
handle.spawn(async move {
if let Err(e) = sandbox_for_cleanup.cleanup().await {
tracing::warn!(error = %e, "Execution environment cleanup failed");
tracing::warn!(error = %e, "Sandbox cleanup failed");
eprintln!("Warning: sandbox cleanup failed: {e}");
}
});

View file

@ -16,7 +16,7 @@ use crate::outcome::{Outcome, StageStatus};
use super::{EngineServices, Handler};
// ---------------------------------------------------------------------------
// WorktreeSandbox — decorates an Sandbox with a custom working dir
// WorktreeSandbox — decorates a Sandbox with a custom working dir
// ---------------------------------------------------------------------------
/// Wraps an existing `Sandbox` so that all operations use a
@ -256,7 +256,7 @@ impl Handler for ParallelHandler {
None
};
// Build per-branch sandboxs (sequentially for git setup)
// Build per-branch sandboxes (sequentially for git setup)
struct BranchSetup {
target_id: String,
branch_index: usize,

View file

@ -7608,10 +7608,10 @@ async fn large_context_values_are_offloaded_to_artifact_store() {
}
// ---------------------------------------------------------------------------
// Artifact sync to remote execution environments
// Artifact sync to remote sandboxs
// ---------------------------------------------------------------------------
/// A mock execution environment where `file_exists` always returns false,
/// A mock sandbox where `file_exists` always returns false,
/// simulating a remote container that doesn't have local artifact files.
struct RemoteMockEnv {
working_dir: String,
@ -7924,7 +7924,7 @@ async fn node_dir_uses_visit_count_on_revisit() {
use arc_workflows::cli::cli_backend::{BackendRouter, AgentCliBackend};
/// A mock execution environment for CLI backend e2e tests.
/// A mock sandbox for CLI backend e2e tests.
/// Records all exec_command and write_file calls, and returns configurable
/// responses based on command content.
struct CliTestEnv {
@ -8966,7 +8966,7 @@ use arc_workflows::engine::GitCheckpointMode;
use arc_workflows::handler::fan_in::FanInHandler;
use arc_workflows::handler::parallel::ParallelHandler;
/// A handler that writes a file named `{node_id}.txt` into the execution environment's
/// A handler that writes a file named `{node_id}.txt` into the sandbox's
/// working directory. Used to verify git worktree isolation in parallel branches.
struct FileWriterHandler;