Inline create-API-key flow on orchestration cards#11124
Conversation
When an orchestration card surfaces for a non-Oz harness (Claude Code, Codex, etc.) and the user has no managed API keys for that harness, the card now auto-pops a workspace-level modal that lets the user create a key inline without leaving the conversation. The picker also gains a "+ New API key…" entry users can click any time to re-open the modal, and the Accept button is disabled (with an explanatory tooltip) until the user has either picked a managed key or explicitly chosen to inherit credentials from the worker environment. To support hosting the existing AuthSecretFtuxView inside a workspace modal, the FTUX view and dropdown are decoupled from the cloud-mode AmbientAgentViewModel and instead take a harness parameter directly, emitting Created/Cancelled/Skipped/Failed events that the workspace subscribes to. The auth-secret selection on each card is now modeled as an enum (Unset/Inherit/Named) instead of a brittle two-field pair, which removes a class of edge cases around defaulting and persistence. Co-Authored-By: Oz <oz-agent@warp.dev>
Documents the inline create-API-key flow on orchestration cards: the product behavior (picker contents, auto-open one-shot, Accept gate, modal lifecycle) and the implementation (AuthSecretSelection enum, decoupled FTUX view, workspace modal host, card-side wiring). Co-Authored-By: Oz <oz-agent@warp.dev>
… dispatch Adds the same create-key auto-pop modal flow to OrchestrationConfigBlockView that the RunAgents card already has, plus an auto-expand of the details panel so the modal opens with the picker visible behind it. To avoid popping the modal on session restoration (the plan card has no document-level equivalent of \`block_model.is_restored()\`), the auto-pop is gated on user interaction. An \`arm_for_fresh_dispatch\` method lets the lazy-creation path in AIDocumentView (which fires only for incoming OrchestrationConfigSnapshot messages, never on restore) arm the auto-open on the agent's behalf. Explicit user actions (toggle approval, change harness, switch mode) also arm it. Co-Authored-By: Oz <oz-agent@warp.dev>
The plan-card auto-pop modal was still firing on session restoration because OrchestrationConfigUpdated is emitted from the restore-time scan_conversation_for_orchestration_config hydration path, not only from live wire messages as previously assumed. The lazy-creation-in- AIDocumentView signal therefore wasn't sufficient to distinguish fresh agent dispatches from restoration. Add a from_restore: bool field on the event variant, populated at each of the three emit sites: live OrchestrationConfigSnapshot messages (false), user edits via the plan-card config block (false), and restore-hydration scan (true). The plan card's lazy-creation arming now gates on !from_restore so the create-key modal is fully suppressed on session restoration. Other consumers ignore the new field. Co-Authored-By: Oz <oz-agent@warp.dev>
apply_create_new_auth_secret_requested was calling persist_auth_secret_selection(None) every time the user clicked '+ New API key…' on the picker, which permanently removed the user's prior selection from CloudAgentSettings.last_selected_auth_secret. If the user already had a managed key selected, clicked '+ New API key…' to add another, and then cancelled the modal, their existing selection was lost from settings and would not be restored on the next session. Drop the persist call from the helper. The in-memory selection still goes to Unset (so the picker shows '+ New API key…' while the modal is open or after cancel), but the persisted name is preserved. On successful creation the workspace modal already writes the new name to settings, overwriting any prior value. Co-Authored-By: Oz <oz-agent@warp.dev>
Cleaned up the long doc/inline comments added throughout the inline create-API-key PR. Kept genuinely useful context (circular-update panic notes, restoration gating rationale) and dropped rotting cross-file references and verbose explainers. Co-Authored-By: Oz <oz-agent@warp.dev>
…n-create-auth-secret # Conflicts: # app/src/ai/blocklist/inline_action/orchestration_controls.rs # app/src/ai/blocklist/inline_action/run_agents_card_view.rs
|
I'm starting a first review of this pull request. You can view the conversation on Warp. I completed the review and no human review was requested for this pull request. Comment Powered by Oz |
There was a problem hiding this comment.
Overview
This PR adds an inline managed API-key creation flow to orchestration confirmation cards and plan-card config blocks, reusing the cloud-mode FTUX view inside a workspace modal. The general structure is reasonable, and the PR includes Loom evidence for the user-facing flow.
Concerns
- The Accept gate remains open while auth-secret fetches are pending, so a fast accept can still dispatch an unset secret as environment inheritance before the user makes the required explicit choice.
- The confirmation card re-arms the auto-open guard on stream completion/model-only streaming updates, which can re-pop the modal after the user cancels it for the same harness/mode.
- Plan-card construction suppresses auto-open unconditionally, which misses the live approved-plan case when the block is created after the config already exists.
Verdict
Found: 0 critical, 3 important, 0 suggestions
Request changes
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
| return false; | ||
| } | ||
| let availability = HarnessAvailabilityModel::as_ref(ctx); | ||
| matches!( |
There was a problem hiding this comment.
Loaded, so the card is still clickable during NotFetched/Loading and dispatches Unset as None (inherit) if the user accepts quickly. Treat pending fetch states as disabled until the user explicitly chooses a key or Inherit.
| self.refresh_accept_button_state(ctx); | ||
| // Stream completion is the authoritative final snapshot — re-arm | ||
| // the auto-open in case earlier evaluations fired against stale state. | ||
| self.has_auto_opened_create_modal = false; |
There was a problem hiding this comment.
Loom: https://www.loom.com/share/53bb766f3f9a4ecc983d516716a88627
Loom 2 (updates for plan cards): https://www.loom.com/share/532d7a88ef084b5baee269f041ad1ccc
Slack thread: https://warpdev.slack.com/archives/C0AAMT5TKC2/p1778989617745909?thread_ts=1778872788.221739&cid=C0AAMT5TKC2
Resolves https://linear.app/warpdotdev/issue/QUALITY-702/add-flow-to-create-a-new-key-when-orchestrating-non-oz-harness-in-the
Description
Product flow
Today, when an orchestration card surfaces for a non-Oz harness (Claude Code, Codex, etc.) and the user hasn't set up an API key for that harness yet, the card is effectively a dead-end: the "API key" picker is empty, Accept either silently dispatches with inherited environment credentials or fails downstream, and there's no clear "create one now" affordance. The only path forward is to drop out of the conversation, go find the cloud-mode FTUX, create a key there, then come back. That's a poor first-run experience and a real blocker for users trying to orchestrate with a fresh harness.
This PR fixes that by making the orchestration card itself the place where the user can finish setup. If a card needs an API key and none exist, the workspace pops a blocking modal that hosts the same create-key form used by cloud mode, scoped to the card's current harness. The picker also grows a permanent "+ New API key…" entry so users can re-open the modal any time, even when other keys already exist. The Accept button stays disabled — with a tooltip explaining why — until the user has either picked a managed key or explicitly clicked "Inherit key from environment" as a deliberate choice. After a key is created the card auto-selects it and re-enables Accept, so the user can keep going without a single context switch. The whole experience now matches what cloud mode has had for a while, and works identically whether the orchestration card was raised by the user or by an agent mid-task.
Technical design
The bulk of the work was decoupling the existing create-key view from cloud mode's ambient agent model so it could be reused inside a generic workspace modal. Previously the FTUX view and its harness-picker dropdown both reached straight into the cloud-mode view model for harness selection and for the side effects that happen on success (persisting the selected secret, marking FTUX completed, etc.). Both now take a plain harness value at construction time, expose a setter for harness changes, and instead of mutating model state directly they emit a small set of lifecycle events — Created, Cancelled, Skipped, Failed — that their host subscribes to. Cloud mode's input layer wires those events back to the same side effects it used to perform inline, so its UX is unchanged.
On the card side, the user's auth-secret choice used to be represented as an optional name plus a sibling boolean for "explicitly inherit". That two-field encoding had a handful of subtle edge cases around defaulting, persistence, and round-tripping through the wire format. It's now collapsed into a single three-state enum — Unset, Inherit, Named — which makes the picker label, the Accept gate, and the per-harness persistence logic each reduce to a small match. The "+ New API key…" entry is a new variant on the shared orchestration-control action trait, so both the confirmation card and the plan-card config block can bubble it up to their parents and ultimately to the workspace, which owns the modal. The workspace subscribes to the FTUX view's lifecycle events to persist the new key and close the modal; cards listen for a harness-availability event so they can adopt the freshly-created key as their selection. A one-shot guard on each card ensures the auto-pop fires at most once per harness/mode change, so a user who cancels the modal isn't immediately re-prompted, but switching harness gives them a fresh chance.
Testing
./script/run