Skip to content

feat: browseros API#284

Merged
DaniAkash merged 60 commits intomainfrom
feat/browseros-api
Jan 30, 2026
Merged

feat: browseros API#284
DaniAkash merged 60 commits intomainfrom
feat/browseros-api

Conversation

@DaniAkash
Copy link
Contributor

Integrates browser os to API to sync conversation history

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 30, 2026

Greptile Overview

Greptile Summary

This PR integrates BrowserOS API to enable conversation history sync and user authentication. The implementation adds magic link and Google OAuth login, bidirectional sync for conversations, LLM providers, and scheduled tasks.

Key Changes:

  • Authentication system using better-auth with magic link and Google OAuth
  • GraphQL client infrastructure with typed documents and React Query hooks
  • Real-time conversation sync when logged in, local storage fallback when logged out
  • Bidirectional sync for LLM providers and scheduled jobs with conflict resolution
  • Chat history UI switches between remote and local data based on login status

Issues Found:

  • Performance bottleneck: All sync operations use sequential GraphQL calls in loops instead of parallel execution
  • useConversations hook triggers excessive uploads due to unstable conversations dependency
  • Multiple watchers and sync operations could cause race conditions under rapid changes

The architectural approach is sound - proper separation between local/remote storage, graceful degradation for offline users, and good error handling with Sentry. However, the sequential network operations will cause poor UX for users with many conversations, providers, or scheduled jobs.

Confidence Score: 3/5

  • Safe to merge but will have performance issues for users with large datasets
  • Core functionality is correct and well-structured with proper error handling, but sequential GraphQL operations in loops will cause significant performance degradation for users with many conversations/providers/schedules. The excessive re-render triggering in conversationStorage could also cause unnecessary API load.
  • apps/agent/lib/conversations/uploadConversationsToGraphql.ts, apps/agent/entrypoints/sidepanel/index/useRemoteConversationSave.ts, apps/agent/lib/schedules/syncSchedulesToBackend.ts, apps/agent/lib/llm-providers/uploadLlmProvidersToGraphql.ts - all need parallel execution patterns

Important Files Changed

Filename Overview
apps/agent/lib/graphql/execute.ts creates GraphQL client with error handling and credential inclusion
apps/agent/lib/conversations/uploadConversationsToGraphql.ts uploads local conversations to backend with batching and deduplication
apps/agent/entrypoints/sidepanel/index/useRemoteConversationSave.ts manages real-time conversation sync to backend with message deduplication
apps/agent/entrypoints/sidepanel/index/useChatSession.ts integrates remote conversation saving and restoring for logged-in users
apps/agent/lib/llm-providers/uploadLlmProvidersToGraphql.ts syncs LLM provider configs to backend excluding sensitive fields
apps/agent/lib/schedules/syncSchedulesToBackend.ts bidirectional sync of scheduled jobs with conflict resolution
apps/agent/entrypoints/background/index.ts adds sync setup on session changes for providers and schedules
apps/agent/lib/conversations/conversationStorage.ts adds upload on login but triggers excessively due to unstable dependencies

Sequence Diagram

sequenceDiagram
    participant User
    participant Extension
    participant AuthClient
    participant BrowserOSAPI
    participant SessionStorage
    participant LocalStorage
    
    Note over User,LocalStorage: Authentication Flow
    User->>Extension: Open login page
    Extension->>AuthClient: signIn.magicLink(email)
    AuthClient->>BrowserOSAPI: POST /auth/magic-link
    BrowserOSAPI-->>User: Send magic link email
    User->>BrowserOSAPI: Click magic link
    BrowserOSAPI-->>AuthClient: Set session cookie
    AuthClient->>SessionStorage: Save session info
    Extension->>Extension: Navigate to /home
    
    Note over User,LocalStorage: Conversation Sync Flow
    User->>Extension: Send message
    Extension->>Extension: Generate conversation
    Extension->>SessionStorage: Check login status
    alt User logged in
        Extension->>BrowserOSAPI: POST /graphql (CreateConversation)
        BrowserOSAPI-->>Extension: Conversation created
        Extension->>BrowserOSAPI: POST /graphql (AppendMessage)
        BrowserOSAPI-->>Extension: Message saved
    else User not logged in
        Extension->>LocalStorage: Save conversation locally
    end
    
    Note over User,LocalStorage: Initial Sync on Login
    SessionStorage->>Extension: Watch session change
    Extension->>LocalStorage: Read local conversations
    Extension->>BrowserOSAPI: POST /graphql (GetProfileId)
    BrowserOSAPI-->>Extension: Profile ID
    loop For each conversation
        Extension->>BrowserOSAPI: POST /graphql (ConversationExists)
        BrowserOSAPI-->>Extension: Check result
        alt Conversation exists
            Extension->>BrowserOSAPI: POST /graphql (GetUploadedMessageCount)
            BrowserOSAPI-->>Extension: Message count
        else New conversation
            Extension->>BrowserOSAPI: POST /graphql (CreateConversation)
        end
        Extension->>BrowserOSAPI: POST /graphql (BulkCreateMessages)
        BrowserOSAPI-->>Extension: Messages uploaded
    end
    Extension->>LocalStorage: Remove uploaded conversations
    
    Note over User,LocalStorage: LLM Providers & Schedules Sync
    Extension->>LocalStorage: Watch providers/schedules
    Extension->>BrowserOSAPI: POST /graphql (SyncProviders)
    Extension->>BrowserOSAPI: POST /graphql (SyncSchedules)
    BrowserOSAPI-->>Extension: Sync complete
    
    Note over User,LocalStorage: History Retrieval
    User->>Extension: Open chat history
    Extension->>SessionStorage: Check login status
    alt User logged in
        Extension->>BrowserOSAPI: POST /graphql (GetConversations)
        BrowserOSAPI-->>Extension: Remote conversations
        Extension->>User: Display remote history
    else User not logged in
        Extension->>LocalStorage: Read conversations
        Extension->>User: Display local history
    end
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

