# Provider and connection component

AgenticDriver includes one framework-neutral web component, `<agenticdriver-providers>`, with backend bindings for TypeScript, Python (sync and async), Go, and Rust (blocking and async). Each language package contains the same browser module. Web apps and desktop webviews can embed it without installing a second provider runtime or a JavaScript framework.

These additions are packaged in the [0.2.0 alpha](/docs/alpha); the published 0.1.0 packages predate them. Pin the exact alpha and check host capabilities before enabling management controls.

The shared icon library and connection-information card described below are
introduced in `0.2.0-alpha.3`. Check the [alpha release status](/docs/alpha) before
installing; earlier SDK versions do not include these additions.

The component uses a constructed shadow stylesheet, with no inline style element
or style attribute. Modern browsers/webviews with `CSSStyleSheet.replaceSync`
and `ShadowRoot.adoptedStyleSheets` can embed it under `style-src 'self'` without
adding `unsafe-inline` or a nonce. Serve its JavaScript from an origin permitted
by the application's existing script policy. The standalone panel also serves
its surrounding page CSS as a same-origin asset.

## Try the complete setup flow

From a built SDK checkout, open the panel:

```sh
npm ci
npm run build
node dist/cli.js panel --connection "$PWD/.private/desktop-connection.json"
```

Open the private loopback link it prints. The link carries an ephemeral panel access key in its fragment; the page removes the fragment and retains that key only in this browser tab’s session storage. Restarting the panel invalidates it. This key is separate from the long-lived host credential, which remains on the backend. The disconnected panel explains local and remote setup. For a local Codex account, run this in another terminal:

```sh
node dist/cli.js setup --provider codex --manage --config "$PWD/.private/host/config.json"
```

Paste its one-use invitation into the panel. The backend exchanges it and saves a private connection profile and separate credential file. Leave both processes running. Existing hosts use `agenticdriver pair` with the existing config; `setup` creates a fresh config and will not overwrite one. Use the actual installed native executable with `--binary` and account directory with `--account-directory` when required by your provider installation.

For another machine, its operator starts an HTTPS host and supplies an invitation containing its reachable URL. See [local and remote connection setup](/docs/connections) for TLS, scopes, expiry, revocation, existing hosts and exact commands. A hosted application's backend must be able to reach that URL; its `127.0.0.1` refers to the server, not the person using the browser. The standalone panel binds only to loopback. Embed the component in your existing authorized settings page when serving remote browsers.

## Runnable language examples

After building the shared asset with `npm run build`, each language can serve the same panel with its own backend:

```sh
# Python (inside your virtual environment)
python -m pip install ./clients/python
python clients/python/examples/provider_panel.py

# Go
(cd clients/go && go run ./examples/provider-panel)

# Rust
cargo run --manifest-path clients/rust/Cargo.toml --example provider_panel
```

Each prints a private loopback URL. Open it and paste a host invitation, just as in the standalone panel. These small examples keep the exchanged host credential in backend memory and forget it on shutdown; the host grant retains its original expiry until revoked. In a real application, use the existing private secret store and settings authorization. `AGENTICDRIVER_CA_FILE` optionally names a private CA certificate for the selected host; certificate and hostname verification remain enabled. The examples never call a model.

The Go and Python examples use their standard HTTP servers. The Rust example uses [Hyper's HTTP server API](https://hyper.rs/guides/1/server/hello-world/) through development dependencies only; these are not added to the SDK's runtime dependencies.

## What the component owns

- Provider instance list, refresh time, reported health and account identifier.
- Searchable provider picker and guided connection methods reported by the selected host: existing native sign-in, owned Codex device sign-in, write-only API key, host credential reference and compatible endpoint. Account identity, executable paths and other advanced settings remain available.
- Revision-checked remote changes to display name, enabled state, native executable/account directory, reasoning effort, API endpoint and credential reference; API keys are write-only.
- Confirmed removal when `management.removalSupported` is true. Older hosts and
  read-only connections do not show this control. Removal uses the existing
  configure operation with the current provider, revision and `remove: true`.
- Reported model inventory plus explicitly configured IDs, search, optional model allowlists, and all-model access by default.
- Device-local favorites, visibility and ordering, isolated by connection ID and provider instance.
- Disconnected setup, pairing hooks, invitation creation and connection revocation for authorized managers.

The component does not start inference. A model selection emits `agenticdriver:model-selected` with `{ provider, model }`; the application owns subsequent runs. `agenticdriver:preferences-changed` contains `{ provider, favorites, hidden, order }`. Hidden models remain visible but muted in settings so they can be restored.

Inventory, host configuration and execution grants are distinct. An empty model allowlist denies all models; an omitted allowlist permits any explicitly chosen model. A reported model is not marked live-tested. Managers can see all instances; `management.executionProviders` identifies which they can actually run. Management-only connections cannot select models for execution. Hosts predating that field leave management-side selection disabled until their permissions can be determined. Refresh and settings changes do not cancel active runs.

Removal stops new runs on that instance and preserves active runs, native
sign-ins, private key files and existing grant records. Explicitly adding the
same instance ID again makes it available to grants that still name it; use a
new ID for a new account. If static host grants reference the provider, removal
fails with `PROVIDER_IN_USE`: disable the provider or have the host operator
update those grants first. Account policies must also remain valid; the panel
does not silently rewrite operator-owned policy. Extensions remain operator-owned.

In alpha.4, a failed snapshot refresh or sign-in-status poll shows
**Connection unavailable**. A snapshot with `connected: false` and a saved
`connection` enters the same recovery view; a disconnected snapshot without a
saved connection shows initial setup according to the application's permissions.
Previous provider/account details, model
selection and management controls are hidden until **Retry connection** obtains
fresh state. The selected provider and device-local model/icon preferences remain
scoped to the saved connection. Retrying reads metadata; it never repeats a
configuration write, pairs another host or restarts a run. An older failed refresh
cannot replace a newer successful view. **Disconnect saved host** is offered only
when the application supplies that permission. Initial backend failures also offer
a retry instead of showing a connection wizard before permissions are known.

Provider-native installation, token exchange, storage and refresh remain with the official runtime on the provider machine. Qualified hosts can guide [Codex device sign-in](/docs/provider-sign-in) from the component. Extension settings remain host-operator owned. This release does not install native binaries or expose arbitrary process arguments/environment variables. Codex device sign-in opens the official provider page in the user's browser. Supported settings match the SDK's qualified runtime adapters.

## Add a provider connection

Choose **Add provider**, search for the provider or gateway, and select its advertised connection method. The panel identifies the connected host before requesting any credentials. API connections accept a key once or reference an existing host environment variable/private file; compatible endpoints also require their URL. Native connections use the official runtime's existing sign-in or an advertised owned sign-in method, with setup requirements shown before proceeding. Expanding **Advanced settings** exposes the instance/account IDs and native executable/account directory.

Saving a connection does not run a model, start provider sign-in or extend any application's execution grant. Model discovery may run afterward and reports its own status. New instances allow all explicitly selected models by default; use **Models** for a connection allowlist. A gateway connection identifies that gateway account, not a guaranteed upstream provider/account; configure upstream routing and billing at the gateway.

Owned sign-in presents its device code, polls caller-owned setup state and requires explicit account confirmation after native verification. It stays available through refresh and can be cancelled without changing shared accounts. The component hides this method when `provider-setup` is absent.

The shared component reads optional `management.providerDefinitions`. Older hosts without that field retain the original `supportedKinds` settings form. All four language packages preserve this metadata and Codex's `applicationTools` setting when configuration is saved. [Provider management](/docs/provider-management#connection-definitions) documents the typed contract. The [connection design review](/docs/provider-connection-design) records the T3 Code, OpenCode, gateway and Langfuse references and the native sign-in lifecycle.

## Embed in an existing application

Serve the supplied JavaScript as a same-origin module, insert the supplied HTML in a settings page, and mount the language bridge at its `api` path. The bridge resolves the already-connected SDK client **on the backend for the current application user**. Keep long-lived host credentials and provider keys out of browser bundles, HTML and browser storage.

Use the application's existing settings authorization and CSRF checks on every bridge request, including snapshots, connect, disconnect and mutations. Limit request bodies to 1 MB, return JSON with `Cache-Control: no-store`, and do not log request bodies (they may contain one-use invitations or replacement API keys). The SDK does not add application authentication. A webview may instead assign an application-owned `transport` function using its existing IPC.

The same component can be used in React, Vue, Svelte, plain HTML, server-rendered pages or a desktop webview. It uses an open shadow root. Set `theme="light"` for its light palette; override `--ad-bg`, `--ad-surface`, `--ad-field`, `--ad-line`, `--ad-fg`, `--ad-muted`, `--ad-accent`, and `--ad-good` on the element for application styling.

### TypeScript / JavaScript

```ts
// Backend, within the consuming application's settings route.
import { providerPanel } from "@agenticdriver/sdk/panel";
import { providerPanelHtml } from "@agenticdriver/sdk/ui";

const panel = providerPanel({
  client: () => currentUserDriverClient(),
  connection: () => ({
    id: connection.id,
    label: connection.label,
    url: connection.url,
  }),
  connect: async (invitation) => saveConnectionForCurrentUser(invitation),
  disconnect: async () => forgetConnectionForCurrentUser(),
});

// After your existing settings authorization and CSRF check:
const result = await panel(requestBody);
// Respond with result as no-store JSON.
const markup = providerPanelHtml({ apiPath: "/api/settings/driver" });
```

Import `@agenticdriver/sdk/ui` in your browser entry or serve its compiled module at `/assets/agenticdriver-panel.js`. A custom transport can supply existing application headers/IPC:

```ts
import {
  registerProviderPanel,
  type ProviderPanelElement,
} from "@agenticdriver/sdk/ui";
registerProviderPanel();
const element = document.querySelector<ProviderPanelElement>(
  "agenticdriver-providers",
)!;
element.transport = (request) => applicationSettingsRpc(request);
await element.refresh();
```

`serveProviderPanel` from `@agenticdriver/sdk/panel-server` is the ready-to-run local application used by `agenticdriver panel`. See [the executable TypeScript example](https://github.com/agenticdriver/agenticdriver/blob/27f2bb1aa5170a35ca5592d6db1eec2b73971bc9/examples/javascript/provider-panel.mts). Its private profile persists through restarts. Disconnect forgets that profile; revocation is a separate host operation.

### Python

```python
from agenticdriver import ProviderPanel, provider_panel_html, provider_panel_script

panel = ProviderPanel(
    lambda: current_user_driver_client(),
    connection=lambda: {"id": connection.id, "label": connection.label, "url": connection.url},
    connect=save_connection_for_current_user,
    disconnect=forget_connection_for_current_user,
)
html = provider_panel_html(api_path="/api/settings/driver")
script = provider_panel_script()  # bytes; serve as text/javascript
# In the authorized application JSON route:
result = panel.handle(request_json)
```

`AsyncProviderPanel` accepts an `AsyncAgenticClient` getter and async connect/disconnect hooks; use `await panel.handle(request_json)`. Both bridges use the exact same packaged component. No Node runtime is needed in the consuming Python application.

### Go

```go
panel := agenticdriver.ProviderPanel{
    Client: func(ctx context.Context) (*agenticdriver.Client, error) {
        return currentUserDriverClient(ctx)
    },
    Connection: func() *agenticdriver.PanelConnection {
        return &agenticdriver.PanelConnection{ID: connection.ID, Label: connection.Label, URL: connection.URL}
    },
    Connect: saveConnectionForCurrentUser,
    Disconnect: forgetConnectionForCurrentUser,
}
markup, err := agenticdriver.ProviderPanelHTML("/api/settings/driver", "/assets/agenticdriver-panel.js")
// Serve agenticdriver.ProviderPanelScript as text/javascript.
// After application settings authorization and CSRF validation:
result, err := panel.Handle(request.Context(), limitedJSONBody)
```

`ProviderPanelScript` is embedded in the Go module; copying a separate JavaScript build is unnecessary. The bridge preserves empty deny-all model arrays through JSON serialization.

### Rust

```rust
use agenticdriver::{AgenticClient, Result};
use agenticdriver::panel::{PanelBackend, PanelConnection, handle_provider_panel,
    provider_panel_html, PROVIDER_PANEL_SCRIPT};

struct Settings<'a> { client: &'a AgenticClient }
impl PanelBackend for Settings<'_> {
    fn client(&self) -> Option<&AgenticClient> { Some(self.client) }
    fn connection(&self) -> Option<PanelConnection> {
        // Return your application's stable connection ID, label and public host URL.
        None
    }
}
// In the authorized application settings route:
fn route(settings: &mut Settings<'_>, request: &serde_json::Value) -> Result<serde_json::Value> {
    handle_provider_panel(settings, request)
}
// Serve PROVIDER_PANEL_SCRIPT as text/javascript and embed:
// provider_panel_html("/api/settings/driver", "/assets/agenticdriver-panel.js")?
```

Implement `can_connect` / `connect` and `can_disconnect` / `disconnect` on the backend to enable the setup controls. `AsyncPanelBackend`, `panel_snapshot_async` and `handle_provider_panel_async` provide the async equivalents. The browser module is included in the crate with `include_str!`; consumers do not need Node or a frontend build step.

## Connection hooks and identities

Each bridge's optional connect hook receives an `ad1.…` invitation. Parse it with the language client's invitation helper, use its short-lived code only to exchange for a connection credential, and put the resulting credential in the application's existing private secret store. Retain its stable ID, expiry and public URL. Use the new credential for the connected SDK client. Do not automatically retry an uncertain exchange: inspect/revoke its connection on the host first. TypeScript's `connectClient` and `connectedClient` additionally implement the private-file profile used by the standalone panel.

Resolve clients and descriptors for the same application user/request. A stable connection ID isolates device-local preferences; replacing a connection does not carry account preferences into a different identity. Native binary/account settings refer to paths on the connected host. Adding a new provider does not extend existing paired applications' provider grants.

## Logos and provider metadata

The component includes the independent [provider-icons library](/docs/provider-icons),
also used by UsageStat-Bar. Applications need only the icon package to reuse the
artwork; it has no SDK or provider-runtime dependency. Unknown providers use
initials. The panel makes no icon CDN requests.

Applications can still supply reviewed Usagestat metadata/assets. The TypeScript
bridge accepts a `presentations(providers)` callback returning
`providerPresentation()` results keyed by SDK instance ID. Other bridges can
enrich their JSON snapshot with the same `presentations` map. Serve reviewed
custom assets from the application's own origin. These presentations take
precedence unless the user explicitly selects a bundled icon preference.

## Validation

The shared panel is checked in the native T3 browser at desktop and narrow widths. Protocol tests exercise role restrictions, revisions, private profile reload, expired connections, browser origin checks, and credential non-disclosure. Installed language packages exercise the management bridge over real HTTP and certificate-verified HTTPS, preserving unrestricted and empty model lists. All CI runs use Prometheus. Browser inspection and catalog refresh do not spend inference tokens.

## Icons and connection information

Settings uses the shared [provider icon library](/docs/provider-icons), with full
colour/monochrome and product-mark choices stored on this device for each
connection and provider. Icons work offline in every language's bundled panel
and in the desktop Companion. These preferences make no host configuration or
model requests.

The connection card shows the reported CLI version, sign-in method/status and
subscription. Account email/name is masked and visually blurred by default.
**Reveal** displays reported fields; **Hide**, switching providers, refreshing,
disconnecting or reopening the component masks them again. The hidden DOM,
accessibility labels and browser preferences contain no reported identity.
Owned-sign-in confirmation uses the same control. Provider labels and explicit
host account IDs remain configuration labels, separate from native identity.

Absent fields say **Not reported**. Saved sign-in, reachable catalogue,
execution permissions and a successful model run remain distinct; refreshing
this card never invokes a model. See [discovery](/docs/discovery#connection-details)
for the typed metadata contract and runtime limitations.

For visual verification, build the SDK and run `npm run preview --prefix apps/desktop`.
The separate preview profile starts empty. Connect a real account or pair with an
actual remote host; verify reveal/hide, refresh masking and narrow/desktop layouts
using the metadata that host reports. No fake identities or usage are injected.
