Commit graph

367 commits

Author SHA1 Message Date
Bryan Helmkamp
dc7b0af2bc Add cryptographic key validation to arc doctor
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>
2026-03-03 20:35:36 -05:00
Bryan Helmkamp
510d8b6df6 Add system dependency checks to arc doctor
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>
2026-03-03 17:49:20 -05:00
Bryan Helmkamp
e0b8da08f2 Add mTLS authentication to arc-api
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>
2026-03-03 17:36:22 -05:00
Bryan Helmkamp
07a765f5a2 Simplify doctor --live: share HTTP client, concurrent LLM probes, extract helper
- 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>
2026-03-03 16:19:39 -05:00
Bryan Helmkamp
c2ea8b3084 Replace DIY terminal color with console crate
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>
2026-03-03 16:02:10 -05:00
Bryan Helmkamp
84b2003a5a Add [web] config section and arc doctor command
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>
2026-03-03 15:17:41 -05:00
Bryan Helmkamp
2737e6164e Add colored output to models list and test, fix clippy warnings
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>
2026-03-03 14:49:02 -05:00
Bryan Helmkamp
d5b98af6d1 Support base64-encoded PEM for ARC_JWT_PUBLIC_KEY and ARC_JWT_PRIVATE_KEY
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>
2026-03-03 14:39:49 -05:00
Bryan Helmkamp
9748d2e3fe Update Cargo.lock for reqwest dependency in arc-devcontainer
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:48:30 -05:00
Bryan Helmkamp
5e337b4cb3 Add arc-devcontainer crate for parsing and resolving devcontainer.json
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>
2026-03-02 22:38:12 -05:00
Bryan Helmkamp
1334a952af Add spec-first OpenAPI for arc-api with generated types
- 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>
2026-03-01 21:43:49 -05:00
Bryan Helmkamp
d7d2294b7c Add structured tracing across 7 crates per logging audit plan
Implements all 47 gaps and fixes all 7 violations identified in
docs/agent/logging-audit-plan.md:

- arc-llm: Add tracing dep + 18 log statements (requests, responses,
  retries, timeouts, tool execution)
- arc-api: Add tracing dep + 6 log statements (server lifecycle,
  pipeline start/complete/fail), replace eprintln with warn
- arc-mcp: Fix 3 violations (string interpolation, secret leakage),
  add 7 log statements (client creation, handshake, tool calls)
- arc-workflows: Fix 3 eprintln violations, add 9 log statements
  (git checkpoints, Daytona sandbox, worktrees, node visit limit)
- arc-git-storage: Add tracing dep + 9 log statements (branch ops,
  snapshot write/delete/rename)
- arc-db: Add 3 log statements (connection, migrations)
- arc-agent: Add 4 debug statements (session init, compaction)
- arc-cli: Add 1 debug statement (command dispatch)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 19:43:19 -05:00
Bryan Helmkamp
ec70be1e0d Add trace() method to event enums for structured file logging
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>
2026-03-01 19:15:43 -05:00
Bryan Helmkamp
716400ac06 Add file-based tracing infrastructure with ARC_LOG control
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>
2026-03-01 18:18:12 -05:00
Bryan Helmkamp
cab25a3e7e Add failure signatures and circuit breakers for deterministic failure cycle detection
Introduces reason normalization (strip variable data like line numbers,
hex hashes), composite failure signatures (node_id|class|normalized_reason),
and circuit breakers that track signature counts to abort when the same
deterministic failure repeats beyond a configurable limit (default 3).

Key additions:
- normalize_failure_reason() strips hex, digits, whitespace for stable grouping
- FailureSignature type with handler-provided hint priority
- FailureClass::is_signature_tracked() (deterministic + structural only)
- Graph-level loop_restart_signature_limit attribute
- LoopState struct bundling node_visits + signature maps through run_internal
- Loop failure circuit breaker (same node repeating)
- Restart failure circuit breaker (across loop_restart edges)
- Checkpoint persistence for both signature maps with backward compat
- 18 e2e integration tests covering all circuit breaker scenarios

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 18:03:09 -05:00
Bryan Helmkamp
c3d46c3885 Add SQLite persistence via sqlx with arc-db crate
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>
2026-03-01 15:56:32 -05:00
Bryan Helmkamp
99ad7ffe29 Extract server code into arc-api crate
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>
2026-03-01 11:51:21 -05:00
Bryan Helmkamp
6d22a6c622 Rename arc-attractor crate to arc-workflows
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 03:05:43 -05:00
Bryan Helmkamp
90dc528c99 Add shadow commits for Docker and Daytona (feature parity)
- 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>
2026-02-28 21:45:48 -05:00
Bryan Helmkamp
6d869aa8da Add asymmetric JWT service-to-service auth between arc-web and arc-attractor
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>
2026-02-28 18:05:49 -05:00
Bryan Helmkamp
2d79362750 Unify ullm, arc-agent, arc-attractor into single arc binary
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>
2026-02-28 17:09:30 -05:00
Bryan Helmkamp
9ef45204f6 Add git checkpointing for Docker and Daytona execution environments
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>
2026-02-28 15:08:22 -05:00
Bryan Helmkamp
434c9e61f1 Update Cargo.lock for ulid dependency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 14:22:24 -05:00
Bryan Helmkamp
32e4f5eb96 Rename all crates from unprefixed to arc-* prefix
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>
2026-02-28 04:20:47 -05:00
Bryan Helmkamp
819911fd3d Add MCP client support for connecting to external tool servers
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>
2026-02-27 23:28:33 -05:00
Bryan Helmkamp
61ed9a6bdb Add HTML-to-markdown conversion and prompt summarization to web_fetch
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>
2026-02-27 22:26:32 -05:00
Bryan Helmkamp
6338f3ec57 Implement web_fetch tool via exec_command curl
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>
2026-02-27 20:07:28 -05:00
Bryan Helmkamp
29b08fc6c5 Implement web_search tool using Brave Search API
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>
2026-02-27 19:36:38 -05:00
Bryan Helmkamp
0756a42ce8 Consolidate redact and terminal crates into util
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>
2026-02-27 18:44:19 -05:00
Bryan Helmkamp
7e4fda2a87 Add Daytona E2E test for artifact sync and update SDK
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>
2026-02-27 16:53:59 -05:00
Bryan Helmkamp
5504660379 Add more tests for daytona 2026-02-26 22:10:43 -05:00
Bryan Helmkamp
8d593ff68e Add Daytona execution environment and replace --docker with --execution-env
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
2026-02-26 18:35:33 -05:00
Bryan Helmkamp
0f9e31204f Add TOML-based task config for attractor run
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
2026-02-26 13:37:08 -05:00
Bryan Helmkamp
d47c8e9048 Redact secrets from pipeline event output (NDJSON, live.json, SSE)
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
2026-02-25 16:02:06 -05:00
Bryan Helmkamp
90e7bf229a Add redact crate for secret detection and redaction in NDJSON logs
Two-layer detection: Shannon entropy on high-entropy alphanumeric tokens
(threshold 4.5) and gitleaks v8.22.1 pattern matching (202 rules) with
Aho-Corasick keyword pre-filtering. JSONL-aware redaction skips exempt
fields (IDs, paths) and image objects. 43 tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 9e8476d16412
2026-02-25 15:25:52 -05:00
Bryan Helmkamp
3d39e06c29 Add skills system for reusable prompt templates
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
2026-02-25 11:32:07 -05:00
Bryan Helmkamp
8e4b8a6e05 Add git-storage crate for storing data in git object database
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
2026-02-24 23:17:44 -05:00
Bryan Helmkamp
b74d837b95 Add ullm models sync command to download OpenRouter model metadata
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 6908e4dfbe5e
2026-02-24 14:18:43 -05:00
Bryan Helmkamp
0431fdb242 Revert "Replace text-scanning with report_outcome tool for routing"
This reverts commit f166bb4959.
2026-02-24 12:10:39 -05:00
Bryan Helmkamp
c9b7bee2da Change default logs directory to ~/.attractor/logs
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>
2026-02-24 09:58:20 -05:00
Bryan Helmkamp
f166bb4959 Replace text-scanning with report_outcome tool for routing
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>
2026-02-24 09:29:25 -05:00
Bryan Helmkamp
b4068b6364 Add missing integration tests for multi-turn caching, cross-provider parity, and attractor E2E
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>
2026-02-23 18:24:34 -05:00
Bryan Helmkamp
2a2a610654 Replace abort_flag with CancellationToken for abort-aware process cancellation
Thread CancellationToken into tool executors and exec_command so that
running processes are killed (SIGTERM -> 2s -> SIGKILL) when abort fires,
rather than only checking the flag between LLM calls. Key changes:

