Skip to content

[OPIK-5596] [FE] Unify pairing codes between agent sandbox and Ollie sidebar#6143

Merged
miguelgrc merged 2 commits intomainfrom
miguelg/OPIK-5596-unify-pairing-codes
Apr 9, 2026
Merged

[OPIK-5596] [FE] Unify pairing codes between agent sandbox and Ollie sidebar#6143
miguelgrc merged 2 commits intomainfrom
miguelg/OPIK-5596-unify-pairing-codes

Conversation

@miguelgrc
Copy link
Copy Markdown
Contributor

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:

  • RunnerConnectionStatus enum unifying backend and client-only connection states
  • usePairingState hook centralizing pair code fetching, connection polling, expiry detection, and disconnect handling
  • useRunnerBridgeSync hook broadcasting pairing state to bridge listeners and handling Ollie's pairing requests
  • Bridge protocol extended with runner:state-changed (host→sidebar) and runner:request-pair (sidebar→host)
  • AgentRunnerEmptyState shows inline spinner while code is loading instead of full-page loader
  • useSandboxConnectionStatus falls 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

  • User facing
  • Documentation update

Issues

  • OPIK-5596

AI-WATERMARK

AI-WATERMARK: yes

  • If yes:
    • Tools: Claude Code (CLI)
    • Model(s): Claude Opus 4.6
    • Scope: Full implementation with human review and iterative refinement
    • Human verification: Extensive manual testing of all pairing flows, code review of every file

Testing

  • npx tsc --noEmit — zero type errors
  • npx vitest run src/hooks/usePairingState.test.ts src/v2/pages/AgentRunnerPage/AgentRunnerContent.test.tsx — 15 tests passing
  • Manual testing:
    • Navigate to Agent Sandbox → code generated, countdown displayed
    • Type /connect in Ollie → same code shown (not a new one)
    • Generate code in Ollie first, then navigate to Agent Sandbox → same code
    • Connect runner via opik connect --pair <code> → both UIs show connected within seconds
    • Disconnect runner → sandbox auto-generates fresh code, Ollie shows disconnected
    • Navigate between project pages → state persists via React Query cache
    • Ollie without bridge (standalone) → falls back to direct API calls

Documentation

N/A — no user-facing API or configuration changes. Bridge protocol changes are internal between Opik FE and Ollie console.

@miguelgrc miguelgrc requested a review from a team as a code owner April 8, 2026 18:30
…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
@miguelgrc miguelgrc force-pushed the miguelg/OPIK-5596-unify-pairing-codes branch from e7684d0 to 93b938f Compare April 8, 2026 18:46
- Add projectId guard in handleRequestPair to prevent empty project_id POST
- Replace pairing.runner! non-null assertion with pairing.runner && guard
Copy link
Copy Markdown
Collaborator

@aadereiko aadereiko left a comment

Choose a reason for hiding this comment

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

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",
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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;
}
Copy link
Copy Markdown
Collaborator

@aadereiko aadereiko Apr 9, 2026

Choose a reason for hiding this comment

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

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

@miguelgrc miguelgrc merged commit e14f9c1 into main Apr 9, 2026
10 checks passed
@miguelgrc miguelgrc deleted the miguelg/OPIK-5596-unify-pairing-codes branch April 9, 2026 14:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants