Commit graph

218 commits

Author SHA1 Message Date
Bryan Helmkamp
be23369d28 Indent subagent status lines deeper than tool calls in CLI progress
Subagent lines (▸ subagent[...], ✓ subagent[...]) now render at 8-space
indent instead of 6, visually distinguishing them as children of the stage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 02:00:21 -04:00
Bryan Helmkamp
fc7ecde756 Add exe.dev cancellation, git cloning, and checkpointing support
- Wire up cancel_token in ExeSandbox::exec_command via tokio::select!
- Clone the local git repo into exe.dev VMs after initialization
- Add GitCredentialSandbox decorator for push credential refresh
- Enable GitCheckpointMode::Remote for exe.dev (rename daytona_* → remote_*)
- Rename setup_daytona_git → setup_remote_git (already sandbox-agnostic)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 01:54:49 -04:00
Bryan Helmkamp
e60c431a6b Fix clippy filter_next lint for Rust 1.94+
Replace `.filter(..).next_back()` with `.rfind(..)` to satisfy the
expanded `clippy::filter_next` lint on newer toolchains.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 01:49:53 -04:00
Bryan Helmkamp
dbf9a7413f Shorten token/tool labels in CLI output for brevity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:27:57 -04:00
Bryan Helmkamp
7b6a2a93b7 Fix asset collection tests to provide required asset globs
The opt-in change (7400a9d5) left these two tests with empty asset_globs,
which now means "no asset collection". Add the matching glob so the tests
actually exercise asset collection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:54:53 -04:00
Bryan Helmkamp
7400a9d5a1 Make asset collection opt-in via [assets] config globs
Asset collection previously ran a full `find` scan before and after every
stage (~30s per scan), even when no assets were needed. This makes it
opt-in: only workflows with an `[assets] include = [...]` section in their
TOML config will run asset collection, and only with user-specified globs.

- Add AssetsConfig struct and wire through WorkflowRunConfig, RunDefaults,
  and RunConfig
- Remove baseline snapshot approach; single post-execution scan using
  user globs filtered by mtime and size budgets
- Delete hardcoded pattern constants, is_asset_candidate, matches_simple_glob,
  FileFingerprint, and snapshot() — replaced by configurable globs
- Skip all asset work when asset_globs is empty (zero overhead default)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:39:04 -04:00
Bryan Helmkamp
686d6bbdb0 Name Daytona sandboxes arc-{run_id} for easy correlation with workflow runs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 23:01:44 -04:00
Bryan Helmkamp
11b9159534 Rename "tool calls" to "tools" in CLI progress output
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:58:45 -04:00
Bryan Helmkamp
96bcff05b3 Add duration_ms, node_visits, and base_branch to shadow branch structs
Enrich Outcome with stage duration, Checkpoint with persisted node visit
counts (avoiding fragile reconstruction on resume), and Manifest with the
base branch name for offline analysis without git lookups.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:57:32 -04:00
Bryan Helmkamp
e24d20b439 Update daytona-sdk-rust to 06033ca and apply clippy/fmt fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:13:43 -04:00
Bryan Helmkamp
f30effaadd Truncate stage failure error display to last line in arc run output
The full failure message (including entire stdout/stderr) was shown inline,
making terminal output very noisy. Now only the last non-empty line is
displayed, truncated to 120 chars. The full error remains in the log file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 22:12:54 -04:00
Bryan Helmkamp
6694dbbeb9 Change logs dir naming from arc-run-YYYYMMDD-HHMMSS to YYYYMMDD-{run_id}
Embeds the ULID run_id directly in the directory name so logs dirs
can be easily correlated with runs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:24:15 -04:00
Bryan Helmkamp
6c6cc652a6 Route script nodes through sandbox instead of spawning on host
CommandHandler was using tokio::process::Command to execute scripts
directly on the host machine, completely bypassing the configured
sandbox (local, Daytona, Docker). This meant script nodes in workflows
ran on the host while agent nodes ran in the sandbox, causing fix cycles
where agents edited files in the sandbox but lint checks ran on the host.

Now delegates to services.sandbox.exec_command() so all node types
execute in the same environment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 21:17:46 -04:00
Bryan Helmkamp
9e35c4a472 Add GitCheckpointFailed event and align with logging/events strategy
- Add GitCheckpointFailed variant to WorkflowRunEvent with node_id and error fields
- Add trace() arm at ERROR level per logging strategy (operation cannot continue)
- Add rename_fields rule to default node_label from node_id
- Emit the event before returning ArcError::Engine on checkpoint failure
- Update events-strategy.md with the new event in the checkpoints table
- Add rename_fields serialization test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 20:54:27 -04:00
Bryan Helmkamp
2620a43950 Fail workflow run on git checkpoint commit failure instead of silently continuing
The git checkpoint functions (host and remote) now return Result<String, String>
with descriptive error messages instead of Option<String>. When a checkpoint commit
fails, the engine returns ArcError::Engine with FailureClass::Deterministic,
stopping the run immediately. This prevents stuck cycles where an agent's edits
are silently lost between node transitions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 20:52:11 -04:00
Bryan Helmkamp
eb584b88c9 Show Run ID before Logs in arc run output
Generate the run ULID early and display it before the logs directory,
then pass it into setup_worktree and setup_daytona_git instead of each
generating their own.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 20:35:15 -04:00
Bryan Helmkamp
d568306701 Add --goal-file CLI flag to arc run for reading goal from a file
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 20:18:49 -04:00
Bryan Helmkamp
0f3931ae9f Fix daytona_asset_collection test to use current artifacts directory layout
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:51:42 -04:00
Bryan Helmkamp
d95a9acf77 Fix daytona_cli_claude OOM by using daytona-medium snapshot (2 vCPU / 4 GiB)
The Claude CLI native binary install was getting OOM-killed (exit 137)
in the default 1 GiB sandbox. Using the daytona-medium default snapshot
also pre-includes Node, making the prereq install step a no-op.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:34:15 -04:00
Bryan Helmkamp
f6284026b1 Fix codex CLI auth: use codex login --with-api-key for credentials
Codex CLI no longer reads OPENAI_API_KEY from the environment at
runtime. It requires `codex login --with-api-key` to store credentials
via its auth system. Add a login step after ensure_cli for codex.

Also pass API keys via exec_command's env_vars parameter as belt-and-
suspenders alongside the env file, to avoid fs-to-process race conditions
in the Daytona sandbox.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:22:41 -04:00
Bryan Helmkamp
dd9bed01e5 Improve CLI backend reliability and error diagnostics
- Use `cat file | command` instead of `command < file` for stdin piping
  to avoid redirect issues in nested shell wrappers
- Include both stdout and stderr in error messages when CLI exits non-zero
  (previously only showed stderr, hiding API errors in stdout)
- Install Node 20 via nodesource + bash/curl as prerequisites for CLI tests
- Use bash instead of sh for Claude CLI install script

The codex test still fails with 401 Unauthorized — the OPENAI_API_KEY
env var isn't reaching the codex process in the sandbox despite the env
file being sourced correctly (gemini works with the same mechanism).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:06:03 -04:00
Bryan Helmkamp
59c01d62fd Fix Daytona e2e test failures and improve test reliability
- Pass GitHub App credentials in create_env() and daytona_snapshot_sandbox
  so private repo clones succeed
- Install git in sandbox before running git CLI in clone_private_repo test
- Use brynary/arc (where app is installed) instead of rust-lang/rust for
  clone_public_repo_gets_credentials test
- Fix git_checkpoint_remote_emits_events assertion: expect 1 event (work
  node only) since start is skipped and exit breaks before checkpoint
- Install Node 20 via nodesource and bash/curl in CLI test prerequisites
- Use bash instead of sh for Claude CLI install script
- Shorten toolbox_idle_diagnostic sleep durations to finish in ~12s
- Add nextest config with 120s per-test timeout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 18:51:43 -04:00
Bryan Helmkamp
44dce81409 Fix e2e test failures and quarantine unreliable Inception provider
- Anthropic max_tokens: use model catalog limit instead of hardcoded 65536,
  fixing claude-sonnet-4-5 (max_output=64000) API errors
- OCI feature tgz: detect actual filename from registry instead of
  assuming devcontainer-feature.tgz (registries use suffixed names)
- Test LlmCodergenBackend: implement one_shot() for prompt handler support
- Quarantine all Inception/mercury-2 parity matrix tests (unreliable provider)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 17:24:55 -04:00
Bryan Helmkamp
7cfbe5f9d1 Add PreToolUse, PostToolUse, PostToolUseFailure Hooks (#6)
* arc(01KK739V1KPSQJT6E492ZWR95G): implement (success)

Arc-Run: 01KK739V1KPSQJT6E492ZWR95G
Arc-Completed: 2
Arc-Checkpoint: 52e0f637b3c802833c0c6eb7e2e8b56aef071441

* arc(01KK739V1KPSQJT6E492ZWR95G): simplify (success)

Arc-Run: 01KK739V1KPSQJT6E492ZWR95G
Arc-Completed: 3
Arc-Checkpoint: c1d6abe508b30b269d019961988212076064fb9e

* Merge main into PR branch: integrate MCP servers with server mode dispatch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: arc <arc@local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 16:40:02 -04:00
Bryan Helmkamp
80fbeb5fcd Auto-PR on Successful Workflow Run (#3)
* arc(01KK6WHJ904CXGN0MCQFGWKMKS): implement (success)

Arc-Run: 01KK6WHJ904CXGN0MCQFGWKMKS
Arc-Completed: 2
Arc-Checkpoint: 4c3a9115be9074c7e23792c0ef9b1f0525ffc10d

* arc(01KK6WHJ904CXGN0MCQFGWKMKS): simplify (success)

Arc-Run: 01KK6WHJ904CXGN0MCQFGWKMKS
Arc-Completed: 3
Arc-Checkpoint: 7d8e8413e70eb198e14fcb676903dcc91bd21b76

* Fix merge conflicts: add pull_request to OpenAPI spec, fix clippy warning, cargo fmt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: arc <arc@local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 16:32:55 -04:00
Bryan Helmkamp
ff053e1e4a Fix ReadBeforeWriteSandbox (#1)
* arc(01KK60MHXX1G7Y2H0YN6HAWWZH): start (success)

Arc-Run: 01KK60MHXX1G7Y2H0YN6HAWWZH
Arc-Completed: 1
Arc-Checkpoint: be0355a55571c147ac954b15ac0d5605d63539e8

* arc(01KK60MHXX1G7Y2H0YN6HAWWZH): implement (success)

Arc-Run: 01KK60MHXX1G7Y2H0YN6HAWWZH
Arc-Completed: 2
Arc-Checkpoint: b67ee066bf67ce3c36bbdf31833e4303065de311

* arc(01KK60MHXX1G7Y2H0YN6HAWWZH): simplify (success)

Arc-Run: 01KK60MHXX1G7Y2H0YN6HAWWZH
Arc-Completed: 3
Arc-Checkpoint: 69fbd818f537771b9d7f8b43339e31fe4fa63321

* Fix formatting in cli.rs and tools.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix clippy warnings after merging main

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: arc <arc@local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 16:19:22 -04:00
Bryan Helmkamp
c3d23d72e9 Extract raise_on_errors helper to deduplicate diagnostic error filtering
The pattern of filtering Error-severity diagnostics, collecting messages,
and returning ArcError::Validation appeared in three places. Extract it
into validation::raise_on_errors and use prepare_from_file in run_from_branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:52:37 -04:00
Bryan Helmkamp
3c476772e7 Add prepare_with_file_inlining API to prevent validate/run divergence
FileInliningTransform was treated as a "custom" transform that every
caller had to remember to register manually. This caused arc validate
to break and left latent bugs in run_from_branch and SubWorkflowHandler.

Add prepare_with_file_inlining() and prepare_from_file() to WorkflowBuilder
so file inlining is a built-in concern. Rename prepare_workflow to
prepare_from_source for clarity. Fix the SubWorkflowHandler to use
prepare_from_file when reading from stack.child_dotfile paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:37:54 -04:00
Bryan Helmkamp
3e47d85b16 Fix arc validate not resolving @file references
Register FileInliningTransform in validate_command so @file references
(e.g. @prompts/simplify.md) are resolved during validation, matching
the behavior in run.rs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:58:56 -04:00
Bryan Helmkamp
839cbc28b8 Fix stale test: goal is now optional in run config
The missing_required_fields test was not updated when goal was changed
from String to Option<String> in c143d2c8.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:11:39 -04:00
Bryan Helmkamp
c143d2c873 Make goal optional in run config TOML so it can be provided via CLI --goal
Precedence: CLI --goal > TOML goal > DOT graph attribute. Previously
the TOML goal field was required but never applied to the graph.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 11:56:41 -04:00
Bryan Helmkamp
aa8aab6588 Remove unused base64::Engine import in daytona integration tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 11:14:37 -04:00
Bryan Helmkamp
fb19251e9c Fix CI: clippy warnings, formatting, and mTLS test cert version
Rust 1.94.0 introduced new clippy lints and rustls now rejects X.509 v1
certificates. This fixes all three CI jobs:

- Format: cargo fmt across the workspace
- Clippy: unnecessary_unwrap, useless_format, derivable_impls,
  type_complexity, too_many_arguments, redundant_closure, map_or
  simplification, and other new lints
- Tests: generate v3 certs (with extensions) for mTLS tests so newer
  rustls accepts them

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 11:07:33 -04:00
Bryan Helmkamp
f4481e5287 Render Run ID line through progress system for consistent styling
The Run: line was using raw eprintln! without indentation or dimming,
making it visually inconsistent with the Logs: and Sandbox: lines.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 10:13:39 -04:00
Bryan Helmkamp
736f6dec57 Reduce Daytona sandbox timeout grace period from 5s to 2s 2026-03-08 10:06:52 -04:00
Bryan Helmkamp
ad226440fb Fix daytona exec tests to load github app credentials
Tests were failing locally because the Daytona sandbox needs GitHub App credentials to clone a private repository, but the exec tests were passing . This updates the tests to load the credentials and supply them during initialization.
2026-03-08 10:05:02 -04:00
Bryan Helmkamp
ae1f13402b Add 5-minute stream read timeout and increase retry backoff to 5s
A stalled Anthropic SSE stream caused a run to hang for 10 minutes
until the stall watchdog killed it. The HTTP connection succeeded but
no SSE events arrived, and with stream_read_timeout defaulting to None
the read blocked indefinitely.

- Set AdapterTimeout default stream_read to 300s (5 min), matching
  the idle timeout used by OpenAI Codex
- Increase BackoffConfig default initial_delay_ms from 200ms to 5s
  so transient LLM failures get meaningful recovery time before retry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 10:03:50 -04:00
Bryan Helmkamp
a019082dfc Print only first line of goal in arc run start output
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 10:01:36 -04:00
Bryan Helmkamp
2b8da8f15b Add tests for Daytona execute_command timeout and cancellation
Adds two new tests to the Daytona integration suite:
1. `daytona_exec_command_cancelled`: verifies that an active token cancellation properly aborts a running command and yields a correct exit code/message.
2. `daytona_exec_command_local_timeout`: tests the recent fix that prevents commands from hanging indefinitely by enforcing a local timeout fallback.
2026-03-08 10:01:06 -04:00
Bryan Helmkamp
59634a3936 Increase default stall watchdog timeout from 10 minutes to 30 minutes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 09:52:07 -04:00
Bryan Helmkamp
3dce68d664 cargo fmt 2026-03-08 09:51:10 -04:00
Bryan Helmkamp
445050dc57 Enforce local timeout and cancellation for Daytona execute_command
The Daytona SDK HTTP client can sometimes hang indefinitely if the connection
drops or the server stops responding. Since `daytona_sandbox.rs` was awaiting
the `execute_command` call directly, this could cause the entire workflow engine
to stall, eventually triggering the stall watchdog.

This wraps the `execute_command` call in a `tokio::select!` block, enforcing
the requested `timeout_ms` locally (with a 5s grace period) and respecting
the `cancel_token`.

Co-Authored-By: Gemini CLI <noreply@google.com>
2026-03-08 09:49:51 -04:00
Bryan Helmkamp
58e08bec60 Cache start node ID before main loop to avoid repeated O(n) lookup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 03:09:39 -04:00
Bryan Helmkamp
a3abbaf191 Skip git checkpoint commit for start node
The start node is a no-op (StartHandler returns success immediately), so its
git checkpoint commit is always empty. Skipping it reduces noise in git history
without losing any data — the checkpoint JSON is still saved to disk, and the
next node's diff falls back to base_sha correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-08 03:02:57 -04:00
Bryan Helmkamp
3e1185d19b Extract ActiveStage::last_bar() and clean up compaction bar handling
- Extract repeated `tool_calls.back().map_or(&spinner, |e| &e.bar)` into
  `ActiveStage::last_bar()` (used in 4 places)
- Clean up existing compaction_bar before overwriting in CompactionStarted
- Tidy CompactionCompleted handler to inline the if-let chain

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-08 01:08:51 -05:00
Bryan Helmkamp
e33d3f02fb Add validation error for unresolved @file references in DOT workflows
Unresolved @file references in node prompts or graph goals silently
passed through as literal text. Now caught at validation time with
a clear error and fix hint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-08 01:05:37 -05:00
Bryan Helmkamp
3d4de58be2 Print run ID at start of arc run start and on resume
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-08 01:04:46 -05:00
Bryan Helmkamp
030dc7fb89 Move compaction_bar to per-stage state to fix parallel stage bug
The compaction progress bar was stored as a single global field on
ProgressUI, which meant parallel stages could clobber each other's
compaction bar. Move it into ActiveStage so each stage tracks its own.
Also clean up compaction_bar in finish_stage to prevent stale spinners.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-08 01:03:12 -05:00
Bryan Helmkamp
2864a6b1d1 Fix @file references with ~ and .. paths
resolve_file_ref now expands tilde via dirs::home_dir() and uses
canonicalize() to resolve .., symlinks, and verify existence in one step.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-08 00:55:30 -05:00
Bryan Helmkamp
6803b9ad9a Show compaction progress in non-verbose arc run output
Compaction events were previously only shown with `-v`. Since compaction
takes time and changes agent behavior, display a spinning progress bar
on CompactionStarted and a summary line on CompactionCompleted regardless
of verbose mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-08 00:39:30 -05:00