From 34ddeb9789b5e5685c04aabc53d338921df5447f Mon Sep 17 00:00:00 2001 From: Garfie Date: Thu, 26 Mar 2026 16:50:18 -0500 Subject: [PATCH] Add sync-upstream skill and upstream submodule - Add official LibreChat repo as git submodule at upstream/ (pinned to v0.8.2) - Create UPSTREAM_VERSION file to track which release the app targets - Create /sync-upstream skill with Agent Teams workflow - Add reference files for API, model, UI, and architecture mappings - Update CLAUDE.md with upstream sync documentation --- .claude/skills/sync-upstream/SKILL.md | 348 ++++++++++++++++++ .../reference/android-architecture.md | 153 ++++++++ .../sync-upstream/reference/api-mapping.md | 43 +++ .../sync-upstream/reference/model-mapping.md | 65 ++++ .../sync-upstream/reference/ui-mapping.md | 51 +++ .../sync-upstream/reference/upstream-paths.md | 31 ++ .gitmodules | 3 + CLAUDE.md | 7 + UPSTREAM_VERSION | 5 + upstream | 1 + 10 files changed, 707 insertions(+) create mode 100644 .claude/skills/sync-upstream/SKILL.md create mode 100644 .claude/skills/sync-upstream/reference/android-architecture.md create mode 100644 .claude/skills/sync-upstream/reference/api-mapping.md create mode 100644 .claude/skills/sync-upstream/reference/model-mapping.md create mode 100644 .claude/skills/sync-upstream/reference/ui-mapping.md create mode 100644 .claude/skills/sync-upstream/reference/upstream-paths.md create mode 100644 .gitmodules create mode 100644 UPSTREAM_VERSION create mode 160000 upstream diff --git a/.claude/skills/sync-upstream/SKILL.md b/.claude/skills/sync-upstream/SKILL.md new file mode 100644 index 0000000..b7f920d --- /dev/null +++ b/.claude/skills/sync-upstream/SKILL.md @@ -0,0 +1,348 @@ +--- +name: sync-upstream +description: > + Sync the LibreChat Android client with a newer official LibreChat server release. + Diffs upstream tags, identifies gaps in the Android app, proposes changes with + user approval, then implements them. Use when a new LibreChat version is released. +allowed-tools: Bash, Read, Glob, Grep, Write, Edit, Agent, TeamCreate, SendMessage, TaskCreate, TaskUpdate, TaskGet, TaskList +argument-hint: "[target-tag] (optional, defaults to latest stable)" +--- + +# Sync Upstream + +Synchronize the LibreChat Android app with a newer version of the official LibreChat server. +This is a multi-phase, team-based workflow. + +You are the **team lead**. You orchestrate at a high level. You NEVER read code, write code, +or grep the codebase yourself. All heavy lifting is delegated to teammates. + +## Team Setup + +Create an Agent Team with 4 teammates at the start: + +| Teammate | Role | Prompt Summary | +|----------|------|----------------| +| **investigator** | Reads upstream diffs, analyzes API/type/UI changes, cross-references with Android codebase | "You are the investigator for a LibreChat Android upstream sync. Your job is to analyze diffs between upstream tags and identify gaps in the Android client. You have read-only responsibilities — never write code." | +| **android-expert** | Technical advisor on Android architecture and best practices. Cross-checks all guidance with online research before advising. | "You are the Android tech lead for a LibreChat Android upstream sync. You provide guidance on Android best practices, Jetpack Compose patterns, Kotlin idioms, and architectural decisions. CRITICAL: You MUST cross-check your knowledge by researching online (WebSearch/WebFetch) before giving advice. Never rely solely on training data — always verify current best practices, especially for Compose, Hilt, Ktor, and Material 3. Return researched guidance to teammates who ask." | +| **implementer** | Writes code changes to Android app following existing patterns and conventions | "You are the implementer for a LibreChat Android upstream sync. Your job is to write code changes following existing architecture patterns. Always read the module's CLAUDE.md before touching it. Follow safeApiCall, Hilt DI, UDF, Ktor, @Serializable patterns. Before making significant architectural decisions, consult the android-expert teammate." | +| **verifier** | Checks implementation correctness, runs builds, validates changes match proposal | "You are the verifier for a LibreChat Android upstream sync. Your job is to independently validate that implementation matches the approved proposal. Run builds, review changes, and flag issues. Message the implementer directly if fixes are needed." | + +**Teammate interaction pattern:** +- **investigator** works independently on Phase A and B (diff + gap analysis) +- **android-expert** is consulted by the implementer before architectural decisions and by the + lead when the proposal involves patterns that may have better modern alternatives. The + android-expert MUST search online to verify recommendations — do not accept advice that + hasn't been cross-checked against current documentation. +- **implementer** does all code changes, consulting android-expert for non-trivial patterns +- **verifier** validates after implementer finishes, messages implementer for fixes + +**Important:** Teammates retain context across messages. Reuse them for follow-ups — do NOT +spawn new sub-agents (sub-agents lose context). Sub-agents are a last resort fallback only. + +## Prerequisites + +Run these checks yourself (lightweight, no codebase reading): + +### 1. Submodule check +```bash +cd LibreChat-Android && git submodule status +``` +If `upstream/` is missing: +```bash +git submodule add https://github.com/danny-avila/LibreChat.git upstream +git submodule update --init +``` + +### 2. Version file check +```bash +cat UPSTREAM_VERSION +``` +If missing, create from current state: +```bash +VERSION=$(grep 'SUPPORTED_BACKEND_VERSION' core/common/src/main/kotlin/com/librechat/android/core/common/BackendVersion.kt | grep -o '"[^"]*"' | tr -d '"') +COMMIT=$(cd upstream && git rev-parse HEAD) +echo "# Upstream LibreChat version this Android build tracks." > UPSTREAM_VERSION +echo "# Updated by the sync-upstream skill. Do not edit manually." >> UPSTREAM_VERSION +echo "tag=v${VERSION}" >> UPSTREAM_VERSION +echo "commit=${COMMIT}" >> UPSTREAM_VERSION +echo "date=$(date +%Y-%m-%d)" >> UPSTREAM_VERSION +``` + +### 3. Clean working tree +```bash +git status --porcelain +``` +If dirty, ask the user to commit or stash before proceeding. + +### 4. Context recovery (new session detection) + +Check if a prior sync was partially done in another session: +```bash +# Get version from tracking file +TRACKED_TAG=$(grep '^tag=' UPSTREAM_VERSION | cut -d= -f2) + +# Get version from BackendVersion.kt +CODE_VERSION=$(grep 'SUPPORTED_BACKEND_VERSION' core/common/src/main/kotlin/com/librechat/android/core/common/BackendVersion.kt | grep -o '"[^"]*"' | tr -d '"') + +# Get submodule current position +SUBMODULE_HEAD=$(cd upstream && git rev-parse HEAD) +TRACKED_COMMIT=$(grep '^commit=' UPSTREAM_VERSION | cut -d= -f2) +``` + +If `TRACKED_TAG` (without `v` prefix) != `CODE_VERSION`, or `SUBMODULE_HEAD` != `TRACKED_COMMIT`: +> "It looks like a sync may have been partially done. UPSTREAM_VERSION says {TRACKED_TAG} +> but BackendVersion.kt says {CODE_VERSION}. Should I treat the higher version as the +> current baseline, or would you like to investigate?" + +Wait for user response before continuing. + +--- + +## Phase A: Diff Analysis + +**Delegate to: investigator** + +### A1. Assign investigation task + +Create a task for the investigator with this prompt: + +> Analyze upstream changes for a version sync. +> +> 1. Read `UPSTREAM_VERSION` at the repo root to get the current tracked tag and commit. +> 2. Fetch latest tags: `cd upstream && git fetch --tags origin` +> 3. List all tags newer than the current tracked tag: +> `cd upstream && git tag --sort=-v:refname` +> Filter OUT any tags containing `-rc`, `-beta`, `-alpha`. +> 4. Generate diffs between the current tag and {target_tag} for these paths +> (read `${CLAUDE_SKILL_DIR}/reference/upstream-paths.md` for the full list): +> - `api/server/routes/` +> - `api/server/controllers/` +> - `packages/data-provider/src/` +> - `packages/data-schemas/src/` +> - `client/src/components/` +> - `client/src/hooks/` +> - `client/src/store/` +> 5. Categorize all changes into: +> - **API changes**: New/modified/removed routes, controller logic +> - **Type/schema changes**: New/modified data types, request/response shapes +> - **UI changes**: New components, modified user flows +> - **Bug fixes**: Fixes that may need Android equivalents +> - **Infrastructure**: Build, config, deps (usually not relevant) +> 6. Report back a structured summary table. + +If `$ARGUMENTS` was provided, use it as the target tag. + +### A2. Present results to user + +Once investigator reports back: +- If no newer tags exist, tell user "Already up to date with {current_tag}" and stop. +- If no `$ARGUMENTS`, present the list of available tags and ask user to pick one (default: latest stable). +- Show the investigator's categorized summary. + +**Done when:** User has seen the diff summary and confirmed the target tag. + +--- + +## Phase B: Gap Analysis + +**Delegate to: investigator** (reuses context from Phase A — do NOT spawn a new agent) + +### B1. Send follow-up to investigator + +> Now cross-reference the upstream changes you found with the Android codebase. +> +> 1. Read these mapping files from `${CLAUDE_SKILL_DIR}/reference/`: +> - `api-mapping.md` — official routes to Android *Api.kt files +> - `model-mapping.md` — official TS types to Kotlin data classes +> - `ui-mapping.md` — web components to Android feature modules +> 2. For each API change from Phase A: +> - Grep the Android codebase (`core/network/src/`) for the corresponding endpoint +> - Note if it exists, needs updating, or is missing +> 3. For each type/schema change: +> - Check `core/model/src/` for matching Kotlin data class +> - Note new fields, removed fields, type changes +> 4. For each UI change: +> - Check the corresponding `feature/*/` module +> - Note if the feature exists, needs updating, or is missing +> 5. Categorize all gaps: +> - **Breaking** (must fix): Changed request/response shapes, removed/renamed endpoints, auth changes +> - **Additive** (new feature): New endpoints, new UI features, new optional fields +> - **Cosmetic** (polish): UI improvements, a11y, i18n +> 6. Report the categorized gap list with specific file paths for both upstream and Android. + +### B2. Receive gap report + +The investigator now has full context of both the upstream diff AND the Android gaps. +Keep the investigator alive for follow-up questions during Phase C. + +**Done when:** You have the categorized gap list from the investigator. + +--- + +## Phase C: Proposal + +**Lead presents to user — no delegation needed.** + +Take the investigator's gap report and present it as a structured proposal: + +``` +## Sync Proposal: {current_tag} → {target_tag} + +### Breaking Changes ({count}) — must fix before updating version +For each: +- What changed upstream (with file reference) +- What Android file(s) need updating +- Proposed change + +### New Features ({count}) — recommended +For each: +- What was added upstream +- Proposed Android implementation approach +- Which module(s) would be affected + +### UI Changes Needing User Input ({count}) +For each: +- What the web app does (with file reference) +- Why it doesn't translate directly to Android/Compose +- 2-3 concrete options for the user to choose from + +### Deferred Items ({count}) — can wait for a future sync +Items that are low priority or require significant new infrastructure. +``` + +### Consult android-expert on non-trivial items + +Before presenting to the user, for any proposed change that involves: +- New architectural patterns (e.g., adding a new module, new DI scope) +- UI patterns that don't have an existing equivalent in the codebase +- Performance-sensitive changes (e.g., new list rendering, image loading) +- Navigation changes + +Send these items to the **android-expert** teammate: + +> Review these proposed Android changes for best practices. For each item, +> research online to verify the recommended approach is current (especially +> for Compose, Material 3, Hilt, and Ktor). Flag any items where modern +> best practices differ from what we're proposing. +> {list of non-trivial items} + +Incorporate the android-expert's feedback into the proposal before showing the user. + +### Approval gate + +Ask the user: + +> Review the proposal above. Reply with: +> - **approve all** — proceed with full implementation +> - **approve with changes** — tell me what to modify in the proposal +> - **approve partial** — list which items to implement now (rest deferred) +> - **cancel** — abort, no changes made + +**Do NOT proceed to Phase D until the user explicitly approves.** + +If the user requests changes, update the proposal and re-present. + +--- + +## Phase D: Implementation + +**Delegate to: implementer, then verifier** + +### D1. Assign implementation task + +Create a task for the implementer with the approved change list: + +> Implement the following approved changes for the upstream sync from {current_tag} to {target_tag}. +> +> **Before touching any module:** +> 1. Read the root `CLAUDE.md` for architecture rules +> 2. Read `${CLAUDE_SKILL_DIR}/reference/android-architecture.md` for patterns +> 3. Read the specific module's `CLAUDE.md` (e.g., `core/network/CLAUDE.md`) +> 4. Read existing similar code as a pattern reference +> +> **Conventions to follow:** +> - `safeApiCall` for all network calls in repositories +> - Hilt `@Inject` for dependency injection +> - Unidirectional data flow: UI → ViewModel → Repository → API/Room +> - `@Serializable` data classes in `core/model/` +> - Ktor client patterns in `core/network/` +> +> **Before making non-trivial architectural decisions** (new patterns, new modules, +> complex UI components), message the **android-expert** teammate for guidance. +> The android-expert will research current best practices online and advise. +> +> **Approved changes:** +> {paste the approved change list here} +> +> **After all code changes:** +> 1. Update `SUPPORTED_BACKEND_VERSION` in `core/common/src/main/kotlin/com/librechat/android/core/common/BackendVersion.kt` to "{new_version}" (without v prefix) +> 2. Advance submodule: `cd upstream && git checkout {target_tag}` +> 3. Update `UPSTREAM_VERSION` at repo root: +> ``` +> tag={target_tag} +> commit={full SHA of target_tag} +> date={today's date YYYY-MM-DD} +> ``` +> 4. Report back the full list of files you created or modified. + +### D2. Assign verification task + +After implementer reports done, create a task for the verifier: + +> Verify the upstream sync implementation from {current_tag} to {target_tag}. +> +> 1. Run `./gradlew assembleDebug` to verify the project compiles +> 2. Review all files the implementer changed against the approved proposal: +> - Are all approved changes implemented? +> - Are there any changes that weren't in the proposal? +> - Do the changes follow existing code patterns? +> 3. Check version consistency: +> - `SUPPORTED_BACKEND_VERSION` in BackendVersion.kt matches target +> - `UPSTREAM_VERSION` file has correct tag, commit, date +> - Submodule is at the correct tag: `cd upstream && git describe --tags` +> 4. If you find issues, message the **implementer** directly to fix them. +> The implementer retains context of what it changed. +> 5. Report back: +> - Build status (pass/fail + any errors) +> - Issues found (if any) +> - List of user-testable behaviors to verify on device + +### D3. Present results to user + +Once verifier confirms everything passes: + +``` +## Sync Complete: {current_tag} → {target_tag} + +### Files Changed +{grouped by module} + +### What to Test +{specific user-facing behaviors from verifier} + +### How to Test +1. Build: `./gradlew assembleDebug` +2. Install on device/emulator +3. Test each behavior listed above + +### Version Updates +- SUPPORTED_BACKEND_VERSION: {old} → {new} +- UPSTREAM_VERSION tag: {old_tag} → {new_tag} +- Submodule: pinned to {target_tag} +``` + +--- + +## Error Recovery + +| Situation | Recovery | +|-----------|----------| +| Submodule missing | `git submodule add https://github.com/danny-avila/LibreChat.git upstream` | +| `UPSTREAM_VERSION` missing | Create from `SUPPORTED_BACKEND_VERSION` + submodule HEAD | +| No newer tags | Report "up to date" and stop | +| Dirty working tree | Ask user to commit/stash | +| Git fetch fails | Show `git remote -v`, check network | +| Build fails after changes | Verifier messages implementer to fix (both retain context) | +| User cancels at Phase C | Stop cleanly, no changes made | +| Version mismatch | Ask user which is the true baseline | +| Teammate unresponsive | Check task status, re-send message; sub-agent as last resort | diff --git a/.claude/skills/sync-upstream/reference/android-architecture.md b/.claude/skills/sync-upstream/reference/android-architecture.md new file mode 100644 index 0000000..10829ff --- /dev/null +++ b/.claude/skills/sync-upstream/reference/android-architecture.md @@ -0,0 +1,153 @@ +# Android Architecture Reference + +Condensed architecture guide for the LibreChat Android app. Read module-specific `CLAUDE.md` files for full details. + +## Module Dependency Graph + +``` +app +├── feature/auth → core/common, core/model, core/network, core/data, core/ui +├── feature/chat → core/common, core/model, core/network, core/data, core/ui +├── feature/conversations → core/common, core/model, core/data, core/ui +├── feature/settings → core/common, core/model, core/data, core/ui +├── feature/agents → core/common, core/model, core/data, core/ui +├── feature/files → core/common, core/model, core/data, core/ui +└── core/data → core/common, core/model, core/network + core/network → core/common, core/model + core/ui → core/common, core/model + core/model → (none — pure Kotlin) + core/common → (none — lowest layer) +``` + +**Rules:** Feature modules depend on `:core:*` only, never on each other. + +## Data Flow Pattern (Unidirectional) + +``` +UI (Composable) → ViewModel (StateFlow) → Repository (interface) → API (Ktor) / Room (cache) +``` + +- ViewModels expose `StateFlow` to Compose +- Repositories define interfaces in `:core:data`, implemented with `@Inject` +- Room is a read-through cache; server is always source of truth + +## safeApiCall Pattern + +All repository network calls use this wrapper (defined in `core/common`): + +```kotlin +suspend fun safeApiCall(block: suspend () -> T): Result = + try { Result.Success(block()) } + catch (e: ClientRequestException) { Result.Error(e, parseErrorMessage(e.response.bodyAsText())) } + catch (e: ServerResponseException) { Result.Error(e, "Server error.") } + catch (e: HttpRequestTimeoutException) { Result.Error(e, "Request timed out.") } + catch (e: IOException) { Result.Error(e, "Network unavailable.") } + catch (e: SerializationException) { Result.Error(e, "Unexpected response format.") } +``` + +## API Service Pattern (core/network) + +Each API service is a Ktor-backed class with `@Inject constructor(client: HttpClient)`: + +```kotlin +class ConversationsApi @Inject constructor(private val client: HttpClient) { + suspend fun getConversations(pageNumber: Int, limit: Int, isArchived: Boolean): PaginatedConversations = + client.get("/api/convos") { + parameter("pageNumber", pageNumber.toString()) + parameter("limit", limit.toString()) + parameter("isArchived", isArchived.toString()) + }.body() +} +``` + +**Backend quirk:** Mutation endpoints wrap body in `arg` field: `{ "arg": { ... } }` + +## Repository Pattern (core/data) + +Interface + implementation, always with Hilt injection: + +```kotlin +interface ConversationRepository { + fun observeConversations(...): Flow>> + suspend fun updateTitle(id: String, title: String): Result +} + +class ConversationRepositoryImpl @Inject constructor( + private val api: ConversationsApi, + private val dao: ConversationDao, + private val mapper: EntityMapper, + @Dispatcher(IO) private val dispatcher: CoroutineDispatcher, +) : ConversationRepository { ... } +``` + +## Room Entity Pattern (core/data) + +```kotlin +@Entity(tableName = "conversations") +data class ConversationEntity( + @PrimaryKey val conversationId: String, + val title: String?, + val endpoint: String?, + // Complex fields stored as JSON strings via TypeConverters + val tagsJson: String?, +) +``` + +DAO read methods return `Flow` for reactive observation. + +## ViewModel Pattern (feature modules) + +```kotlin +@HiltViewModel +class ConversationsViewModel @Inject constructor( + private val conversationRepository: ConversationRepository, +) : ViewModel() { + + private val _uiState = MutableStateFlow(ConversationsUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + fun loadConversations() { + viewModelScope.launch { + conversationRepository.observeConversations(...) + .collect { result -> + _uiState.update { state -> + when (result) { + is Result.Success -> state.copy(conversations = result.data) + is Result.Error -> state.copy(error = result.message) + is Result.Loading -> state.copy(isLoading = true) + } + } + } + } + } +} +``` + +## SSE Streaming (core/network) + +Custom line parser over raw `ByteReadChannel` (do NOT use Ktor SSE plugin). + +Two-phase protocol: +1. `POST /api/agents/chat` → returns `{ streamId }` (streamId === conversationId) +2. `GET /api/agents/chat/stream/:streamId` opens SSE stream + +`SseConnectionManager` handles lifecycle: start, reconnect (exponential backoff), abort, `StateFlow`. + +## Naming Conventions + +- API classes: `{Domain}Api.kt` (e.g., `ConversationsApi.kt`) +- Repository: `{Domain}Repository.kt` (interface) + `{Domain}RepositoryImpl.kt` +- ViewModel: `{Feature}ViewModel.kt` +- Screen composable: `{Feature}Screen.kt` +- UI state: `{Feature}UiState` data class +- Room entity: `{Domain}Entity.kt` +- Room DAO: `{Domain}Dao.kt` +- DI module: `{Domain}Module.kt` +- Convention plugins: `librechat.android.{type}` (e.g., `librechat.android.feature`) + +## Build System + +- Gradle 8.11.1, AGP 8.7.3, Kotlin 2.1.0, compileSdk 35, minSdk 26 +- 7 convention plugins in `build-logic/convention/` +- Version catalog: `gradle/libs.versions.toml` +- Convention plugin `apply()` must use `override fun apply(target: Project) { with(target) { ... } }` diff --git a/.claude/skills/sync-upstream/reference/api-mapping.md b/.claude/skills/sync-upstream/reference/api-mapping.md new file mode 100644 index 0000000..50a162e --- /dev/null +++ b/.claude/skills/sync-upstream/reference/api-mapping.md @@ -0,0 +1,43 @@ +# API Route Mapping: Official → Android + +Maps official LibreChat API route files to their Android `*Api.kt` counterparts. + +## Implemented + +| Official Route | Android API File | Notes | +|---------------|-----------------|-------| +| `routes/auth.js` | `AuthApi.kt` | Login, register, refresh, logout, 2FA | +| `routes/oauth.js` | `AuthApi.kt` | OAuth flows (shared file) | +| `routes/config.js` | `ConfigApi.kt` | Startup config, version check | +| `routes/convos.js` | `ConversationsApi.kt` | CRUD, archive, share, fork, duplicate | +| `routes/messages.js` | `MessagesApi.kt` | Get messages by conversation | +| `routes/search.js` | `SearchApi.kt` | Conversation search | +| `routes/tags.js` | `TagsApi.kt` | Conversation tags/bookmarks | +| `routes/presets.js` | `PresetsApi.kt` | Save/load presets | +| `routes/prompts.js` | `PromptsApi.kt` | Prompts library | +| `routes/agents/` (dir) | `AgentsApi.kt` | Agent CRUD, marketplace | +| `routes/files/` (dir) | `FilesApi.kt`, `FilesExtApi.kt` | Upload, list, delete, external files | +| `routes/share.js` | `ShareApi.kt` | Shared conversation links | +| `routes/user.js` | `UserApi.kt` | Profile, delete account | +| `routes/balance.js` | `BalanceApi.kt` | Token balance check | +| `routes/banner.js` | `BannerApi.kt` | System banners | +| `routes/keys.js` | `KeysApi.kt` | User API keys | +| `routes/mcp.js` | `McpApi.kt` | MCP server management | +| `routes/memories.js` | `MemoriesApi.kt` | Memory/context management | +| N/A | `ChatApi.kt` | SSE streaming (POST + GET, not a standard route) | +| N/A | `SpeechApi.kt` | Text-to-speech | +| N/A | `ApiKeysApi.kt` | API key management | + +## Gaps (no Android counterpart yet) + +| Official Route | What It Does | Priority | +|---------------|-------------|----------| +| `routes/categories.js` | Agent categories | Low — categories embedded in agent responses | +| `routes/endpoints.js` | List available endpoints | Medium — Android uses config response instead | +| `routes/models.js` | List available models per endpoint | Medium — Android uses config response instead | +| `routes/roles.js` | Role-based access control (admin) | Low — admin feature | +| `routes/settings.js` | User settings CRUD | Medium — Android uses local DataStore | +| `routes/actions.js` | Custom actions/plugins | Low — plugin system | +| `routes/assistants/` (dir) | OpenAI Assistants API | Low — agents supersede assistants | +| `routes/accessPermissions.js` | File access permissions | Low — admin feature | +| `routes/static.js` | Static file serving | N/A — not applicable to mobile | diff --git a/.claude/skills/sync-upstream/reference/model-mapping.md b/.claude/skills/sync-upstream/reference/model-mapping.md new file mode 100644 index 0000000..d0caea4 --- /dev/null +++ b/.claude/skills/sync-upstream/reference/model-mapping.md @@ -0,0 +1,65 @@ +# Model Mapping: Official TypeScript → Android Kotlin + +Maps official TypeScript types from `packages/data-provider/src/` to Android Kotlin data classes in `core/model/`. + +## Core Models (Implemented) + +| TypeScript Type | Kotlin Class | File | Notes | +|----------------|-------------|------|-------| +| `TMessage` | `Message` | `Message.kt` | Includes sender, text, children, content parts | +| `TConversation` | `Conversation` | `Conversation.kt` | Core conversation metadata | +| `TConversationTag` | `ConversationTag` | `ConversationTag.kt` | Tag/bookmark on conversations | +| `TPreset` | `Preset` | `Preset.kt` | Saved endpoint configurations | +| `TPromptGroup` / `TPrompt` | `Prompt` | `Prompt.kt` | Prompts library entries | +| `TSharedLink` | `SharedLink` | `SharedLink.kt` | Shared conversation links | +| `TStartupConfig` | `StartupConfig` | `StartupConfig.kt` | `/api/config` response | +| `TEndpointConfig` | `EndpointConfig` | `EndpointConfig.kt` | Per-endpoint configuration | +| `EModelEndpoint` | `Endpoint` | `Endpoint.kt` | Enum of supported endpoints | +| `TUser` | `User` | `User.kt` | User profile data | +| `TBalance` | `Balance` | `Balance.kt` | Token balance | +| `TBanner` | `Banner` | `Banner.kt` | System banner messages | +| `TFile` | `FileModel` | `FileModel.kt` | Uploaded file metadata | +| `Agent` | `Agent` | `Agent.kt` | Agent definition | +| `AgentExpanded` | `AgentExpanded` | `AgentExpanded.kt` | Agent with full details | +| `PaginatedAgents` | `PaginatedAgents` | `PaginatedAgents.kt` | Paginated agent list response | +| `AgentCategory` | `AgentCategory` | `AgentCategory.kt` | Agent marketplace categories | +| `TMemory` | `Memory` | `Memory.kt` | Memory/context entries | +| `TUserKey` | `UserKey` | `UserKey.kt` | User API keys | +| `ApiKey` | `ApiKey` | `ApiKey.kt` | API key management | + +## SSE/Streaming Models (Android-specific) + +| Kotlin Class | File | Purpose | +|-------------|------|---------| +| `StreamEvent` | `StreamEvent.kt` | Sealed class for SSE event types | +| `SseContentEvent` | `SseContentEvent.kt` | Content delta events during streaming | +| `ToolCallRecord` | `ToolCallRecord.kt` | Tool call progress tracking | +| `ToolCallResult` | `ToolCallResult.kt` | Tool call completion data | +| `ToolAuthStatus` | `ToolAuthStatus.kt` | Tool authentication state | + +## Android-Only Models + +| Kotlin Class | File | Purpose | +|-------------|------|---------| +| `ServerConnection` | `ServerConnection.kt` | Saved server URL + metadata | +| `LoginOutcome` | `LoginOutcome.kt` | Auth flow result sealed class | +| `ConversationExport` | `ConversationExport.kt` | Export/import format | +| `Feedback` | `Feedback.kt` | Message feedback (thumbs up/down) | +| `SupportContact` | `SupportContact.kt` | Help/support info | +| `MessageContentPart` | `MessageContentPart.kt` | Rich content (text, image, file) | +| `ParameterDefinition` | `ParameterDefinition.kt` | Model parameter config | +| `AgentAction` | `AgentAction.kt` | Agent action definitions | +| `AgentTool` | `AgentTool.kt` | Agent tool definitions | + +## Key Type Sources in Official Repo + +| File | Contains | +|------|----------| +| `packages/data-provider/src/types.ts` | Main type exports | +| `packages/data-provider/src/types/queries.ts` | React Query response types | +| `packages/data-provider/src/types/mutations.ts` | Mutation request/response types | +| `packages/data-provider/src/types/agents.ts` | Agent-specific types | +| `packages/data-provider/src/types/files.ts` | File-related types | +| `packages/data-provider/src/types/runs.ts` | Run/streaming types | +| `packages/data-provider/src/schemas.ts` | Zod validation schemas | +| `packages/data-provider/src/config.ts` | Config types and VERSION constant | diff --git a/.claude/skills/sync-upstream/reference/ui-mapping.md b/.claude/skills/sync-upstream/reference/ui-mapping.md new file mode 100644 index 0000000..2d1cfa4 --- /dev/null +++ b/.claude/skills/sync-upstream/reference/ui-mapping.md @@ -0,0 +1,51 @@ +# UI Mapping: Official React Components → Android Feature Modules + +Maps web client component directories to Android Compose feature modules. + +## Implemented + +| Web Component Dir | Android Module | Coverage | +|-------------------|---------------|----------| +| `components/Chat/` | `feature/chat/` | Core chat UI, message list, input | +| `components/Messages/` | `feature/chat/` | Message rendering, branching, siblings | +| `components/Input/` | `feature/chat/` | Message input, attachments, mentions | +| `components/Conversations/` | `feature/conversations/` | List, search, tags, CRUD | +| `components/Auth/` | `feature/auth/` | Login, register, 2FA, forgot password | +| `components/Nav/` | `app/` (navigation) | Side nav, navigation rail, adaptive layout | +| `components/Files/` | `feature/files/` | Upload, list, delete, image viewer | +| `components/Agents/` | `feature/agents/` | Marketplace grid, detail screen | +| `components/Banners/` | `app/` | System banner display | +| `components/Endpoints/` | `feature/chat/` | Model/endpoint selector bottom sheet | + +## Gaps (no Android counterpart) + +| Web Component Dir | What It Does | Priority | Notes | +|-------------------|-------------|----------|-------| +| `components/Prompts/` | Prompts library with CRUD, categories, sharing | Medium | Android has PromptsApi but no dedicated UI | +| `components/Artifacts/` | Code/document artifact viewer and editor | Medium | New feature in recent versions | +| `components/MCP/` | MCP server configuration UI | Low | Admin-oriented feature | +| `components/MCPUIResource/` | MCP UI resource rendering | Low | Depends on MCP support | +| `components/Bookmarks/` | Conversation bookmarks management | Low | Tags partially cover this | +| `components/Share/` | Share conversation dialog and link management | Low | ShareApi exists, no UI | +| `components/Sharing/` | Shared conversation viewer | Low | Public link viewer | +| `components/SharePoint/` | SharePoint integration | N/A | Enterprise feature | +| `components/SidePanel/` | Side panel for artifacts, files, etc. | Medium | Tablet layout opportunity | +| `components/Audio/` | Voice input/output, TTS | Medium | SpeechApi exists, no UI | +| `components/Plugins/` | Legacy plugin system | N/A | Superseded by agents | +| `components/Tools/` | Tool configuration and management | Low | Part of agent setup | +| `components/OAuth/` | OAuth provider buttons | Low | Android uses Chrome Custom Tabs | +| `components/Web/` | Web search integration | Low | Agent tool feature | +| `components/System/` | System-level UI (error boundaries, etc.) | Low | Android has own error handling | + +## Key Web Patterns → Android Equivalents + +| Web Pattern | Android Equivalent | +|------------|-------------------| +| Recoil atoms (`store/`) | ViewModel state (StateFlow) | +| React Query hooks (`hooks/`) | Repository + ViewModel | +| React Context (`Providers/`) | Hilt DI + CompositionLocal | +| Radix UI components | Material 3 components | +| Tailwind CSS | Material 3 theme + Modifier | +| react-router-dom | Compose Navigation | +| React.memo / useMemo | remember / derivedStateOf | +| useCallback | rememberUpdatedState / LaunchedEffect | diff --git a/.claude/skills/sync-upstream/reference/upstream-paths.md b/.claude/skills/sync-upstream/reference/upstream-paths.md new file mode 100644 index 0000000..b95ec03 --- /dev/null +++ b/.claude/skills/sync-upstream/reference/upstream-paths.md @@ -0,0 +1,31 @@ +# Upstream Paths to Watch + +These are the directories in the `upstream/` submodule that matter for Android parity. +Use these paths when generating focused diffs between tags. + +| Path | What It Contains | Why It Matters | +|------|-----------------|----------------| +| `api/server/routes/` | Express route definitions (REST endpoints) | Defines the API contract the Android app calls | +| `api/server/controllers/` | Request handlers and business logic | Shows request/response shapes and validation | +| `packages/data-provider/src/` | Shared types, schemas, API client, config | Canonical type definitions; Android models must match | +| `packages/data-provider/src/types/` | TypeScript type subdirectory (queries, mutations, agents, etc.) | React Query hook types that define API shapes | +| `packages/data-schemas/src/` | Mongoose schemas and DB models | Database schema changes that affect API responses | +| `client/src/components/` | React UI components by feature area | Reference for Android UI feature parity | +| `client/src/hooks/` | Custom React hooks (data fetching, state) | Shows how the web app consumes APIs — informs ViewModel design | +| `client/src/store/` | Recoil/Jotai state atoms | Global state patterns — informs Android state management | +| `client/src/Providers/` | React Context providers | Feature flags, config-driven UI — informs Android feature gating | + +## Diff Command Template + +```bash +cd upstream && git diff {old_tag}..{new_tag} --stat -- \ + api/server/routes/ \ + api/server/controllers/ \ + packages/data-provider/src/ \ + packages/data-schemas/src/ \ + client/src/components/ \ + client/src/hooks/ \ + client/src/store/ +``` + +For detailed diffs, run each path separately to keep output manageable. diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a74ab00 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "upstream"] + path = upstream + url = https://github.com/danny-avila/LibreChat.git diff --git a/CLAUDE.md b/CLAUDE.md index 5f0c82d..e3cb454 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,4 +47,11 @@ Each module has its own `CLAUDE.md` with specific guidance. - ua-parser-js middleware rejects non-browser User-Agents with 403 (workaround: Chrome UA string) - Refresh token sent via request body (not HTTP-only cookies) +## Upstream Sync + +- **`upstream/`** — Git submodule of the [official LibreChat repo](https://github.com/danny-avila/LibreChat). Read-only reference for API and web app parity. Do not modify. +- **`UPSTREAM_VERSION`** — Tracks which official tag/commit this Android build is based on. Updated by the `/sync-upstream` skill. +- **`BackendVersion.kt`** (`core/common/`) — `SUPPORTED_BACKEND_VERSION` constant must match the tag in `UPSTREAM_VERSION` (without `v` prefix). +- **`/sync-upstream`** — Claude Code skill to diff upstream releases, identify gaps, propose changes, and implement them with user approval. Uses Agent Teams (investigator, android-expert, implementer, verifier). + @~/.claude/librechat-android-local.md diff --git a/UPSTREAM_VERSION b/UPSTREAM_VERSION new file mode 100644 index 0000000..c15f0b9 --- /dev/null +++ b/UPSTREAM_VERSION @@ -0,0 +1,5 @@ +# Upstream LibreChat version this Android build tracks. +# Updated by the sync-upstream skill. Do not edit manually. +tag=v0.8.4 +commit=0736ff26686e911c9785a237c63a799db1813f0b +date=2026-03-26 diff --git a/upstream b/upstream new file mode 160000 index 0000000..0736ff2 --- /dev/null +++ b/upstream @@ -0,0 +1 @@ +Subproject commit 0736ff26686e911c9785a237c63a799db1813f0b