Skip to content

Agentic UI: Add Anthropic API key form to AI settings - #4503

Merged
sejas merged 22 commits into
trunkfrom
add-anthropic-api-key-settings
Aug 13, 2026
Merged

Agentic UI: Add Anthropic API key form to AI settings#4503
sejas merged 22 commits into
trunkfrom
add-anthropic-api-key-settings

Conversation

@sejas

@sejas sejas commented Aug 11, 2026

Copy link
Copy Markdown
Member

Related issues

How AI was used in this PR

  • AI helped to build the code and tests.

Proposed Changes

Studio's agentic UI always routes chat through the WordPress.com AI service. This PR lets users bring their own Anthropic API key instead:

  • The AI settings page gains a "Use your Anthropic API key" section, in both the browser UI (studio ui) and the desktop app's agentic UI. A toggle picks the provider and a field below it holds the key. The key is write-only: the renderer only ever receives a truncated preview (sk-ant-api03-6Ic...lgAA), never the key itself.
  • Settings live in ~/.studio/shared.json (the Desktop↔CLI shared config, next to the WordPress.com auth token), so the CLI, browser UI, and desktop stay in sync. Reads fall back to the legacy cli.json location, and a new CLI migration (apps/cli/migrations/08-move-ai-settings-to-shared.ts) moves aiProvider/anthropicApiKey over and strips them from cli.json on first run.
  • The key is validated and saved as you type (debounced): it is checked against Anthropic and a rejected key is not stored — the section shows why, and the field gets a matching error border. The toggle stays disabled until a key has been saved, and switching it on re-checks the key so one revoked since it was saved is caught too. Fail-open throughout: if Anthropic is unreachable (offline, outage), the key is accepted.
  • Clearing the field removes the key and falls back to the WordPress.com provider.
  • With the Anthropic provider active, the model picker only offers Anthropic models (Sonnet 5, Opus 5) — the provider can't serve other families.
  • Resumed conversations keep the provider they started with; only new conversations pick up the switch.
  • A new Tracks event, studio_setting_ai_provider_change, is emitted from the desktop settings handlers when the provider or key presence actually changes (provider, has_anthropic_api_key, surface — the key itself is never sent). Documented in docs/design-docs/analytics-tracks.md.
  • The shared store (packages/common/ai/settings-store.ts) tolerates unknown aiProvider values by falling back to WordPress.com instead of failing, and preserves unrelated config fields untouched.

Scope notes for reviewers:

  • The hosted connector reports the capability as unavailable and hides the section.
  • The key is stored in plaintext, matching the existing precedent for the WordPress.com OAuth token. There is no keychain integration in the repo today.
  • Browser-hosted studio ui writes these settings through apps/local, which has no Tracks emitter, so those changes go uncounted for now (STU-2247).
  • Whether a personal key should bypass the WordPress.com-login gate for agentic features (agenticRequiresAuth) is a product decision and intentionally untouched.

Testing Instructions

Browser UI

  1. npm start, open the Agentic UI
  2. Open Settings → AI. Below "Agentic features" there is a "Use your Anthropic API key" section: description and toggle on the top line, key field underneath. With no key saved, the toggle is disabled.
  3. Type a made-up key (e.g. 1234). Shortly after you stop typing, "Anthropic rejected this API key. Check the key and try again." appears with a red border on the field, nothing is written to ~/.studio/shared.json, and the toggle stays disabled.
  4. Replace it with a real key: it saves and the toggle becomes available.
  5. Switch it on — it stays on and ~/.studio/shared.json has "aiProvider": "anthropic-api-key" plus the key. Reload the page — the field shows the truncated key as its placeholder.
  6. Open a chat composer and check the model picker only lists Sonnet 5 and Opus 5.
  7. Switch the toggle off: shared.json resets to "aiProvider": "wpcom" and the picker offers all models again. Clear the key field: the key is removed from shared.json.
  8. API-level checks: PUT /api/ai-settings stores the key; PUT /api/ai-settings/provider with anthropic-api-key answers 400 when the key is rejected.

Tracks Desktop

  1. npm start (full restart required — new main-process IPC handlers).
  2. Switch to the agentic UI, open Settings → AI, and repeat the browser steps 2–7. The same ~/.studio/shared.json is used, so a key saved in the desktop app is also active for studio ui and the CLI.
  3. Tracks: with STUDIO_DEBUG_TRACKS=1, changing the provider or key logs a studio_setting_ai_provider_change event carrying only provider, has_anthropic_api_key, and surface.

Migration from an earlier build

  1. With Studio stopped, add "aiProvider": "anthropic-api-key" and an "anthropicApiKey" to ~/.studio/cli.json and remove both from ~/.studio/shared.json.
  2. Run any CLI command (e.g. node apps/cli/dist/cli/main.mjs site list): both fields move to shared.json and disappear from cli.json. Values already present in shared.json are kept.
add-antrhopic-api-key.mp4

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

@sejas sejas self-assigned this Aug 11, 2026
@sejas
sejas marked this pull request as ready for review August 11, 2026 12:38
@sejas

sejas commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

We can consider adding a dropdown next to the model when an API key exists, but I prefer to add and discuss that as a follow up.

Screenshot 2026-08-11 at 14 39 02

@sejas
sejas requested a review from bcotrim August 11, 2026 12:40
@wpmobilebot

wpmobilebot commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing a5f9498 vs trunk

app-size

Metric trunk a5f9498 Diff Change
App Size (Mac) 1406.71 MB 1406.75 MB +0.04 MB ⚪ 0.0%

site-editor

Metric trunk a5f9498 Diff Change
load 1058 ms 1069 ms +11 ms ⚪ 0.0%

