Commit graph

213 commits

Author SHA1 Message Date
Bryan Helmkamp
2c70e87630 Add icons to top nav items
Rocket for Start, play circle for Workflow Runs, chart bar for
Insights, gear for Settings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 13:17:43 -05:00
Bryan Helmkamp
0d95c27325 Rename Pipeline Runs to Workflow Runs, change URL to /runs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 13:16:45 -05:00
Bryan Helmkamp
248f8ae7fc Add Insights tab and server-side route
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 13:10:50 -05:00
Bryan Helmkamp
01bd717a3b Move CI badges to action row, add elapsed times to Verify/Merge cards
CI status badges now sit alongside action buttons instead of in the
metadata row. All cards now show elapsed time. Stale items (3d) get
amber warning color.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 13:10:20 -05:00
Bryan Helmkamp
ab88043288 Color-code Resolve button with teal-500 accent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 13:05:32 -05:00
Bryan Helmkamp
4dfe86545f Switch sans-serif font from Inter to Geist
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 13:04:16 -05:00
Bryan Helmkamp
ac691dd1ba Color-code action buttons: mint for Merge, amber for Answer Question
Uses brand palette accent colors to visually distinguish action types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 13:00:28 -05:00
Bryan Helmkamp
e73f6be166 Add background atmosphere and compact ghost pill action buttons
Background: radial teal/mint gradient with subtle film grain noise overlay.
Buttons: replace heavy full-width teal buttons with compact ghost pills
featuring icons + text labels.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 12:59:02 -05:00
Bryan Helmkamp
b0d6becb33 Add branded favicon, avatar update, and Watch/Steer buttons
Generate favicon from brand symbol SVG (ico, svg, apple-touch-icon,
192/512 PNGs). Switch avatar to GitHub. Split Working column action
into Watch and Steer buttons side by side.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 12:38:42 -05:00
Bryan Helmkamp
1f366845ca Add arc-web React Router 7 app with pipeline kanban board
New Bun-based React Router 7 app with Tailwind CSS, branded with Arc
logo/colors. Features an app shell with nav routing (/start, /pipelines,
/settings) and a Trello-style pipeline board with Working, Pending,
Verify, and Merge columns showing PR cards with CI status, comments,
resources, and action buttons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 12:34:18 -05:00
Bryan Helmkamp
91f5fc938a Surface git metadata in pipeline events, manifest, and final.json
Add git observability to match Kilroy parity: base_sha, run_branch,
worktree_dir on PipelineStarted; GitCheckpoint events with commit SHAs;
final_git_commit_sha on PipelineCompleted/PipelineFailed; run_branch in
manifest.json; final_git_commit_sha in final.json; diff.patch per node;
and git::diff_against helper.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 11:09:36 -05:00
Bryan Helmkamp
036c8897d9 move brand 2026-02-28 10:54:20 -05:00
Bryan Helmkamp
02f72eea51 move pipelines 2026-02-28 10:53:56 -05:00
Bryan Helmkamp
43cf536c5c rm daytona_build.py 2026-02-28 10:52:59 -05:00
Bryan Helmkamp
e97c16f03f Git worktree isolation and per-node checkpoint commits
Create a dedicated git branch + worktree per pipeline run (Local env only)
and commit after every node checkpoint. This gives each run an isolated
working directory and a full git trail of changes per stage.

New module: git.rs with ensure_clean, head_sha, create_branch,
add/remove_worktree, and checkpoint_commit (using arc identity).

Engine changes: RunConfig gains run_id and work_dir fields; after each
checkpoint save, a git commit is created in the worktree and the SHA
is stored in checkpoint.git_commit_sha.

CLI changes: for Local execution, the repo cleanliness is verified
before any log files are written, then a worktree is created on branch
arc/run/{uuid}, cwd is switched into it, and cleanup runs after the
engine completes.

Handler changes: run_hook() accepts work_dir so hooks execute in the
worktree.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 10:51:37 -05:00
Bryan Helmkamp
172a4a89a5 palette 2026-02-28 04:38:13 -05:00
Bryan Helmkamp
1d314523d0 brand 2026-02-28 04:29:34 -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
622b39f40e rm attractor-web 2026-02-28 04:20:19 -05:00
Bryan Helmkamp
67633e2a7a Add Kimi, Z.AI, and Minimax API keys to .env.example
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-28 04:07:03 -05:00
Bryan Helmkamp
5ba3535a1c Add Kimi, Z.AI, and Minimax provider support
All three providers use the OpenAI Chat Completions protocol via
OpenAiCompatibleAdapter:
- Kimi (KIMI_API_KEY) → api.moonshot.ai/v1
- Z.AI (ZAI_API_KEY) → api.z.ai/api/coding/paas/v4
- Minimax (MINIMAX_API_KEY) → api.minimax.io/v1