- ToolExecutor type gains CancellationToken parameter
- ExecutionEnvironment::exec_command gains cancel_token param
- LocalExecutionEnvironment uses tokio::select! (completion vs timeout
  vs cancellation) with extracted sigterm_then_kill helper
- DockerExecutionEnvironment uses same select! pattern
- Session replaces Arc<AtomicBool> with CancellationToken, passes
  child_token() per tool call
- Shell tool forwards cancel token to exec_command
- CLI SIGINT handler calls cancel_token.cancel()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:10:10 -05:00
Bryan Helmkamp
028353c82e Replace manual CLI prompts with dialoguer for arrow-key navigation
Interactive TTY sessions now use dialoguer widgets (Select, MultiSelect,
Confirm, Input) instead of raw eprintln/read_line. Non-TTY input falls
back to the existing line-based reader. Suppresses redundant "Stage
started" inform message for wait.human nodes since the prompt itself
serves as notification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 17:58:18 -05:00
Bryan Helmkamp
a3605ec0d4 Speed up slow tests: drop SSE broadcast on completion, switch to rustls-tls, replace hardcoded sleeps with poll loops, and prevent real API calls in ullm tests
- Drop event_tx from ManagedPipeline when pipeline completes/cancels/fails so
  SSE streams end promptly instead of blocking until timeout (3.5s → 0.02s)
- Switch reqwest from native-tls to rustls-tls to avoid 500ms macOS cert store
  load per process (0.67s → 0.005s per OpenAI adapter test)
- Replace hardcoded sleep(500ms)/sleep(200ms)/sleep(100ms) in server and
  integration tests with 10ms poll loops (0.2-0.5s → 0.02-0.03s each)
- Add env_clear() to ullm prompt tests to prevent .env from triggering real
  Anthropic API calls (0.45s → 0.15s)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 17:19:09 -05:00
Bryan Helmkamp
60dad3c1cd Add DockerExecutionEnvironment for sandboxed agent tool execution
Implements ExecutionEnvironment trait backed by Docker containers via
bollard. Host working directory is bind-mounted; all file ops, commands,
grep, and glob execute inside the container via docker exec. Extracts
shared format_lines_numbered() helper from LocalExecutionEnvironment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 13:29:38 -05:00
Bryan Helmkamp
dddc6df8c8 Extract terminal crate and prettify attractor CLI output
Move ANSI Styles struct from agent/cli.rs into a shared terminal crate
so both binaries can use it. Add green and yellow color codes. Prettify
all attractor CLI output: bold headers, colored diagnostics by severity,
green/red status, yellow warnings, dimmed event details, and styled
interviewer prompts. Move pipeline status output from stdout to stderr.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:06:24 -05:00
Bryan Helmkamp
df8502d83b Merge agent-cli crate into agent as cli module
The agent-cli binary was a thin wrapper over the agent library with
nothing else depending on it. Moving it into the agent crate as a
`pub mod cli` with a `[[bin]]` entry reduces workspace complexity
and follows the attractor crate pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:14:25 -05:00
Bryan Helmkamp
0edc93e1c3 Add CLI tests for validate and dry-run on all test workflows
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:58:54 -05:00
Bryan Helmkamp
b7883d6bef Add agent CLI with tool approval callback
Introduce `ToolApprovalFn` callback in `SessionConfig` to gate tool
execution by permission level. Create `agent-cli` crate as a thin CLI
binary wrapping `Session` with provider/model resolution, permission
model (read-only/read-write/full), interactive approval prompts,
real-time event rendering, debug middleware, and SIGINT handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 10:50:40 -05:00