ULIDs are lexicographically sortable by creation time, making log
directories and run lists naturally ordered without extra metadata.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make RunConfig.run_id required (String instead of Option<String>) so the
caller always provides the ID. This eliminates the duplicate UUID that
was generated: one in the HTTP server / CLI and a second inside the
engine fallback.
Also fixes a bug in preamble.rs where context key "run_id" was read but
the engine stores it as "internal.run_id", so the run ID always showed
as "unknown" in preambles.
Renames PipelineStarted.id to PipelineStarted.run_id for consistency.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use CSS grid with subgrid for guaranteed column alignment. Remove repo
name, add PR icon, move duration next to status, show CI as dot next
to PR number, add typeahead search. Reverse sort (Merge first).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shared data module at data/runs.ts powers both the kanban board
(/runs) and the vertical list view (/workflows/:name/runs). List view
shows status dot, repo, title, diff stats, CI badge, comments, and
elapsed time in a compact row format.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Definition at /workflows/:name, Diagram at /workflows/:name/diagram,
Runs at /workflows/:name/runs. Detail page is now a layout with Outlet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Detail page shows title, filename, description, and horizontal tabs
for Definition, Diagram, and Runs. Workflow names in the list are now
links to their detail pages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>