#17410 · @BYK · opened Mar 13, 2026 at 6:44 PM UTC · last updated Mar 21, 2026 at 10:24 AM UTC

fix(app): defer provider and agent fetches from blocking bootstrap

appfix
70
+784 files

Score breakdown

Impact

8.0

Clarity

9.0

Urgency

7.0

Ease Of Review

9.0

Guidelines

9.0

Readiness

9.0

Size

9.0

Trust

5.0

Traction

2.0

Summary

This PR optimizes the application's initial load time by making provider and agent data fetches non-blocking during bootstrap. This allows the UI to render faster, improving user experience. The change is small, focusing on deferring these requests.

Open in GitHub

Description

Issue for this PR

Closes #17408

Type of change

  • [ ] Bug fix
  • [ ] New feature
  • [x] Refactor / code improvement
  • [ ] Documentation

What does this PR do?

During global sync bootstrap, the provider list, provider auth, and agent list fetches are all marked as blocking requests. The UI cannot render until all three complete. However, provider and agent data are not needed for the initial render — the session list and message timeline are what the user sees first.

This PR moves the provider and agent fetches out of the blockingRequests object and into fire-and-forget calls that populate the store asynchronously. The UI now renders as soon as sessions are loaded, with provider/agent data appearing when their respective API calls complete.

The change is a one-file, 4-insertion/5-deletion diff in bootstrap.ts.

How did you verify your code works?

  • Tested locally — UI renders faster, provider/agent dropdowns populate shortly after
  • No regressions in session list or message rendering

Screenshots / recordings

N/A — logic change in bootstrap flow.

Checklist

  • [x] I have tested my changes locally
  • [x] I have not included unrelated changes in this PR

Linked Issues

#17408 Slow initial load — provider and agent fetches block bootstrap

View issue

Comments

No comments.

Changed Files

packages/app/src/components/dialog-connect-provider.tsx

+11
@@ -383,7 +383,7 @@ export function DialogConnectProvider(props: { provider: string }) {
setFormStore("error", undefined)
await globalSDK.client.auth.set({
providerID: props.provider,
auth: {
body: {
type: "api",
key: apiKey,
},

packages/app/src/components/dialog-custom-provider.tsx

+11
@@ -131,7 +131,7 @@ export function DialogCustomProvider(props: Props) {
const auth = result.key
? globalSDK.client.auth.set({
providerID: result.providerID,
auth: {
body: {
type: "api",
key: result.key,
},

packages/app/src/context/global-sync/bootstrap.ts

+45
@@ -124,11 +124,6 @@ export async function bootstrapDirectory(input: {
const blockingRequests = {
project: () => input.sdk.project.current().then((x) => input.setStore("project", x.data!.id)),
provider: () =>
input.sdk.provider.list().then((x) => {
input.setStore("provider", normalizeProviderList(x.data!))
}),
agent: () => input.sdk.app.agents().then((x) => input.setStore("agent", x.data ?? [])),
config: () => input.sdk.config.get().then((x) => input.setStore("config", x.data!)),
}
@@ -149,6 +144,10 @@ export async function bootstrapDirectory(input: {
if (input.store.status !== "complete") input.setStore("status", "partial")
Promise.all([
input.sdk.provider.list().then((x) => {
input.setStore("provider", normalizeProviderList(x.data!))
}),
input.sdk.app.agents().then((x) => input.setStore("agent", x.data ?? [])),
input.sdk.path.get().then((x) => input.setStore("path", x.data!)),
input.sdk.command.list().then((x) => input.setStore("command", x.data ?? [])),
input.sdk.session.status().then((x) => input.setStore("session_status", x.data!)),

packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx

+11
@@ -265,7 +265,7 @@ function ApiMethod(props: ApiMethodProps) {
if (!value) return
await sdk.client.auth.set({
providerID: props.providerID,
auth: {
body: {
type: "api",
key: value,
},