After each checkpoint commit, the metadata branch (checkpoint.json, manifest,
graph DOT, artifacts) is now pushed from the host process to the GitHub remote
using a GitHub App installation token. The local custom ref (refs/arc/{run_id})
is mapped to refs/heads/arc/meta/{run_id} on the remote since GitHub rejects
branch names starting with "refs/".
Changes:
- Move ssh_url_to_https to github_app.rs as pub fn for reuse
- Add push_ref() to git.rs for pushing a ref to an explicit URL
- Add github_app field to RunConfig to thread credentials into the engine
- Add git_push_meta_host() async wrapper in engine.rs
- Call git_push_meta_host after each remote checkpoint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces a [checkpoint] config table with exclude_globs in both run.toml
(per-run) and server.toml (defaults). Globs are merged (union + dedup) when
both are present. Non-empty excludes use git pathspec :(glob,exclude) syntax
to prevent staging matching files during checkpoint commits.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enables `arc models test` to work in server mode by adding an API
endpoint that sends "Say OK" (max_tokens=16, 30s timeout) to a model
and reports pass/fail. Dry-run mode returns synthetic "ok" status.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ModelInfo already derives Serialize with matching field names, so the
manual json!({...}) mapping was redundant. Matches the pattern used by
every other paginated handler in demo/mod.rs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Exposes the embedded model catalog (same data as `arc models list`) via
a new authenticated API endpoint so the web UI can display available models.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restructure the verification API from a flat `/verifications` namespace to
`/verification/criteria` and `/verification/controls` as distinct resources.
Singularize the run sub-resource path to `/runs/{id}/verification`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New `arc-exe` crate that runs agent tool operations inside ephemeral
exe.dev VMs via SSH. Uses two SSH connections: a management plane
(`ssh exe.dev`) for VM lifecycle and a data plane (`ssh vmname.exe.xyz`)
for command execution and file I/O.
Includes SshRunner trait with MockSshRunner for unit tests and
OpensshRunner for real SSH, with raw_mode for the exe.dev management
plane's custom command handler.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace append-based .env writing with mergeEnv() that upserts keys,
preventing duplicates when setup runs multiple times. Capture the GitHub
App slug from the manifest API response and persist it in TOML config
so we can construct installation URLs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use SmoothnessRating enum instead of String for smoothness filter param
- Remove duplicate default_page_limit(), reuse pub fn from server module
- Replace Json(json!(detail)) with Json(detail) to avoid double serialization
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace untyped JSON blob on /runs/{id}/retro with a structured RetroDetail
schema mirroring the Rust Retro type. Add workflow and smoothness filter
parameters to /retros. Document optionality of smoothness and total_cost fields.
Convert demo handler from inline JSON to typed structs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Define typed RunCheckpoint schema replacing untyped object on checkpoint endpoint
- Rename WorkflowDetail.title to name for consistency with WorkflowListItem
- Make RunConfiguration.goal optional (only version and graph required)
- Add optional requestBody (StartWorkflowRunRequest) to startWorkflowRun
- Remove dead tools field from SystemStageTurn and AssistantStageTurn
- Add cancelled to StageStatus enum
- Document HookDefinition field-to-type mapping
- Add oneOf type to DaytonaConfiguration.network (string enum or allow_list object)
- Document stages pagination cardinality (bounded by graph size, typically <20)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses agreed items from the openapi-hitl-and-run-outputs review:
rename retrieveRunDiff operationId, add 409s to steer/preview, bound
expires_in_secs, add selected_option_keys for multi-select end-to-end,
document skip/na semantics, add slug and require type on
RunVerificationControl, require file on CodeLocation, and remove the
checkpoint "all" sentinel in favor of omitting the parameter.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add pagination params to /verifications endpoint
- Rename operationIds: listVerificationCategories, retrieveVerificationControl
- Merge VerificationStatus and EvaluationResult into unified VerificationResult enum
- Rename ControlDetail.description to rationale to avoid ambiguity
- Add 400 error response to POST /insights/execute
- Add GET /insights/queries/{id} endpoint with 404 handling
- Type ExecuteQueryResponse.rows items with oneOf instead of empty schema
- Make SavedQuery.updated_at required, default to created_at for unmodified queries
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace raw TOML / UI-oriented SettingGroup responses with structured
JSON that mirrors the Rust config types (WorkflowRunConfig, ServerConfig).
- Add Serialize derives to all config types in arc-workflows and arc-api
- Add manual Serialize impl for DaytonaNetwork enum
- Update OpenAPI spec: /runs/{id}/configuration returns RunConfiguration,
/settings returns ServerConfiguration, WorkflowDetail.config is now
RunConfiguration object instead of TOML string
- Remove SettingGroup/SettingField/SettingFieldType schemas
- Update demo handlers to construct and serialize real config structs
- Regenerate TypeScript client
- Simplify settings page to JSON display, update run-configuration and
workflow-definition pages to show JSON
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The API was returning human-readable strings like "2h ago" and "in 4 hours"
instead of real timestamps. The frontend now formats these for display using
timeAgo() and a new timeUntil() helper.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update all struct constructions in demo data and server handlers to match
the new nested sub-schemas. Fix aggregate usage handler to use
AggregateUsageTotals wrapper, ModelReference, and TokenUsage. Update
test assertions from flat field paths to nested paths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract millis_u64 and save_json/load_json to shared crate root, replacing
identical copies in engine, parallel, human, conclusion, manifest,
checkpoint, and retro modules
- Remove exists() pre-checks before Manifest::load and Conclusion::load
in CLI runs scanner (TOCTOU anti-pattern)
- Merge duplicate cancel_run match arms for Queued/Starting/Running
- Change Conclusion.status from String to StageStatus enum
- Extract RunFilterArgs shared struct from RunsListArgs/RunsPruneArgs
- Replace hand-rolled formatDuration with formatDurationMs wrapper over
existing formatDurationSecs
- Extract duplicated ToolRow/ToolBlock components to shared tool-use.tsx
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Capture chrono::Utc::now() once in start_run so the stored ManagedRun
and the StartRunResponse return the same created_at. Move the identical
ts() timestamp-parsing helper from 4 demo submodules to the parent scope.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enrich the entire OpenAPI spec with descriptions, examples, format
annotations, extracted parameters, discriminated unions, and richer
response schemas — matching the patterns already established for
Sessions.
Structural changes:
- Extract CancelRunResponse and SteerRunResponse from inline schemas
- Change POST /runs/{id}/steer from 200 to 202 (async fire-and-forget)
- Split StageTurn into discriminated union (SystemStageTurn,
AssistantStageTurn, ToolStageTurn) with oneOf + discriminator
- Add status + created_at to StartRunResponse
- Add created_at to RunListItem and RunStatusResponse
- Add created_at + updated_at to SavedQuery
- Add format: date-time on RetroListItem.timestamp
- Extract 7 new reusable parameters (StageId, QuestionId, WorkflowName,
VerificationSlug, InsightQueryId, ProjectId, CheckpointFilter)
- Enrich existing RunId, PageLimit, PageOffset parameters
- Add descriptions + examples to all ~66 schemas and ~200+ properties
- Add operation descriptions to all ~35 endpoints
Rust handler updates:
- Change ManagedRun.created_at from Instant to chrono::DateTime<Utc>
- Populate new fields in StartRunResponse, RunStatusResponse
- Use new StageTurn discriminated union variants in demo data
- Update demo data timestamps to use DateTime<Utc>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each SSE frame now includes a sequential numeric id: field. Clients can
reconnect with the Last-Event-ID header to resume the stream after the
last received event, skipping already-processed events.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add updated_at to CreateSessionResponse for consistency with SessionListItem/SessionDetail
- Add format: uuid to session ID fields and parameter across the OpenAPI spec
- Remove SessionEvent discriminated union and SessionEvent* wrapper schemas that conflated
SSE transport-level event names with JSON data payload fields
- Update demo data to use proper UUIDs instead of string IDs
- Add uuid dependency to arc-types crate
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add created_at to AssistantTurn and ToolTurn schemas (matching UserTurn)
- Document SSE event types (assistant_turn, tool_turn, done, error) with
SessionEvent discriminated union schema
- Add title and model to CreateSessionResponse
- Add model and last_message_preview to SessionListItem
- Update demo data with timestamps, model, and preview fields
- Regenerate TypeScript API client
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mintlify requires all referenced files under docs/, so consolidate to a
single copy and eliminate the symlink and the copy step in the generate
script.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Refactor SessionTurn into discriminated union (UserTurn, AssistantTurn, ToolTurn)
using oneOf + discriminator so invalid states are unrepresentable
- Add format: date-time to all timestamp fields for proper codegen types
- Rename CreateSessionRequest.prompt to .content for consistency with SendMessageRequest
- Change sendSessionMessage from 200 to 202 (async processing via SSE)
- Extract inline response to SendMessageResponse schema
- Add updated_at to SessionListItem for sort-by-activity support
- Extract SessionId parameter to components/parameters (DRY)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add id, is_error, duration_ms to ToolUse; rename args to input
- Add created_at/updated_at timestamps to session schemas; replace time/date display strings
- Add descriptions and examples to all Sessions API fields and endpoints
- Flatten List Sessions response from grouped SessionGroup[] to SessionListItem[]
- Move date grouping (Today/Yesterday/etc.) to React client via groupSessionsByDate()
- Symlink docs/api-reference/arc-api.yaml to canonical openapi/arc-api.yaml
- Update React ToolRow components with duration display and error styling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a FeatureFlags config section with a session_sandboxes boolean
(default false) to both the Rust server config and web app config.
Gate the project/branch picker UI behind this flag. Set it to false
in the Docker demo config.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the server-level `--demo` flag with per-request demo dispatch.
The Rust API builds both a demo and real router; incoming requests with
the `X-Arc-Demo: 1` header hit the demo router (auth disabled, static
data), all others hit the real router with normal auth.
The React web app gets a beaker icon toggle in the top nav bar (next to
the theme toggle) that sets an `arc-demo` cookie. Loaders read the
cookie to decide whether to send the `X-Arc-Demo: 1` header to the API.
The `ARC_DEMO=1` env var still works as a default when no cookie is set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep legacy aliases (agent_loop, one_shot) in the handler registry
and validation rules for backwards compatibility. Add codergen_mode
attribute support in the DOT parser, translating legacy values to
the new type names.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rename handler type strings: codergen → agent_loop, wait.human → human,
script → command, wait.timer → wait
- Rename handler modules/structs to match: AgentHandler, HumanHandler,
CommandHandler, WaitHandler
- Split one_shot into PromptHandler (handler/prompt.rs) with shape=tab mapping
- Remove CodergenMode enum and codergen_mode attribute — one_shot is now its
own handler type, not a mode flag on the agent loop handler
- Update all demo DOT files: codergen_mode="one_shot" → shape=tab
- Update spec, README, validation rules, preamble, and hook tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce a configurable hook system that triggers user-defined actions
at workflow lifecycle points (RunStart, StageStart, StageComplete,
StageFailed, EdgeSelected, CheckpointSaved, etc). Hooks can block
execution, skip nodes, or override edge routing via JSON decisions.
- New `hook/` module: types, config, executor (command), runner
- Engine instrumented at 8 lifecycle points with HookRunner calls
- TOML config: `[[hooks]]` in server.toml and run config files
- Config cascade: server hooks + run hooks merge, name collisions
resolved by run config winning
- Remove legacy tool_hooks.pre/post from codergen handler (breaking)
- 30 e2e integration tests covering all hook events and behaviors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename the endpoint, schema (RunFiles -> RunCompare), operation ID,
handlers, and frontend route across the full stack.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The question_type field on ApiQuestion was a bare string serialized via
Debug formatting. Define a proper enum in the spec so typify generates a
typed QuestionType, then map from the workflow enum in the handler.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace inline json!() wrappers with a shared ListResponse<T> struct
that serializes directly, avoiding the intermediate serde_json::Value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap 4 list endpoints that returned bare arrays in the standard
paginated `{ data, meta: { has_more } }` shape so adding real
pagination later is additive rather than a breaking change.
Endpoints: GET /runs/{id}/questions, /runs/{id}/stages,
/runs/{id}/verifications, and /verifications.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When an LLM provider returns a transient error (rate limit, server error,
quota exceeded, timeout, network, or stream failure), Arc now automatically
retries on fallback providers using the closest matching model from the
catalog based on capability filters and cost proximity.
Key changes:
- closest_model() and build_fallback_chain() in arc-llm catalog
- failover_eligible() on SdkError to classify transient vs deterministic errors
- fallbacks config field on LlmConfig with task-wins-over-defaults merging
- WorkflowRunEvent::Failover variant for observability
- Failover logic in both one_shot and agent session (run) code paths
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces 10 identical copies of the pagination block with a single
shared function. Also eliminates a redundant second collect() by
using truncate() instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply the same page[limit]/page[offset] pagination pattern from
GET /runs to: workflows, workflow runs, retros, sessions, projects,
branches, saved queries, query history, and stage turns.
Each endpoint now returns { data, meta: { has_more } } instead of
a bare array. Includes OpenAPI spec updates, demo handler changes,
regenerated TS client, updated frontend consumers, and a new
pagination conformance test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
load_server_config() now accepts an optional explicit path. When
provided, it reads from that path (erroring if missing) instead of
the default ~/.arc/server.toml. The --config flag is wired through
ServeArgs and the hot-reload polling loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- AppState.server_config was stored but never read; remove it and revert
create_app_state_with_options back to 5 parameters
- Config polling now compares under a read lock first, only acquiring
the write lock when a change is detected
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Poll ~/.arc/server.toml every 5s and swap run_defaults/git config for new
runs without restarting. CLI overrides (--model, --provider) always win.
On parse error, log a warning and keep the previous config.
Also add a `default` field to the model catalog so default model resolution
uses catalog data instead of hardcoded model names in Rust code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Runs now go through a state machine (queued → starting → running →
completed/failed/cancelled) instead of spawning immediately. A background
scheduler promotes queued runs when capacity is available, defaulting to
4 concurrent runs. Configurable via --max-concurrent-runs CLI flag or
max_concurrent_runs in server.toml.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce page[limit]/page[offset] query params and { data, meta: { hasMore } }
response wrapper for the /runs endpoint, establishing the pagination pattern
for all future list endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace opaque (i64, i64, i64, f64) tuple with ModelUsageTotals struct
for readable field access. Remove redundant top-level token/cost fields
that duplicated by_model sums — derive them at read time instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tracks total runs, input/output tokens, cost, and runtime in-memory
(resets on server restart). Accumulates from checkpoint node_outcomes
when runs complete. Includes per-model breakdown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All API errors now return a consistent JSON shape:
{"errors": [{"status": "4xx", "title": "...", "detail": "..."}]}
Introduces ApiError type with IntoResponse impl, replaces bare
StatusCode returns and ad-hoc {"error": "..."} responses in all
handlers and auth extractors. Updates OpenAPI spec and regenerates
TypeScript client.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add discovery, health check, OpenAPI spec, and current user endpoints.
The first three are public; /user requires authentication and returns
the login extracted from JWT sub claim, mTLS CN, or "demo" in demo mode.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Install ring CryptoProvider at CLI startup to prevent rustls panic
- Skip TLS in demo mode so the server uses plain HTTP
- Add ARC_DEMO=1 env var to web app config to bypass GitHub OAuth
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both verbose and normal modes now use the same indicatif-based ProgressUI
renderer with the same visual hierarchy (indentation, glyphs, colors).
In verbose mode: tool calls persist after stage completion, no 5-call cap,
stage completion shows stats (turns, tool calls, tokens), and additional
events are rendered (edge transitions, loop restarts, setup commands,
retries, context warnings, compaction, subagents).
Remove the old format_event_summary function and its ~70 tests, the
verbose stderr printing from AgentApiBackend, and the verbose/styles
fields that are no longer needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>