LLM responses were printing raw Markdown (bold markers, table pipes,
heading hashes). Use termimad to render with proper ANSI formatting
when color is enabled, falling back to plain text when NO_COLOR is set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sandbox cleanup was spawned fire-and-forget in a scopeguard, causing DNS
resolution to be cancelled when the tokio runtime shut down before the
HTTP request completed. Now cleanup is awaited explicitly before
returning, with the scopeguard kept only as a safety net for panics.
Also: add blank line before the warning and color-code it yellow to
match other warnings. Update daytona-sdk to include full error source
chain in error messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace plain dimmed text output with cargo/BuildKit-style progress bars
using indicatif MultiProgress. Shows spinners during active stages and
tool calls, right-aligned timers, green/red glyphs for completion status,
and stage costs. Falls back to simple eprintln for non-TTY. Verbose mode
is unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automates first-time setup: generates server.toml, Ed25519 JWT keypair,
mTLS CA+server certificates, session secret, and .env file with proper
permissions. Includes pre-flight system dependency checks shared with
doctor, LLM provider API key collection, and optional doctor verification.
Also renames config file from arc.toml to server.toml across the codebase,
and loads ~/.arc/.env before CWD .env for centralized secret management.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
HumanCount's comma-separated output (1,234) is less scannable for token
counts. Restore k/m suffix formatting with added millions support. Remove
indicatif dependency from arc-agent since it no longer uses it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hand-rolled format_duration_human, format_tokens_human, and
format_token_count with indicatif's HumanDuration, HumanCount, and
HumanBytes. Token counts now display as comma-separated (e.g. "1,234")
instead of abbreviated (e.g. "1.2k"), and byte counts show units
(e.g. "1.50 KiB").
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use the semver crate's Version type instead of manual (u32, u32, u32)
tuples for version comparison and display, eliminating the custom
format_version helper in favor of Version's built-in Display and Ord.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Validates mTLS certs (PEM parsing, expiry), JWT public/private keys
(Ed25519 PEM with base64 support), and session secret (hex, 256-bit
minimum) when the corresponding auth strategies are configured.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Checks openssl, node, gh, and dot for presence, version, and command
success. Reports errors for missing/broken required tools and warnings
for optional ones. Also fixes pre-existing build break from
ApiAuthenticationStrategy -> ApiAuthStrategy rename.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Support mutual TLS as an authentication strategy alongside JWT.
The server accepts both auth methods on the same port — mTLS if a
client cert is presented, JWT via Bearer header otherwise.
Config changes:
- Replace `authentication_strategy` (singular) with
`authentication_strategies` (list of "jwt" and/or "mtls")
- Add `[api.tls]` section for cert, key, and CA paths
New files: tls.rs (rustls ServerConfig builder)
Modified: server_config.rs, jwt_auth.rs, serve.rs, lib.rs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract apply_live_result() helper to deduplicate connectivity-result
handling across check_api, check_web, and check_brave_search
- Merge probe_api/probe_web into single probe_url function
- Share one reqwest::Client across all HTTP probes
- Run LLM probes concurrently via futures::future::join_all instead of
sequential loop (saves wall-clock time with multiple providers)
- Compute daytona_configured once before the live/offline branch
- Move live flag from DoctorReport struct field to render() parameter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Styles fields change from &'static str (raw ANSI escape codes) to
console::Style, removing unsafe Send/Sync impls and manual reset
handling. The console crate handles TTY detection and NO_COLOR natively.
Also adds live connectivity probes to arc doctor (--live flag).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
- Create openapi/arc-api.yaml as source of truth for all API endpoints
- Add arc-types crate with build.rs using typify to generate Rust structs
from the spec's component schemas
- Refactor server.rs to use generated types instead of hand-written ones
- Add route coverage conformance test validating router matches spec
- Add openapi-typescript to arc-web for TypeScript type generation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every emitted event now produces a structured tracing log line so
developers can debug after the fact via ~/.arc/logs/. Each event
variant gets an appropriate log level (info/debug/warn/error) with
structured fields. Streaming noise variants (TextDelta,
ToolCallOutputDelta) are no-ops, and wrapper variants (Agent,
ExecutionEnv on PipelineEvent) delegate to the inner event's trace.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tracing events (e.g. 13 calls in arc-mcp) were silently dropped because
no subscriber was configured. This adds a file-based tracing subscriber
that logs to ~/.arc/logs/YYYY-MM-DD.log with INFO as the default level,
controllable via the ARC_LOG env var.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces durable storage so pipeline run data survives restarts.
The new arc-db crate provides SQLite connection helpers, a
PRAGMA user_version migration system, and a WorkflowRun model.
AppConfig (arc.toml) controls data_dir; the server initializes
the DB at startup and threads the pool through AppState.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move HTTP server (Axum routes, JWT auth, serve CLI command, server
config) from arc-workflows into a dedicated arc-api crate. This
improves separation — arc-workflows is a pipeline engine library,
not a web server.
- Create crates/arc-api with server.rs, jwt_auth.rs, serve.rs,
server_config.rs and their integration tests
- Remove server feature flag and optional deps from arc-workflows
- Update arc-cli to depend on arc-api for ServeArgs and serve_command
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change GitCheckpointMode::Remote to Remote(PathBuf) so both variants
carry a repo path for MetadataStore shadow commits
- Unify init_run and shadow write logic to work with either Host or
Remote mode, eliminating Host-only gates
- Add trailers (Arc-Run, Arc-Completed, Arc-Checkpoint) to remote
checkpoint commits via write_file + git commit -F to avoid shell
escaping issues with multi-line messages
- Wire up meta_branch for Daytona in run.rs (was only set for worktree)
- Fix sandbox name collisions by adding random hex suffix
- Fix pre-existing build_router() test compilation errors from JWT auth
- Add E2E tests for Host shadow branch and Daytona shadow branch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ed25519 asymmetric JWT: arc-web signs with private key, arc-attractor verifies
with public key. Adds AuthenticatedService axum extractor to all routes, jose
dependency for TypeScript signing, and key generation script.
Startup behavior: ARC_JWT_PUBLIC_KEY set → enforce JWT auth; not set +
ARC_INSECURE_DISABLE_AUTHENTICATION=true → allow unauthenticated; neither →
refuse to start with clear error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three separate binaries are replaced by a single `arc` CLI with subcommands:
arc llm prompt/models, arc agent, arc run, arc validate, arc serve
Extract public CLI modules (arc_llm::cli, arc_agent::cli::AgentArgs/run_with_args)
so the new arc-cli crate can dispatch to each library. Integration tests migrate
to crates/arc-cli/tests/cli.rs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Git checkpoint commits (stage-level snapshots, diff.patch, GitCheckpoint
events) previously only ran for Local execution. This extends support to
Docker (bind-mount uses host git, same as Local) and Daytona (runs git
commands remotely via exec_command).
- Add GitCheckpointMode enum (Host/Remote) replacing RunConfig.work_dir
- Extract git_checkpoint_host/git_diff_host helpers from inline code
- Add git_checkpoint_remote/git_diff_remote using exec_command
- Enable git_clean check for Docker alongside Local
- Add setup_daytona_git to create run branch in remote sandbox
- Switch Daytona wrap_bash_command from quote-escaping to base64 encoding
(matches TypeScript/Python/Ruby Daytona SDKs, avoids nested quote issues)
- Add e2e tests for both Host mode and Remote mode (Daytona, live-tested)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename crate directories, package names, binary names, path
dependencies, use statements, qualified paths, clap command names,
and string literals across the workspace.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New `mcp` crate using rmcp v0.15.0 with stdio and HTTP transports.
MCP tools are registered as regular `RegisteredTool` instances in the
agent's `ToolRegistry`, sharing the same `execute_one_tool` path as
built-in tools. Tools are namespaced as `mcp__{server}__{tool}`.
Includes end-to-end test: real MCP server subprocess (Python echo
server) → mock LLM issues tool call → MCP bridge executes → result
flows back through the session loop.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
web_fetch now converts HTML responses to clean markdown using the htmd
crate (stripping script/style tags), and supports an optional prompt
parameter that makes a secondary LLM call to answer questions about the
fetched content. Each provider profile picks a cheap/fast summarizer
model (Haiku, gpt-4o-mini, gemini-2.0-flash).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the placeholder web_fetch tool with a working implementation
that executes curl within the execution environment, respecting
Docker/Daytona network sandboxing.
- Build curl command with shell-escaped URL, follow redirects, custom
user agent, and configurable timeout (default 30s, max 60s)
- Validate URL scheme (http/https only) to prevent misuse
- Truncate responses exceeding 100KB to protect context window
- Register web_fetch in Anthropic and OpenAI profiles (was Gemini-only)
- Add web_fetch guidance to all three profile system prompts
- Add captured_command to MockExecutionEnvironment for test assertions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the placeholder web_search tool with a real implementation backed
by the Brave Search API. Register it in all three profiles (Anthropic,
OpenAI, Gemini) so every provider has web search capability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merges two small utility crates into a single `util` crate to reduce
workspace clutter. No behavioral changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update daytona-sdk-rust to 5d370099 which fixes multipart file upload
(upload_file was sending an empty form). Add E2E test that verifies
artifact pointers are rewritten and files uploaded to a live Daytona
sandbox.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the `--docker` boolean flag with `--execution-env <local|docker|daytona>`
to support three execution environments. The new `DaytonaExecutionEnvironment`
uses the Daytona cloud sandbox SDK to run agent tools remotely, auto-cloning the
current git repo into the sandbox via `gh auth token` credentials.
Setup commands from TOML task configs now run inside the execution environment
(via `exec_command` after `initialize()`) rather than locally, so they work
correctly for both Docker and Daytona sandboxes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 306da8ff6de1
Support `attractor run task.toml` as an alternative to `.dot` files.
Auto-detects format by file extension. TOML bundles pipeline config
(graph path, model, setup commands, working directory) into a single
file with precedence: CLI flag > TOML > DOT graph attrs > defaults.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 8744d57d6053
Apply redact::redact_jsonl_line at all three serialization sites so
secrets (AWS keys, GitHub PATs, private keys, etc.) are scrubbed
before reaching disk or the network.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: e23c92750cd9
Skills are markdown files with YAML frontmatter that define reusable
prompt templates (e.g., /commit, /review-pr). When a user references
/skill-name in their input, the skill template expands in place with
{{user_input}} receiving the remaining text.
- Add skills.rs with parse, expand, discover, and formatting functions
- Discover skills from ~/.attractor/skills/, <git-root>/.attractor/skills/,
and <git-root>/skills/ (overridable with --skills-dir CLI flag)
- Inject available skills into system prompt between project docs and
user instructions
- Expand skill references in session input before recording user turn
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 2a8ed77f26c5
Layered library for blob/tree/commit/ref operations without touching
the working directory. Four modules: gitobj (primitives), branchstore
(key-value on a branch), snapshot (working dir captures), trailerlink
(commit message trailers). 56 tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 1e40b3267885
Logs from `attractor run` were cluttering project directories. Now defaults
to ~/.attractor/logs/attractor-run-TIMESTAMP instead of ./attractor-run-TIMESTAMP.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous approach scanned LLM response text for JSON containing
routing fields (extract_status_fields), which was fragile and violated
the spec's abstraction boundary. Replace with two clean mechanisms:
- Backend registers a report_outcome tool that the LLM calls to declare
routing decisions (status, preferred_next_label, context_updates, etc.)
- Handler auto-generates a routing preamble listing available edge labels,
appended to the prompt only when 2+ labeled unconditional edges exist
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Test 1 (llm crate): Multi-turn cache verification runs 6 conversation turns
with a large system prompt (~5460 tokens) and verifies cache_read_tokens on
the final turn. Anthropic threshold 0.5, OpenAI/Gemini 0.0 (automatic
caching not guaranteed).
Test 2 (agent crate): Cross-provider parity matrix with 15 scenarios
(file CRUD, shell, grep/glob, editing, steering, reasoning effort, loop
detection, error recovery, etc.) across Anthropic, OpenAI, and Gemini.
41 total tests. Some scenarios excluded for OpenAI due to gpt-4o-mini
limitations (no reasoning.effort, is_error rejection, weak editing).
Test 3 (attractor crate): E2E pipeline with real LLM using AgentBackend,
AutoApproveInterviewer, and default_registry. Verifies pipeline success,
artifact files, goal gate outcomes, and checkpoint state.
All tests are #[ignore] and require API keys to run.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>