Key changes:
- Extend Provider enum with Kimi, Zai, Minimax variants
- Add with_name() to AnthropicAdapter for non-Anthropic providers
  using the Messages protocol (conditional Bearer vs x-api-key auth)
- Add complete_via_stream() for providers requiring stream=true
- Add with_provider() to AnthropicProfile and OpenAiProfile so the
  session routes requests to the correct adapter
- Add kimi-k2.5, glm-4.7, minimax-m2.5 to model catalog
- Handle reasoning_content in OpenAI compatible adapter (capture in
  stream, store as ContentPart::Thinking, echo back in assistant
  messages) — required by Kimi for multi-turn tool use
- Handle missing [DONE] sentinel in SSE streams (Minimax omits it)
- Wire up all exhaustive Provider matches across agent and attractor

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-28 04:06:20 -05:00
Bryan Helmkamp
950a2d06a7 Introduce Provider enum and ModelId type for compile-time provider safety
Replace scattered provider string literals ("anthropic", "openai", "gemini")
with a Provider enum and ModelId struct in the llm crate. This prevents
bugs like routing an OpenAI model to Anthropic's API (the bug fixed in
3263d0c) by making the provider identity a compile-time checked value.

Key changes:
- Add Provider enum (Anthropic, OpenAi, Gemini) with as_str/Display/FromStr
- Add ModelId struct bundling Provider + model name
- Replace WebFetchSummarizer's separate model+provider fields with ModelId
- Replace BaseProfile.id: &'static str with BaseProfile.provider: Provider
- Replace ProviderProfile::id() -> &str with provider() -> Provider
- Parse --provider CLI strings to Provider early via FromStr
- Update AgentBackend and CliBackend to use Provider instead of String/Option

Serialization boundaries (Request.provider, Response.provider, Client HashMap
keys) remain as strings, converted via provider.as_str().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-28 03:29:30 -05:00
Bryan Helmkamp
3263d0c21e Fix web_fetch summarizer routing to wrong LLM provider
The WebFetchSummarizer was sending requests without specifying a
provider, so they always routed to the default (Anthropic). When using
the OpenAI or Gemini profile, the summarizer model (e.g. gpt-4o-mini)
was rejected by Anthropic with a 404.

Add a `provider` field to WebFetchSummarizer so the summarization
request routes to the correct provider. Also improve the error message
to include the model name, and relax the parity test assertion to
accept summarized content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-28 01:14:04 -05:00
Bryan Helmkamp
13877b4e81 logo 2026-02-28 00:55:27 -05:00
Bryan Helmkamp
876e487821 Suppress too_many_arguments lint on compact_context
The 8 params are all distinct concerns freshly extracted from Session;
bundling them into a struct would add indirection without clarity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-27 23:49:36 -05:00
Bryan Helmkamp
275c0aa5a9 Extract tool execution from session.rs into tool_execution.rs
Move the tool execution subsystem (~250 lines) into a dedicated module:
- execute_tool_calls: dispatches to parallel or sequential execution
- execute_and_emit_one_tool: wraps execution with event emission
- execute_one_tool: registry lookup, validation, and execution
- validate_tool_args: JSON schema validation for tool arguments
- truncate_tool_result: output truncation for history storage

These functions have zero dependency on Session's history, state machine,
or LLM interaction. Session now calls the extracted free functions,
passing needed context as parameters.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-27 23:47:46 -05:00
Bryan Helmkamp
ee831c2db0 Introduce ToolContext struct to simplify ToolExecutor type
Bundle the execution parameters (env, cancel) into a single
ToolContext struct, reducing the ToolExecutor closure signature
from 3 parameters to 2. This makes the type alias simpler and
means future parameters won't change the signature.

Key changes:
- Add ToolContext { env, cancel } in tool_registry.rs
- Update ToolExecutor type alias: Fn(Value, ToolContext) -> ...
- Update all tool factories in tools.rs, v4a_patch.rs,
  subagent.rs, mcp_integration.rs, and skills.rs
