Commit graph

408 commits

Author SHA1 Message Date
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
f9e30c7ae0 allow dead code in arc-devcontainer for now 2026-03-03 14:49:22 -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
b7f69a6b13 Add estimated output speed (tok/s) to model catalog
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 14:38:50 -05:00
Bryan Helmkamp
cff594b422 Add context and cost columns to models test, tighten column widths
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 14:36:43 -05:00
Bryan Helmkamp
7c8ef76d4c Use claude-haiku-4-5 alias instead of dated snapshot ID
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 14:31:38 -05:00
Bryan Helmkamp
e3b31f50e1 Improve models list table: human-readable context, cost columns
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>
2026-03-03 14:28:26 -05:00
Bryan Helmkamp
43356dabb1 Update Inception model catalog: mercury → mercury-2
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>
2026-03-03 14:13:49 -05:00
Bryan Helmkamp
72d71525f5 agent-design-memo.md 2026-03-03 12:44:28 -05:00
Bryan Helmkamp
ead284cc39 Add icons to sidebar groups, move Deployment to Guides
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 10:35:32 -05:00
Bryan Helmkamp
d450695a20 Scaffold Mintlify docs site with full navigation structure
Replace boilerplate Mintlify starter with Arc docs outline:
- Switch to almond theme (sidebar search, card-based layout)
- Top nav: Documentation, Guides, API Reference, Changelog
- Documentation sidebar: Getting Started, Core Concepts, Defining
  Workflows, Executing Workflows, Agents, Administration, Deployment,
  Reference
- Guides tab with example workflows
- Get Started button in navbar
- 48 MDX stub pages across 9 directories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-03 10:33:09 -05:00
Bryan Helmkamp
864cb5d853 Fix 3 feature spec bugs: shorthand version, option env names, install user vars
- 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>
2026-03-03 10:07:23 -05:00
Bryan Helmkamp
8168c85ac9 Extract sandbox/daytona resolution helpers, fix preflight defaults bug
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>
2026-03-03 01:34:36 -05:00
Bryan Helmkamp
e3fa99eba1 Add run defaults to server config for workflow run inheritance
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>
2026-03-03 01:26:41 -05:00
Bryan Helmkamp
c8c1ec6916 Fix stale config after setup and extract shared config path
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>
2026-03-03 01:11:46 -05:00
Bryan Helmkamp
ae41f1c2c3 Move GitHub App ID and Client ID from env vars to TOML config
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>
2026-03-03 01:05:48 -05:00
Bryan Helmkamp
eab81aee99 Simplify feature fetch functions: extract shared helpers
- 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>
2026-03-03 00:59:41 -05:00
Bryan Helmkamp
ba2b6faef3 Add e2e tests for 5 devcontainer spec gaps using local features
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>
2026-03-03 00:51:26 -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
cf05084f7c Fix clippy collapsible_if warning in topo_sort
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:46:23 -05:00
Bryan Helmkamp
afaffa2538 Update DEVCONTAINER-COMPATIBILITY.md for 5 new spec gaps
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>
2026-03-03 00:45:44 -05:00
Bryan Helmkamp
f17c457a15 Add local path and HTTPS feature references to arc-devcontainer
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>
2026-03-03 00:45:17 -05:00
Bryan Helmkamp
c888ab8be5 Rename TaskConfig → WorkflowRunConfig, task → goal
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>
2026-03-03 00:44:24 -05:00
Bryan Helmkamp
62b4e9df74 Add subdirectory file discovery to arc-devcontainer
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>
2026-03-03 00:42:34 -05:00
Bryan Helmkamp
d437f038d0 Add feature lifecycle hooks to arc-devcontainer
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>
2026-03-03 00:41:21 -05:00
Bryan Helmkamp
22e992d8ed Add feature containerEnv support to arc-devcontainer
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>
2026-03-03 00:38:49 -05:00
Bryan Helmkamp
88508a4b5f Add feature dependsOn support to arc-devcontainer
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>
2026-03-03 00:36:39 -05:00
Bryan Helmkamp
4ad60489b9 Fix 4 spec compliance gaps in arc-devcontainer
- 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>
2026-03-03 00:06:23 -05:00
Bryan Helmkamp
3b2c0de11a Simplify preflight: remove dead RunMode enum, fix stringly-typed provider matching
- 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>
2026-03-02 23:54:02 -05:00
Bryan Helmkamp
ee11a46b86 Fix 4 critical gaps in arc-devcontainer: onCreateCommand, build.args, containerEnv, compose array
- 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>
2026-03-02 23:43:45 -05:00
Bryan Helmkamp
74cd6bc791 Add --preflight flag to arc run start for config validation without execution
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>
2026-03-02 23:42:54 -05:00
Bryan Helmkamp
82643d80c3 Add icons to API reference sidebar categories
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:28:06 -05:00
Bryan Helmkamp
d93785ae83 Simplify config loading: fix TOCTOU, eager init, remove dead code
- Rust: replace exists() check with direct read + NotFound handling
- TS: load config eagerly at module init instead of lazy per-request
- TS: remove unused resetAppConfigCache() export

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:24:39 -05:00
Bryan Helmkamp
1cebe29fad Move auth and API config from env vars to TOML (~/.arc/arc.toml)
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>
2026-03-02 22:49:04 -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
ffac356c7b Add pass numbering to parallel branch names to prevent overwrites on retry
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>
2026-03-02 22:02:43 -05:00
Bryan Helmkamp
e911a60220 Fix useless format! clippy warning in asset_snapshot
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-02 22:00:55 -05:00
Bryan Helmkamp
cffb00bcdb Carry typed errors through the entire stack instead of stringifying early
- AgentError gains Clone + Serialize/Deserialize with tagged serde format
- ArcError::Handler/Engine become struct variants with eager FailureClass
  classification via smart constructors handler()/engine()
