Skip to content

feat(auto): multi-model orchestration of coding agents#137

Merged
adithyn7 merged 14 commits into
mainfrom
feat/auto-model
Jul 7, 2026
Merged

feat(auto): multi-model orchestration of coding agents#137
adithyn7 merged 14 commits into
mainfrom
feat/auto-model

Conversation

@adithyn7

@adithyn7 adithyn7 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Introduces Auto mode — an orchestrator-of-workers coding agent for SuperCoder. The user's task is handed to a planning LLM that decomposes it into a sequence of sub-tasks, each dispatched to a fresh AgentLoop running a model chosen from a user-curated worker pool. Different sub-tasks can run on different models across different providers in a single plan.

Screenshot 2026-07-02 at 4 58 36 PM

How Auto Mode Works

1. Worker Pool

User-curated, in Settings → Auto. Each pool entry is {provider_id, model, description}. The description is a free-text hint the user writes: "cheap exploration", "strong on multi-file edits", "fast codebase reading", etc. Pool can mix providers freely — Anthropic + OpenRouter + OpenAI in the same plan.

2. Planning

On each Auto turn the orchestrator LLM receives the user's task, the full pool listing (models + descriptions), and any prior worker outputs. It emits a plan via a forced submit_plan tool call — a strictly-typed JSON object containing:

  • reasoning: 1–3 sentences explaining the decomposition
  • plan: ordered list of 1–10 workers, each {id, model, prompt, see_prior}

The orchestrator never executes work itself — the sole allowed action is emitting the plan.

3. Approval

The plan card renders in the chat with each worker's model + prompt visible. User clicks Run to proceed or Cancel to reject.

4. Sequential Worker Execution

Workers run one at a time, top-to-bottom. Each worker is a fresh full-featured AgentLoop with the standard coding toolkit (read/edit/bash/grep/glob/write/…) running in the user's working directory. Workers share the file system but not conversation history — w2 can read files w1 created, but only sees w1's final summary if see_prior allows.

see_prior gates upstream context per-worker:

Value Behavior
"none" Worker gets only the orchestrator's prompt
"all" Worker gets every prior worker's final summary
["w1", "w3"] Worker gets only those specific summaries

Cheap models on the bookends, expensive models on the design + edit core.

adithyn7 added 14 commits June 24, 2026 15:28
refractor@3 returns Array<Node>; hast-util-to-jsx-runtime expects a
Root. Without the wrap, fenced code blocks rendered as empty boxes
while inline code worked fine.
- P7 Resume: hydrate AutoRun panel from DB snapshot; approve/cancel
  awaiting_approval runs after restart via agent_resume_auto_turn.
- P8 Cross-provider: worker pool dispatches each model to its own
  provider via build_worker_llm_configs (fail-fast on missing provider
  or duplicate model names). Removes the orchestrator-config-shared-
  with-workers v1 limitation.
- P9a Capped LLM retries in workers (RetryConfig::auto_worker,
  max_retries=1) so transient endpoint failures surface fast.
- P9b Replan budget setting removed (AUTO_REPLAN_BUDGET=0 hardcoded);
  user is the budget via P9c.
- P9c Pause on hard worker failure: new AwaitingFailureDecision
  status + WorkerFailureContext + pending_failure DB column. Surfaces
  Retry / Replan / Cancel in an amber banner; survives restart via
  reaper skip. Cancel emits agent:auto_failed directly so the panel
  transitions without a reload.

Plus: serialized DbAutoStateListener writes (race fix), tolerant plan
parsing (stringified plan + missing see_prior default), tool list
tail-5 + collapse on RunningWorkerCard.
Three product-polish gaps closed before the merge to main plus a
handful of review fixes that landed alongside them.

- Mode gating: Auto is scoped to Code mode. ModelPicker hides the
  Auto entry outside coding; AgentInput.handleModeChange silently
  reverts the model when the user switches away from Code with Auto
  still selected. resolve_session_auto_mode enforces the same
  constraint at the backend as a backstop. Auto-sentinel capability
  is short-circuited in agentSlice so the + attach button and
  context bar stay visible with Auto picked.