- Update all call sites in session.rs and tool_execution.rs
- Update test helpers and test call sites

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-27 23:46:23 -05:00
Bryan Helmkamp
b391af7968 Register compaction module in lib.rs
The compaction.rs module was created and session.rs was updated to use
it in the previous commit, but the module was not registered in lib.rs.
Add `pub mod compaction;` to complete the extraction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-27 23:45:20 -05:00
Bryan Helmkamp
e8694f08bb Add delegate_execution_env macro to reduce decorator boilerplate
ReadBeforeWriteEnvironment had 9 pass-through methods that just forwarded
to self.inner. The new delegate_execution_env! macro generates these
automatically, so only the 4 customized methods (read_file, write_file,
delete_file, grep) need to be written explicitly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-27 23:44:28 -05:00
Bryan Helmkamp
b80f9cb100 Unify reasoning storage in Turn::Assistant
Remove the standalone `reasoning: Option<String>` field from
Turn::Assistant. Reasoning/thinking text is now stored exclusively
in `provider_parts` as `ContentPart::Thinking` blocks, eliminating
the dual-storage reconciliation logic in `convert_to_messages`.

Add `Turn::reasoning_text() -> Option<&str>` accessor that extracts
the first non-redacted thinking text from provider_parts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-27 23:43:38 -05:00
Bryan Helmkamp
5c74f88814 Consolidate tool registration into register_core_tools
All three provider profiles (Anthropic, OpenAI, Gemini) independently
registered the same 7 core tools (read_file, write_file, shell, grep,
glob, web_search, web_fetch). Extract a shared register_core_tools()
function in tools.rs that accepts a ToolRegistry, SessionConfig (for
shell timeout customization), and optional WebFetchSummarizer.

Each profile now calls register_core_tools() then adds its
profile-specific tools:
- Anthropic: edit_file (with 120s shell timeout via config)
- OpenAI: apply_patch (default 10s shell timeout)
- Gemini: edit_file, read_many_files, list_dir (default 10s shell timeout)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-27 23:40:49 -05:00
Bryan Helmkamp
96443e2508 Extract v4a patch parser from OpenAI profile into standalone module
The parser, applicator, types, and tool factory had no dependency on the
OpenAI profile. Moving them to `v4a_patch.rs` makes them independently
testable, reusable by other profiles, and cuts `openai.rs` nearly in half.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-27 23:36:45 -05:00
Bryan Helmkamp
06dcd3c4f5 Unify tool execution event emission into execute_and_emit_one_tool
Extract duplicated ToolCallStarted/ToolCallOutputDelta/ToolCallCompleted
emission and output truncation from both sequential and parallel paths
into a single shared function.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-27 23:32:32 -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
6bafc012c3 Add structured compaction with file tracking
After compaction, the agent previously lost awareness of which files it
touched. This adds a FileTracker that records file operations from tool
calls (read_file, write_file, edit_file, apply_patch) and injects a
## File Operations section into the structured compaction prompt so file
context survives across compaction cycles.

- FileTracker: BTreeMap-based tracker with record_from_tool_calls()
- Structured compaction prompt with Goal/Progress/Key Decisions/
  Failed Approaches/Open Issues/Next Steps sections
- tracked_file_count field on CompactionCompleted event
- File tracker accumulates monotonically (never reset)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 22:46:29 -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
35005ca0fe Add use_skill tool for agent-initiated skill loading
Let the agent autonomously load skill templates when it recognizes a
matching task, instead of requiring users to type /skill-name. The
system prompt now instructs the agent to call `use_skill` and skill
names use backtick formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 22:03:16 -05:00
Bryan Helmkamp
f6c711fdd9 Add --output-format json flag for NDJSON event streaming
Enables machine-readable output from the agent CLI by streaming
SessionEvent objects as newline-delimited JSON to stdout. This
unlocks scripting, integration testing, and UI integration.