site-startup

Metric trunk a5f9498 Diff Change
siteCreation 7540 ms 7541 ms +1 ms ⚪ 0.0%
siteStartup 2868 ms 2872 ms +4 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@bcotrim bcotrim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial thoughts after a first review and tests:

  1. The app shouldn't write to cli.json. Let's either move anthropicApiKey and aiProvider to shared.json, or add getters and setters as CLI operations. I'd say shared.json is the easiest for now — it's already the layer for Desktop↔CLI state, and it's where the auth token lives.

  2. Let's make the error layout consistent with the rest of the app. (example from the create site form)

Image

sejas added a commit that referenced this pull request Aug 11, 2026
… Code CLI (#4504)

This PR validates the key at entry time, in both `/api-key` and `/provider` (both funnel through the provider's `prepare` step), and displays an error if the key is not valid.
It only affects CLI. The UI is being implemented in #4503
@sejas

sejas commented Aug 12, 2026

Copy link
Copy Markdown
Member Author

Thanks for the feedback. I changed the code to save the anthropic API key in shared.json and the error style to match existing inputs.

@bcotrim , I would appreciate another review. Thanks!

change-error-color.mp4

@wojtekn

wojtekn commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

@sejas could you please add a Tracks bump? I will merge #4506, and you could use studio_setting_instructions_change as an example.

};

// AI provider settings live in shared.json (they used to target cli.json).
if ( typeof oldData.aiProvider === 'string' ) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should change this migration. It's the move from the old appdata-v1.json to the split configs, so it only runs for pre-split users — it would miss every user already on `cli.json.

Let's add a new migration under apps/cli/migrations/ instead that copies aiProvider and anthropicApiKey into shared.json and then removes them from cli.json.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the suggestion!

sejas added 6 commits August 13, 2026 12:57
…y-settings

# Conflicts:
#	apps/cli/commands/ai/index.ts
#	apps/studio/src/modules/user-settings/lib/ipc-handlers.ts
#	apps/studio/src/modules/user-settings/lib/tests/settings-tracks.test.ts
#	apps/studio/src/preload.ts
#	apps/ui/src/components/settings-view/studio-code-panel.tsx
#	docs/design-docs/analytics-tracks.md
#	packages/common/lib/record-tracks-event.ts
…y-settings

# Conflicts:
#	apps/local/src/index.ts
#	apps/ui/src/ui-classic/components/session-view/composer/index.tsx
@sejas
sejas requested a review from bcotrim August 13, 2026 13:50
@sejas

sejas commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

@sejas could you please add a Tracks bump? I will merge #4506, and you could use studio_setting_instructions_change as an example.

I added the tracks event when the toggle is enabled/disabled here. b26ecb3 The UI version doesn't have any tracking yet.

tracks-event-toggle-key.mp4

@sejas

sejas commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

@bcotrim , @wojtekn , I added tracks and added the migration for aiProvider and anthropicApiKey in the right place. 75312ff#diff-cf956473de32305a64e2d8b454483a4178cadcf7a68e6c70d1cee0795ba631e1

Could you review it again? Thx!

@bcotrim bcotrim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After running some more tests I noticed another behavior:

Switching the Anthropic API key on/off only affects new sessions — existing ones keep the provider they were already using. That makes sense to me, but there's no way to tell which provider a chat is on.

It's not only a missing indicator either: the model dropdown filters by the global setting rather than the session's, so an existing WordPress.com session shows only Anthropic models and hides GPT 5.6 Sol, which it can still use.

We can start with something simple, like a label next to the model dropdown, and revisit later. What do you think?

cc @shaunandrews

@bcotrim bcotrim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After running some more tests I noticed another behavior:

Switching the Anthropic API key on/off only affects new sessions — existing ones keep the provider they were already using. That makes sense to me, but there's no way to tell which provider a chat is on.

It's not only a missing indicator either: the model dropdown filters by the global setting rather than the session's, so an existing WordPress.com session shows only Anthropic models and hides GPT 5.6 Sol, which it can still use.

We can start with something simple, like a label next to the model dropdown, and revisit later. What do you think?

cc @shaunandrews

@bcotrim bcotrim left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After running some more tests I noticed another behavior:

Switching the Anthropic API key on/off only affects new sessions — existing ones keep the provider they were already using. That makes sense to me, but there's no way to tell which provider a chat is on.

It's not only a missing indicator either: the model dropdown filters by the global setting rather than the session's, so an existing WordPress.com session shows only Anthropic models and hides GPT 5.6 Sol, which it can still use.

We can start with something simple, like a label next to the model dropdown, and revisit later. What do you think?

cc @shaunandrews

@sejas

sejas commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

Thanks for pointing that out @bcotrim . Initially I wanted to keep that out of scope of this PR, but I agree that we need some visual indicator. I think we could add the dropdown for users to pick the provider if the API key is saved so they can choose per conversation, I'll create a separate PR for that.

provider-dropdown.mp4

@sejas

sejas commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

@bcotrim , I've created this separate PR so we can decide one or the other #4534

@shaunandrews

Copy link
Copy Markdown
Contributor

Can we move the provider to the same menu as the model picker? Something like this maybe:

image

@shaunandrews

Copy link
Copy Markdown
Contributor
image

And then maybe adjust the display of the menu trigger when using an API key.

@sejas

sejas commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

I'll add the provider in the same dropdown as a follow-up. Great suggestion!

@sejas
sejas merged commit 9def1b2 into trunk Aug 13, 2026
15 checks passed
@sejas
sejas deleted the add-anthropic-api-key-settings branch August 13, 2026 16:54
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.

5 participants