- Outcome replaces failure_reason: Option<String> with failure: Option<FailureDetail>
  carrying message, failure_class, and failure_signature together
- AgentEvent::Error/LlmRetry/SubAgentFailed carry typed AgentError/SdkError
  instead of pre-stringified error messages
- WorkflowRunEvent::WorkflowRunFailed carries ArcError; StageFailed/StageCompleted
  carry FailureDetail instead of separate string fields
- classify_outcome() trivially reads from FailureDetail; circuit breaker reads
  failure_signature from FailureDetail instead of context_updates hack
- flatten_event() decomposes structured errors into flat fields for progress.jsonl
  backward compatibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-02 21:53:23 -05:00
Bryan Helmkamp
1fd9a8ebfe SQLite-backed sessions with GitHub email and app manifest fix
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>
2026-03-02 21:05:59 -05:00
Bryan Helmkamp
00145424a5 Consolidate artifacts and assets under unified directory layout
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>
2026-03-02 20:31:21 -05:00
Bryan Helmkamp
b0e65423ae Base64-encode GITHUB_APP_PRIVATE_KEY in .env
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>
2026-03-02 20:21:53 -05:00
Bryan Helmkamp
f1bf37349d Extract AuthLayout component and polish auth UI
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>
2026-03-02 20:19:15 -05:00
Bryan Helmkamp
4d362f320c Auto-restart after GitHub App setup instead of requiring manual restart
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>
2026-03-02 20:03:18 -05:00
Bryan Helmkamp
67652ad480 Add generic asset snapshot collection for workflow node outputs
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>
2026-03-02 19:58:08 -05:00
Bryan Helmkamp
743a4fc677 Add GitHub App manifest registration and OAuth login
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>
2026-03-02 19:45:50 -05:00
Bryan Helmkamp
c560c608e2 Rename progress.jsonl fields for clarity
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>
2026-03-02 19:11:33 -05:00
Bryan Helmkamp
b4f1495982 Avoid data loss in SubAgentEvent flattening
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>
2026-03-02 18:29:28 -05:00
Bryan Helmkamp
eb195a0be9 Skip envelope-colliding keys when merging flattened event fields
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>
2026-03-02 18:26:54 -05:00
Bryan Helmkamp
62b54c8f4d Flatten progress.jsonl event format to top-level fields
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>
2026-03-02 18:25:27 -05:00