- Make SessionEvent serializable with ISO-8601 timestamps via chrono
- Add OutputFormat enum (text/json) and --output-format CLI flag
- JSON mode: each event is one JSON line to stdout, flushed per line
- JSON mode: skip print_output/print_summary (all info in event stream)
- Text mode (default): behavior unchanged

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 21:51:41 -05:00
Bryan Helmkamp
2750da01c6 Add gpt-5.3-codex model and make it the default codex alias
- Add gpt-5.3-codex to catalog (API model, 1047576 context, 128K output)
- Move "codex" alias from gpt-5.2-codex to gpt-5.3-codex
- Add e2e integration test for gpt-5.3-codex via OpenAI API
- Remove gpt-5.3-codex-spark (not yet available)
- Empty CLI_ONLY_MODELS list and update related tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 21:40:01 -05:00
Bryan Helmkamp
bb5f9899ed Add CLI backend support for Claude, Codex, and Gemini CLIs
Route codergen nodes to external CLI tools (claude, codex, gemini) via
exec_command() based on node backend="cli" attribute, stylesheet rules,
or CLI-only model detection.

Key components:
- CliBackend: writes prompt to temp file, shells out to CLI tool,
  parses NDJSON/JSON response, detects file changes via git diff
- BackendRouter: wraps AgentBackend + CliBackend, routes per-node
- Parsers matched to real CLI output formats (Claude stream-json,
  Codex NDJSON, Gemini JSON)
- Permission flags for non-interactive use (--dangerously-skip-permissions,
  --full-auto, --yolo)
- Node::backend() accessor and "backend" stylesheet property

Tested against real Claude, Codex, and Gemini CLIs locally.
Daytona integration tests added (ignored, require DAYTONA_API_KEY).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-27 21:08:19 -05:00
Bryan Helmkamp
8c90945ab6 Add e2e parity tests for web_fetch and web_search tools
Test both tools across all three providers (Anthropic, OpenAI, Gemini).
web_fetch fetches example.com and asserts content is written to a file.
web_search searches for "Rust programming language" and asserts results
are saved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 20:09:07 -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
a93ca70fb4 Emit subagent lifecycle events and forward child session events
Add SubAgentSpawned/Completed/Failed/Closed/Event variants to AgentEvent
with a deferred callback mechanism on SubAgentManager. Child session
events are subscribed to at spawn time and forwarded as wrapped
SubAgentEvent (skipping streaming noise). Wired in both the standalone
CLI and the pipeline AgentBackend.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 19:59:56 -05:00
Bryan Helmkamp
f211ec86d9 Cache ripgrep availability check in all execution environments
The rg availability probe (rg --version / which rg) was running on every
grep() call. Cache the result in a OnceLock/OnceCell per environment so
the probe runs at most once. Also add grep -rn fallback to Daytona env,
matching the pattern already used by local and Docker envs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 19:47:07 -05:00
Bryan Helmkamp
915cdd2a1f Build system prompt once in initialize() instead of per-input
Moves build_system_prompt() from run_single_input() to initialize(),
storing the result as a Session field. This makes the prompt's
static-ness explicit and guarantees Anthropic cache breakpoint 1
always hits. Internal methods (build_request, check_context_usage,
compact_context, estimate_token_count) no longer take a system_prompt
parameter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 19:40:48 -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
3253589193 Simplify LLM crate: constructors, shared HTTP/SSE infra, Deref delegation
- Add ToolResult::success()/error() constructors, replacing 16 manual
  construction sites across tools.rs, session.rs, history.rs, generate.rs
- Collapse ContentPart::RedactedThinking into Thinking (use redacted field)
- Extract HttpApi base struct shared by all 4 provider adapters
- Extract LineReader into common.rs for shared SSE byte buffering and
  timeout handling; convert Anthropic/OpenAI from BoxStream to Response
- Replace manual delegation methods on GenerateResult/StepResult with
  Deref<Target=Response>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 19:12:33 -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
b6d285a79c Remove dead LLM crate code and add --schema to ullm prompt
Remove unused items that have no callers outside their own tests:
middleware wrap_stream_with_middleware/process_stream_event,
common ApiMessage/send_and_read_body, types ProviderEvent variant,
lib CancellationToken re-export, tools execute_all_tools, and
catalog get_latest_model.

Add --schema/-S flag to `ullm prompt` so generate_object() and
stream_object() are exercisable end-to-end. Includes unit test for
invalid JSON rejection and two ignored integration tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 18:31:11 -05:00
Bryan Helmkamp
ab3b3bb050 Wire subagent tools into production session creation
Register spawn_agent, send_input, wait, and close_agent tools in both
AgentBackend::create_session and the agent CLI run() so subagents are
available outside of tests. Child sessions inherit the parent's
tool_approval callback but omit subagent tools to prevent recursive
spawning.

Also classifies subagent tools as auto-approved at all permission levels
and removes unused id/depth fields from SubAgent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 18:23:09 -05:00