Move auth config under [web.auth] in arc.toml to group web-specific
settings together. Add WebConfig with url field (default localhost:5173).
Add `arc doctor` command with checks for config, API, web, LLM providers,
Brave Search, sandbox, and GitHub App. Extract Provider::api_key_env_vars
and has_api_key to deduplicate validation logic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ANSI color to `arc models` and `arc models test` output when stdout
is a TTY: bold model IDs, dim provider/aliases, cyan speed, green/red
test results. Add `Styles::detect_stdout()` to arc-util.
Also fix pre-existing clippy warnings: derive Default instead of manual
impls for enums in server_config, remove unused FailureDetail imports
in arc-workflows error tests, inline print literal in test_models header.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Some deployment environments (e.g. container orchestrators) make it
easier to pass secrets as single-line base64 strings rather than
multi-line PEM. Both env vars now auto-detect the format: if the value
starts with "-----" it's treated as raw PEM, otherwise base64-decoded.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Format context window as 1m/200k/128k instead of raw token counts.
Add input/output cost per million tokens column.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace mercury and mercury-coder with mercury-2 (128K context,
reasoning support, $0.75/M output). Remove mercury-edit (code editing
model not needed in catalog).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Normalize string option values ("1.18") to {"version": "1.18"} so
shorthand syntax correctly sets the VERSION env var
- Add option_id_to_env_name() to convert option IDs like "node-version"
to NODE_VERSION per the dev container spec
- Emit _REMOTE_USER, _CONTAINER_USER, _REMOTE_USER_HOME, and
_CONTAINER_USER_HOME in feature install snippets, threading remoteUser
from devcontainer.json through resolve_features/generate_layer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract parse_sandbox_provider, resolve_sandbox_provider, and
resolve_daytona_config helpers to eliminate 4 near-identical sandbox
parsing chains. Compute sandbox_provider once with proper error handling
instead of twice (preview silently swallowed errors). Fix bug where
run_preflight did not fall back to run_defaults for daytona config.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename AppConfig → ServerConfig with flattened RunDefaults so users can
set default llm, sandbox, setup, directory and vars in ~/.arc/arc.toml.
Precedence: CLI flags > workflow TOML > server config defaults > DOT
graph attrs > hardcoded defaults. Vars merge (defaults first, task
config overwrites collisions).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add reloadAppConfig() so setup-callback refreshes the in-memory config
after writing TOML, fixing a bug where the login redirect would fail.
Export ARC_CONFIG_PATH to eliminate duplicated path construction.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Non-secret config (app_id, client_id) now lives in [git] section of
~/.arc/arc.toml. Secrets remain in .env. Setup callback writes
non-secrets to TOML and secrets to .env.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract read_feature_metadata(), extract_tgz(), and create_feature_dir()
helpers to eliminate copy-paste across fetch_feature_oci/local/https
- Move ensure_oras() out of per-feature calls into a once-per-resolve
check via oras_checked flag
- Simplify dockerfile::generate() to take &HashMap instead of
&Option<HashMap>, removing an unnecessary clone in the caller
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create a local-features fixture with 3 local feature directories that
exercise all gap fixes through the full DevcontainerResolver pipeline:
- dependsOn auto-injection (base-utils not in features map, injected)
- feature containerEnv merge (3 features contribute env vars)
- feature lifecycle hooks (appended after devcontainer.json commands)
- local path feature references (all features use ./ paths)
- install ordering (dependsOn/installsAfter edges respected)
Also fix a concurrency bug: resolve_features now uses a unique temp
dir per invocation instead of a shared /tmp/devcontainer-features/
that caused parallel test corruption.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document support for: feature dependsOn, feature containerEnv, feature
lifecycle hooks (onCreateCommand/postCreateCommand/postStartCommand),
subdirectory file discovery, and local path/HTTPS feature references.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Features can now be referenced as local paths (./feature or ../feature)
or HTTPS URLs (https://example.com/feature.tgz) in addition to OCI
registry references. A dispatch function routes fetches based on the
feature ID prefix. The oras CLI is only required for OCI refs.
Also updates dir_name_from_id to handle local paths (strip ./) and
URLs (strip .tgz extension), and passes devcontainer_dir to
resolve_features instead of build_context.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Better reflects domain semantics: the config describes a workflow run,
and the free-text field is the run's goal, not a generic "task".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When neither .devcontainer/devcontainer.json nor .devcontainer.json
exists, scan .devcontainer/ for subdirectories containing a
devcontainer.json. Subdirectories are sorted alphabetically and the
first match is used. Standard locations still take priority.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Features can now declare onCreateCommand, postCreateCommand, and
postStartCommand lifecycle hooks. These are collected in install order
via ResolvedFeatures and appended after the devcontainer.json lifecycle
commands during resolution.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add containerEnv field to FeatureMetadata and introduce ResolvedFeatures
struct that collects container_env from each feature in install order.
Feature containerEnv is merged with devcontainer.json containerEnv
(devcontainer.json wins on conflicts) before generating the Dockerfile.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add dependsOn field to FeatureMetadata for declaring hard feature
dependencies per the devcontainer spec. The topo_sort function now
handles both installsAfter and dependsOn edges with deduplication.
Missing dependsOn targets are auto-injected (fetched and added to
the sorted feature set with the specified options).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Stop baking remoteEnv into Dockerfile (only containerEnv belongs as ENV)
- Merge forwardPorts with compose ports in compose mode (with dedup)
- Support build.target (parsed with variable substitution)
- Handle forwardPorts string formats like "8080:80" and "9090"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove RunMode enum (only Preflight was checked; DryRun/Normal unused)
— use args.preflight directly
- Switch default_model_for_provider() to take Provider enum instead of
Option<&str> for exhaustive matching; adds missing Inception default
- Extract sandbox init/cleanup into creation + single check pattern,
eliminating 3x copy-paste of identical init/cleanup boilerplate
- Replace setup_commands Vec clone with direct count (only .len() used)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add onCreateCommand lifecycle hook (parsed, resolved, exposed as on_create_commands)
- Expose build.args on DevcontainerConfig for docker build --build-arg
- Wire containerEnv into generated Dockerfile as ENV directives (remoteEnv overrides on collision)
- Support dockerComposeFile as array of paths with merge semantics (last wins for image/build/user, ports accumulate, env overrides)
- Update DEVCONTAINER-COMPATIBILITY.md and INTEGRATION.md docs
- Add e2e tests with realistic Python and compose project fixtures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verifies sandbox boot (local/docker/daytona), LLM provider availability,
and model/provider resolution chain, then prints a structured report.
Extracts model/provider resolution into reusable helpers
(default_model_for_provider, resolve_model_provider) with tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace ARC_INSECURE_DISABLE_AUTHENTICATION and ARC_API_BASE_URL env vars
with [auth] and [api] sections in ~/.arc/arc.toml. Only secrets
(ARC_JWT_PUBLIC_KEY, ARC_JWT_PRIVATE_KEY) remain as env vars.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Standalone crate that reads devcontainer.json (with JSONC support), fetches
OCI Features via oras, and produces a resolved config containing a generated
Dockerfile, lifecycle hooks, environment variables, and forwarded ports.
Supports image, Dockerfile, and Docker Compose modes with devcontainer
variable substitution. No coupling to arc-workflows or DaytonaSandbox.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parallel branches now include the node visit count (pass1, pass2, etc.)
in their ref names, preventing silent overwrite when a parallel node is
re-executed via retry or loop_restart.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace cookie-based sessions with SQLite-backed storage using
better-sqlite3 and React Router's createSessionStorage. Sessions are
now stored in ~/.arc/arc-web.db with a session ID cookie, enabling
larger payloads and server-side revocation.
- Add db.server.ts (lazy singleton, WAL mode, web_sessions table)
- Add session-storage.server.ts (CRUD ops, probabilistic cleanup)
- Fetch primary verified email from /user/emails during OAuth
- Add emails:read to GitHub App manifest default_permissions
- Expand session data: userUrl, githubId, githubNodeId, email
- Default ARC_API_BASE_URL to localhost:3000
- Whitelist better-sqlite3 in trustedDependencies
- Externalize better-sqlite3 from Vite SSR bundling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename node visit suffix from `-attempt_{V}` to `-visit_{V}` and move
asset collection under `artifacts/assets/` alongside artifact values in
`artifacts/values/`. Retry directories use `retry_{N}` instead of the
ambiguous `attempt_{N}`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Write the private key as base64 to avoid multiline quoting issues.
Add getGitHubAppPrivateKey() that accepts both raw PEM and base64.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shared layout for setup, login, and callback pages with consistent
styling. Add GitHub mark icon to action buttons. Document dev server
commands in CLAUDE.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Set env vars on process.env at runtime after writing .env, then redirect
to /auth/login. Removes the success UI telling users to restart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automatically discovers and collects well-known output files (test reports,
screenshots, trace files) from each node's execution into its log directory.
Works across all three sandbox types (local, Docker, Daytona) via a new
`download_file_to_local` trait method that handles binary files correctly.
- New `asset_snapshot` module with pure functions for find command generation,
output parsing, candidate matching, and budget enforcement
- `Sandbox::download_file_to_local` implemented for Local (fs::copy),
Docker (host bind-mount resolution), and Daytona (SDK download)
- `AssetsCaptured` event variant with DEBUG-level tracing
- Engine integration: baseline snapshot before handler, collection after
(both success and error paths), non-fatal on errors
- E2e tests for local sandbox (2 tests), Docker (#[ignore]), Daytona (#[ignore])
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds one-click GitHub App setup via the manifest flow, OAuth login
via Arctic, and cookie-based sessions so the app shell shows the
real authenticated user instead of a hardcoded placeholder.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add node_id to Stage* enum variants so both the programmatic ID and
display label are available. Add rename_fields() post-processing in
flatten_event() to give flattened JSONL fields self-describing names:
- timestamp → ts (save space)
- name → node_label (Stage*), workflow_name, snapshot_name
- index → stage_index, branch_index, command_index
- stage → node_id (Agent.*, Interview*, Prompt)
- branch → node_id (ParallelBranch*)
- node → node_id (StallWatchdogTimeout)
- from_node/to_node → from_node_id/to_node_id
- start_node → start_node_id
- provider → sandbox_provider (Sandbox.*)
- text → prompt_text (Prompt)
- Insert node_label defaulting to node_id where only an id exists
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of flattening the inner event's fields into the top level
(which causes collisions when SubAgentEvent wraps SubAgentEvent —
losing inner agent_id, depth, and nested event body), keep the inner
event as a `nested_event` JSON value. The dot-notation event name
still reflects the inner type for easy filtering.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WorkflowRunStarted and GitCheckpoint have a `run_id` field that
collides with the envelope's top-level `run_id`. Filter out
`timestamp`, `run_id`, and `event` from event fields to avoid
duplicate keys in the JSONL output.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Event data was nested inside a tagged enum (`"event": {"StageStarted": {fields}}`).
Now `event` is a string name and fields merge into the top-level object
(`"event": "StageStarted", "name": "plan", ...`).
Nested events use dot notation:
- Agent wrapper: "Agent.ToolCallStarted" with stage at top level
- Sandbox wrapper: "Sandbox.Initializing" with fields at top level
- SubAgentEvent: "Agent.SubAgentEvent.ToolCallStarted" flattened one level
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>