6 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@DaniAkash
Copy link
Contributor Author

@greptileai

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

8 files reviewed, 6 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +65 to +120
for (const provider of providers) {
if (provider.type === 'browseros') continue

try {
const remote = remoteProviders.get(provider.id)

if (remote) {
if (isEqual(toComparable(provider), omit(remote, ['rowId']))) continue

await execute(UpdateLlmProviderForUploadDocument, {
input: {
rowId: provider.id,
patch: {
type: provider.type,
name: provider.name,
baseUrl: provider.baseUrl ?? null,
modelId: provider.modelId,
supportsImages: provider.supportsImages,
contextWindow: provider.contextWindow,
temperature: provider.temperature,
resourceName: provider.resourceName ?? null,
region: provider.region ?? null,
updatedAt: new Date(provider.updatedAt).toISOString(),
},
},
})
} else {
await execute(CreateLlmProviderForUploadDocument, {
input: {
llmProvider: {
rowId: provider.id,
profileId,
type: provider.type,
name: provider.name,
baseUrl: provider.baseUrl ?? null,
modelId: provider.modelId,
supportsImages: provider.supportsImages,
contextWindow: provider.contextWindow,
temperature: provider.temperature,
resourceName: provider.resourceName ?? null,
region: provider.region ?? null,
createdAt: new Date(provider.createdAt).toISOString(),
updatedAt: new Date(provider.updatedAt).toISOString(),
},
},
})
}
} catch (error) {
sentry.captureException(error, {
extra: {
providerId: provider.id,
providerName: provider.name,
},
})
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

sequential GraphQL mutations in loop - creates network waterfall

use Promise.allSettled() to execute all provider updates in parallel

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/agent/lib/llm-providers/uploadLlmProvidersToGraphql.ts
Line: 65:120

Comment:
sequential GraphQL mutations in loop - creates network waterfall

use `Promise.allSettled()` to execute all provider updates in parallel

How can I resolve this? If you propose a fix, please make it concise.

@claude
Copy link

claude bot commented Jan 30, 2026

Code Review

I found 3 issues that need attention:

1. Missing useEffect import in AuthProvider.tsx

Location: apps/agent/lib/auth/AuthProvider.tsx:1

useEffect is used on line 9 but not imported, which will cause a runtime error (ReferenceError: useEffect is not defined).

Current:

import type { FC, PropsWithChildren } from 'react'

Fix:

import { useEffect, type FC, type PropsWithChildren } from 'react'

2. Incorrect import path in CLAUDE.md example

Location: apps/agent/CLAUDE.md:76

The import path in the example code is incorrect and will cause a module not found error.

Current:

import { graphql } from '~/graphql/gql'

Should be:

import { graphql } from '@/generated/graphql/gql'

The correct import path is @/generated/graphql/gql as used throughout the codebase. See chatHistoryDocument.ts for reference.


3. Incorrect import path in CLAUDE.md example

Location: apps/agent/CLAUDE.md:80

The import path in the example code is incorrect and will cause a module not found error.

Current:

import { getQueryKeyFromDocument } from '@/modules/graphql/getQueryKeyFromDocument'

Should be:

import { getQueryKeyFromDocument } from '@/lib/graphql/getQueryKeyFromDocument'

The /modules/ directory does not exist in the codebase. The correct path is @/lib/graphql/getQueryKeyFromDocument. See ProfilePage.tsx for correct usage.

@DaniAkash DaniAkash merged commit 8639f2b into main Jan 30, 2026
27 of 28 checks passed
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