[OPIK-5596] [FE] Unify pairing codes between agent sandbox and Ollie sidebar#6143
Merged
[OPIK-5596] [FE] Unify pairing codes between agent sandbox and Ollie sidebar#6143
Conversation
apps/opik-frontend/src/v2/pages/AgentRunnerPage/AgentRunnerContent.test.tsx
Show resolved
Hide resolved
apps/opik-frontend/src/v2/pages/AgentRunnerPage/AgentRunnerEmptyState.tsx
Show resolved
Hide resolved
…sidebar Add bridge events (runner:state-changed, runner:request-pair) so the Opik FE owns pairing/connection state and Ollie receives it via the iframe bridge instead of calling the pairing endpoint independently. - Add RunnerConnectionStatus enum unifying backend and client-only states - Add RunnerBridgeState type and bridge events to assistant-sidebar protocol - Add usePairingState hook centralizing pair code + connection + expiry logic - Add useRunnerBridgeSync hook broadcasting pairing state to bridge listeners - Wire bridge sync into AssistantSidebar with lastRunnerState replay on subscribe - Refactor AgentRunnerContent to consume usePairingState - Refactor AgentRunnerEmptyState to receive expiresAt and show inline spinner - Update useSandboxConnectionStatus to fall back to most recent runner - Add usePairingState unit tests
e7684d0 to
93b938f
Compare
apps/opik-frontend/src/v2/pages/AgentRunnerPage/AgentRunnerEmptyState.tsx
Show resolved
Hide resolved
- Add projectId guard in handleRequestPair to prevent empty project_id POST - Replace pairing.runner! non-null assertion with pairing.runner && guard
aadereiko
approved these changes
Apr 9, 2026
Collaborator
aadereiko
left a comment
There was a problem hiding this comment.
In general, the code looks good, nice! Feel free to merge this PR, and fix the comments in a follow up
Comment on lines
+12
to
19
| export enum RunnerConnectionStatus { | ||
| LOADING = "loading", | ||
| IDLE = "idle", | ||
| PAIRING = "pairing", | ||
| EXPIRED = "expired", | ||
| CONNECTED = "connected", | ||
| DISCONNECTED = "disconnected", | ||
| } |
Collaborator
There was a problem hiding this comment.
Nit: we could break it down into 2 types (server + cline types). and cliemt types will be an extension of server types
| queryKey: [AGENT_SANDBOX_KEY, "pair-code", { projectId }], | ||
| queryFn: ({ signal }) => getPairCode(projectId, signal), | ||
| enabled: !!projectId, | ||
| enabled: false, |
Collaborator
There was a problem hiding this comment.
would move this enabled: false to be passed down from a parent
Comment on lines
+84
to
+98
|
|
||
| let status: RunnerConnectionStatus; | ||
| if (isFetching && !pairCodeData) { | ||
| status = RunnerConnectionStatus.LOADING; | ||
| } else if (isConnected) { | ||
| status = RunnerConnectionStatus.CONNECTED; | ||
| } else if (isDisconnected) { | ||
| status = RunnerConnectionStatus.DISCONNECTED; | ||
| } else if (pairCodeData && !isExpired) { | ||
| status = RunnerConnectionStatus.PAIRING; | ||
| } else if (pairCodeData && isExpired) { | ||
| status = RunnerConnectionStatus.EXPIRED; | ||
| } else { | ||
| status = RunnerConnectionStatus.IDLE; | ||
| } |
Collaborator
There was a problem hiding this comment.
you should have it as useMemo (ot as a function), for readability, i would not have nested else if statmenets just at a component level
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Details
The Agent Sandbox page and the Ollie console sidebar independently generate pairing codes and poll for runner connection status via the same backend endpoints. This causes duplicate codes and unsynchronized connection state between the two UIs.
This PR makes the Opik FE the single owner of pairing/connection state. Two new bridge events (
runner:state-changed,runner:request-pair) let Ollie request and receive pairing state via the iframe bridge instead of calling the backend directly. React Query cache deduplication ensures both UIs share the same code.Key changes:
RunnerConnectionStatusenum unifying backend and client-only connection statesusePairingStatehook centralizing pair code fetching, connection polling, expiry detection, and disconnect handlinguseRunnerBridgeSynchook broadcasting pairing state to bridge listeners and handling Ollie's pairing requestsrunner:state-changed(host→sidebar) andrunner:request-pair(sidebar→host)AgentRunnerEmptyStateshows inline spinner while code is loading instead of full-page loaderuseSandboxConnectionStatusfalls back to most recent runner (enables DISCONNECTED status detection)Companion PR for ollie-assist changes will follow (bridge types + bridge-aware pairing hook).
Change checklist
Issues
AI-WATERMARK
AI-WATERMARK: yes
Testing
npx tsc --noEmit— zero type errorsnpx vitest run src/hooks/usePairingState.test.ts src/v2/pages/AgentRunnerPage/AgentRunnerContent.test.tsx— 15 tests passing/connectin Ollie → same code shown (not a new one)opik connect --pair <code>→ both UIs show connected within secondsDocumentation
N/A — no user-facing API or configuration changes. Bridge protocol changes are internal between Opik FE and Ollie console.