- Title generation: run_auto_turn now runs the same substring
  fallback + LLM few-shot title flow as run_agent_turn. Title model
  falls back to the orchestrator's provider when no dedicated title
  model is configured. spawn_title_generation, read_selection and
  resolve_ref exposed pub(crate) for the cross-module call. The
  few-shot prompt was hardened after haiku refused to title
  "list top-level files" as a real request: user messages are
  wrapped in <first_message> tags and the system prompt names the
  specific failure modes to avoid. Adds [title-gen] telemetry.

- Cancel summary: AutoResult::Cancelled now carries a phase string
  captured at each finalize_cancelled call site (Planning vN,
  Replanning vN, Approval for plan vN, Worker wN (model)). The
  Tauri layer renders a structured markdown summary including
  completed workers so the persisted assistant message stays useful
  for the next LLM turn instead of "Auto run interrupted by user".

Also folded in from the pre-merge review pass:
- LlmClientConfig has a manual Debug impl that redacts api_key.
- PendingFailureBanner clears submitting in a finally block so a
  crash between resolve_worker_failure success and the next event
  can't strand the buttons disabled.
- Adds AwaitingFailureDecision to the AutoStatus DB roundtrip test.

52/52 backend auto tests pass (added
cancel_during_second_worker_preserves_prior_success).
Close four bugs in the P9c pause-on-failure flow before merging to main.

1. Race: finalize_awaiting_failure_decision persisted the AutoRun via a
   fire-and-forget mpsc + spawn_blocking, then emitted AutoFailed. The
   frontend refetched the snapshot to check pending_failure, but the
   read and the write both contended for the same Mutex<Connection>
   and could arrive out of order — when the read won, pending_failure
   was null in the returned snapshot, the recovery branch didn't fire,
   and the panel stayed permanently on the red terminal banner with no
   Retry/Replan/Cancel controls. Fix: new AgentEvent variant
   AutoAwaitingFailureDecision carries the WorkerFailureContext inline,
   so the frontend can transition straight to the amber banner with no
   DB round-trip.

2. useAgentEvents' getAutoRun call swallowed any IPC/DB error in a
   .catch(warn). Removing the refetch entirely closes this.

3. setAutoFailed unconditionally wrote status='failed' before the
   async correction landed, causing a visible red-flash on every hard
   failure. The new reducer setAutoAwaitingFailureDecision writes
   'awaiting_failure_decision' + pendingFailure atomically from the
   event payload — no intermediate state.

4. resume_auto_turn never called db.set_session_status(sid, "active"),
   so resumed runs (Retry / Replan / restart-resume) executed with
   the sidebar showing "idle". Mirror run_auto_turn and flip to
   "active" after acquiring the folder lock.

Executor return value (AutoResult::Failed { reason, run }) is
unchanged; only the event channel differs. Terminal-event drain
helper updated to treat AutoAwaitingFailureDecision as terminal
alongside AutoDone / AutoFailed. Existing budget-exhaustion test
updated to assert the new event variant.

Verified: cargo test -p agent (498 passed), cargo check
(agent + tauri), tsc --noEmit (desktop frontend).
…ed plan

Previously if the user clicked Retry and the target worker id somehow
wasn't in the saved plan, `position(...).unwrap_or(0)` silently
restarted the run from index 0 — replaying every already-completed
worker while showing no indication that anything was wrong. That masks
a real data-integrity bug (plan and pending_failure out of sync) as a
confusing UX one.

Return AutoResult::Failed with a diagnostic that names the missing id
and lists the plan's actual worker ids so it's obvious what happened.
Test added to pin the behavior.
Each worker pool entry stored a provider_id (v4 UUID) and the row
rendered it verbatim, so users saw a cryptographic string under each
model name. Look up the provider from the store and render the same
friendly label the ModelPicker uses ("OpenAI" / "Anthropic" / custom
label / base URL host). Falls back to a truncated marker with the raw
id when the underlying provider was deleted after the pool entry was
added — the run-time validator still fail-fasts that case with a
clear error.
@adithyn7 adithyn7 merged commit 54cf972 into main Jul 7, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant