Date: 2026-05-20 Status: Active Related:
../platform/components/ambient-api-server/openapi/— API contract../platform/components/ambient-cli/cmd/acpctl/login/authcode.go— OAuth reference implementation
Chrome browser extension (Manifest V3) that provides a persistent side panel for monitoring and interacting with Ambient Code Platform agentic sessions. Targets power users (PMs, data scientists, ops engineers) who need session visibility and chat access without the full web UI.
The extension connects to the ACP API v1 (/api/ambient/v1/), authenticates via Red Hat SSO OIDC with PKCE, and provides: session list with lifecycle controls, real-time chat with streaming, workspace management, push notifications, and an in-extension help chatbot.
No build step. No framework. Vanilla JS with Chrome Extensions APIs.
The extension SHALL authenticate users via the OIDC authorization code flow with PKCE, using chrome.identity.launchWebAuthFlow to handle the browser redirect.
| Parameter | Value |
|---|---|
| Issuer | https://sso.redhat.com/auth/realms/redhat-external (configurable) |
| Client ID | ocm-cli |
| Grant type | authorization_code |
| PKCE method | S256 |
| Redirect URI | chrome.identity.getRedirectURL() |
- GIVEN the user has entered a valid server URL
- WHEN the user clicks "Login with Red Hat SSO"
- THEN the extension opens the SSO login page via
launchWebAuthFlow - AND exchanges the authorization code for tokens
- AND stores
access_token,refresh_token, andexpires_atin chrome.storage.local - AND proceeds to workspace selection
- GIVEN the SSO login popup is open
- WHEN the user closes the popup without completing login
- THEN the extension shows an error message on the wizard
- AND no tokens are stored
The extension SHALL support manual token paste as an alternative to OAuth when the redirect URI is not registered with the SSO provider.
- GIVEN the user has entered a server URL and pasted a token
- WHEN the user clicks "Use Token"
- THEN the extension validates the token by calling
GET /api/ambient/v1/projects - AND on success, stores the token with a 24-hour expiry
- AND proceeds to workspace selection
- GIVEN the user has pasted an invalid or expired token
- WHEN the extension validates against the API
- THEN the API returns 401
- AND the extension clears the stored token
- AND shows "Token rejected by server" error
The extension SHALL refresh access tokens before expiry using the stored refresh token.
- GIVEN a stored token with less than 60 seconds until expiry
- AND a valid refresh token exists
- WHEN any API request is made
- THEN the extension refreshes the token before making the request
- AND updates stored tokens with new values
- GIVEN a stored refresh token that the SSO server rejects
- WHEN a refresh is attempted
- THEN the extension clears all stored tokens
- AND broadcasts
AUTH_EXPIREDto all extension pages - AND stops all polling and SSE connections
- AND the side panel shows the login wizard
Every API request SHALL include:
| Header | Value | Required |
|---|---|---|
Authorization |
Bearer {access_token} |
Always |
X-Ambient-Project |
{project_name} |
Always (may be empty during setup) |
User-Agent |
acp-browser-extension/{version} |
Always |
Content-Type |
application/json |
On POST/PATCH/PUT |
Accept |
application/json or text/event-stream |
Always |
On receiving a 401 response, the extension SHALL attempt one token refresh and retry the request. If the retry also returns 401, the extension SHALL broadcast AUTH_EXPIRED and stop all background activity.
- GIVEN a valid refresh token
- WHEN an API request returns 401
- THEN the extension refreshes the token
- AND retries the request with the new token
- AND the request succeeds
- GIVEN no valid refresh token (or refresh fails)
- WHEN an API request returns 401
- AND the retry also returns 401
- THEN the extension broadcasts
AUTH_EXPIRED - AND stops polling and all SSE connections
- AND the side panel shows the login wizard
All SSE connections SHALL use fetch + ReadableStream (not EventSource) to support custom authorization headers.
SSE reconnection SHALL use exponential backoff: 1s → 2s → 4s → ... → 30s max. Backoff SHALL reset on successful connection. SSE connections SHALL NOT reconnect on authentication errors.
- GIVEN an active SSE connection to a running session
- WHEN the connection drops due to a network error
- THEN the extension schedules a reconnection with exponential backoff
- AND resumes from the last known sequence number
- GIVEN an SSE connection attempt fails with 401
- THEN the extension does NOT schedule a reconnection
- AND stops all polling and SSE connections
The extension SHALL poll GET /api/ambient/v1/sessions?size=100 at regular intervals and display sessions for the active workspace.
| Interval | Condition |
|---|---|
| 15 seconds | All sessions in stable phases |
| 3 seconds | Any session in Creating, Pending, or Stopping phase |
The extension SHALL only broadcast SESSIONS_UPDATED when session data has actually changed (comparison guard).
Concurrent poll execution SHALL be prevented with a mutex flag.
- GIVEN the user is authenticated and has selected a workspace
- WHEN the side panel is open
- THEN the extension displays all sessions for the workspace
- AND each session shows: name, phase badge, model, creation time, prompt preview
- GIVEN a session transitions from Stopped to Creating
- WHEN the next poll detects the transition
- THEN the polling interval switches to 3 seconds for 30 seconds
- AND reverts to 15 seconds afterward
The extension SHALL provide action buttons based on session phase:
| Phase | Available Actions |
|---|---|
| Running | Chat, Stop |
| Creating, Pending | (no actions — transitional) |
| Stopped, Completed, Failed | Start, Delete |
| Stopping | (no actions — transitional) |
- GIVEN a running session
- WHEN the user clicks Stop
- THEN the Stop button is replaced with "Stop?" (danger) and "No" (cancel) buttons
- AND clicking "Stop?" calls
POST /sessions/{id}/stop - AND clicking "No" restores the original Stop button
- GIVEN a stopped session
- WHEN the user clicks Delete
- THEN the Delete and Start buttons are replaced with "Delete?" and "No"
- AND clicking "Delete?" calls
DELETE /sessions/{id} - AND the session is removed from the list
The extension SHALL provide a form to create new sessions with fields:
| Field | Type | Required | Notes |
|---|---|---|---|
| Session Name | text | Yes | |
| Initial Prompt | textarea | No | |
| Repository URL | text + datalist | No | Autocomplete from history (max 20 entries) |
| Model | select dropdown | No | Options: Claude Sonnet 4.6, Claude Opus 4.6, Claude Haiku 4.5 |
- GIVEN the user has previously created sessions with repository URLs
- WHEN the user opens the Create Session form
- THEN the Repository URL field offers autocomplete suggestions from stored history
The extension SHALL load message history via GET /api/ambient/v1/sessions/{id}/messages?after_seq=0.
Message types and their rendering:
| event_type | Rendering |
|---|---|
| user | Right-aligned blue bubble |
| assistant | Left-aligned gray bubble |
| tool_use | Bordered block with tool name and arguments |
| tool_result | Bordered block with "Result" label |
| error | Error-styled bubble |
| system | Muted-styled bubble |
- GIVEN a running session with existing messages
- WHEN the user opens the chat view
- THEN the extension loads messages via REST API
- AND renders them in chronological order
- AND connects an SSE stream for new messages
- AND the chat input is active
After loading history, the extension SHALL connect to GET /api/ambient/v1/sessions/{id}/messages?after_seq=N with Accept: text/event-stream for real-time message delivery.
- GIVEN the user is in the chat view of a running session
- WHEN the user sends a message
- THEN the user message appears immediately (optimistic rendering)
- AND the extension calls
POST /api/ambient/v1/sessions/{id}/messages - AND the assistant response streams in via SSE
- AND new messages render incrementally as SSE events arrive
- Enter key sends the message (Shift+Enter does not)
- Chat input SHALL remain fixed at the bottom of the panel
- Auto-scroll SHALL only trigger when the user is within 100px of the bottom
The extension SHALL provide a project dropdown in the toolbar for quick workspace switching.
- GIVEN the user has multiple workspaces
- WHEN the user selects a different workspace from the toolbar dropdown
- THEN the extension updates the stored project name
- AND refreshes the session list for the new workspace
- AND shows a success toast
GET /api/ambient/v1/projects— list workspacesPOST /api/ambient/v1/projects— create workspaceDELETE /api/ambient/v1/projects/{id}— delete workspace (with inline confirmation)
- GIVEN only one workspace exists
- WHEN the user deletes it
- THEN the extension clears the stored project name
- AND returns to the setup wizard
The background service worker SHALL monitor SSE event streams from running sessions and create notifications for:
| Event | Notification Kind | Trigger Pattern |
|---|---|---|
| AskUserQuestion tool call | input_needed |
TOOL_CALL_START with tool name matching `/ask.*user |
| Run completed | run_finished |
RUN_FINISHED event |
| Run error | error |
RUN_ERROR event |
Notifications SHALL be stored in chrome.storage.local, capped at 50 entries (oldest evicted first).
Unread notification count SHALL display on the extension badge (red background).
A persistent bar at the top of the side panel showing connection state:
| Element | Behavior |
|---|---|
| Status dot | Green (connected), yellow (connecting), red (error), gray (disconnected) |
| Cluster name | Extracted from server URL. Click → copy full URL to clipboard |
| Server version | Shows server:unknown until API version endpoint exists. Click → copy URL |
| Extension version | Shows extension:v{version}. Click → open GitHub repository |
Cluster name extraction: For ROSA URLs (apps.rosa.{cluster}.xxx), extract the cluster segment. For other URLs, use the first hostname segment.
Two-step wizard with navigation controls on both steps:
| Step | Content | Navigation |
|---|---|---|
| Step 1: Connect | Server URL input (with history autocomplete), SSO login button, OR manual token paste | Reset button (✕) clears all credentials |
| Step 2: Workspace | Workspace dropdown, Go button, create new workspace form | Back button (←) returns to step 1 and clears auth |
- GIVEN the user is on the workspace selection step
- WHEN the user clicks the back button
- THEN stored tokens are cleared
- AND the wizard returns to step 1
The extension SHALL support dark and light themes via CSS custom properties. Light theme SHALL be the default. Theme selection SHALL persist in chrome.storage.local.
The extension SHALL provide a help panel (accessible via "?" button) with:
- A chat interface connected to a help agent session
- Static resource links (documentation, issues, community)
- Optimistic UI: the panel opens immediately with buffered input while the help session starts
- GIVEN no help session exists for the current workspace
- WHEN the user types a question and sends it
- THEN the user message appears immediately
- AND the extension creates a help session with a comprehensive ACP knowledge prompt
- AND buffers the message until the session is ready
- AND sends the message when the session starts
- AND connects SSE for streaming the response
The service worker SHALL:
- Import
utils.js,oauth.js,api-client.jsviaimportScripts - On activation, check authentication and project, start polling if both present
- Register
chrome.action.onClickedto toggle the side panel
The service worker SHALL handle these message types from extension pages:
| Message Type | Action | Response |
|---|---|---|
OAUTH_LOGIN |
Run OAuth flow | { ok, result? , error? } |
OAUTH_LOGOUT |
Clear tokens, stop polling | { ok } |
GET_NOTIFICATIONS |
Read from storage | { notifications } |
MARK_ALL_READ |
Set all read, clear badge | { ok } |
REFRESH_SESSIONS |
Trigger immediate poll | { ok } |
SESSION_TRANSITIONING |
Switch to fast polling for 30s | { ok } |
The service worker SHALL broadcast these messages to extension pages:
| Message Type | When |
|---|---|
SESSIONS_UPDATED |
Session data changed after poll |
SSE_EVENT |
SSE event received from a running session |
AUTH_EXPIRED |
Authentication failed and cannot be recovered |
NOTIFICATION_ADDED |
New notification created |
| Permission | Purpose |
|---|---|
sidePanel |
Side panel UI |
storage |
Persistent local storage for tokens, config, cache |
identity |
OAuth launchWebAuthFlow |
activeTab |
Side panel open from action click |
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src https: http://localhost:*
https://*/* and http://localhost/* — required to connect to any ACP server instance.
The extension SHALL be testable via Cypress or Playwright against a running ACP instance.
- GIVEN a running ACP server and valid credentials
- WHEN the user completes the login wizard
- THEN the session list loads and displays sessions for the selected workspace
- GIVEN the user is authenticated and has selected a workspace
- WHEN the user creates a session with a name and prompt
- THEN the session appears in the list
- AND the user can open the chat view
- AND send a message and receive a response
- GIVEN a running session
- WHEN the user stops it
- THEN the phase changes to Stopped
- AND the Start and Delete buttons appear
- AND the user can restart or delete the session
- GIVEN the user has access to multiple workspaces
- WHEN the user selects a different workspace from the toolbar dropdown
- THEN the session list updates to show sessions from the new workspace
- GIVEN a stored token that has expired
- WHEN the extension attempts an API call
- THEN the wizard login screen is shown
- AND the user can re-authenticate and resume
Core modules SHALL be testable in isolation without Chrome APIs:
| Module | Testable Without Chrome | Test Strategy |
|---|---|---|
api-client.js (apiRequest, parseSSEStream) |
Yes (mock fetch) | Verify URL construction, header injection, error handling, SSE parsing |
oauth.js (base64urlEncode, PKCE generation) |
Partially (crypto.subtle available in Node) | Verify PKCE challenge derivation, state generation |
utils.js (escHtml, timeAgo) |
Yes | Verify escaping, time formatting edge cases |
help-prompt.js |
Yes (static string) | Verify prompt contains key sections |
The extension SHALL provide visible connection health via the title bar status dot:
| State | Color | Meaning |
|---|---|---|
| Connected | Green | Last poll succeeded |
| Connecting | Yellow | Poll in progress or initial load |
| Error | Red | Auth expired or API unreachable |
| Disconnected | Gray | No credentials or not configured |
All API errors SHALL surface to the user via toast notifications with the error reason from the API response. The extension SHALL NOT silently swallow errors that affect user-visible state.
- GIVEN the user performs an action (create session, start, stop, delete)
- WHEN the API returns a non-2xx response
- THEN the extension shows an error toast with the
reasonfield from the API error body - AND the UI remains in a consistent state (no half-applied changes)
The extension badge SHALL display the count of unread notifications. The badge SHALL clear when all notifications are marked as read.
The extension SHALL log to the browser console (visible via DevTools) at these levels:
| Level | What |
|---|---|
console.warn |
Poll failures, SSE connection errors, SSE reconnection attempts |
console.error |
Should not be used for expected errors (auth expiry, network issues) |
| Silent (no log) | Successful operations, normal polling, broadcast failures when no listener exists |
The extension SHALL NOT flood the console — failed SSE reconnections on auth errors SHALL stop reconnecting rather than logging repeatedly.
- The side panel SHALL be inspectable via right-click → Inspect
- The service worker SHALL be inspectable via
chrome://extensions→ "service worker" link - API requests SHALL be visible in the Network tab of the appropriate DevTools window
- SSE streams SHALL be visible as long-lived requests in the Network tab
- GIVEN the user opens the side panel DevTools Network tab
- WHEN an API call fails
- THEN the request URL, headers, response status, and error body are visible in the Network tab
- AND a
console.warnentry appears in the Console tab
- All requirements are testable (Given/When/Then scenarios)
- No implementation details in requirements (tech-agnostic where possible)
- User scenarios cover primary workflows
- Success criteria are measurable
- Data model captures all entities
- External interfaces are documented
- Known gaps from previous implementation are addressed
- Testing strategy defined (E2E + unit)
- Observability requirements specified (connection health, error surfacing, badge)
- Logging and debugging requirements specified