Fire Mode: Disable chat sync #8792
Conversation
234ebfa to
e106f3f
Compare
21054fb to
30246c3
Compare
The browser-mode simplification removed JsMessaging.browserMode, but the Duck.ai sync handlers still read it via SyncFireModeGuard, breaking :duckchat-impl compilation (and with it Build, JVM tests, and Lint). Activity-scope the five sync handlers (Encrypt/Decrypt/SetUpSync/ SetAIChatHistoryEnabled/GetSyncStatus) and inject the host activity's frozen BrowserMode, matching the native-storage handler. The guard now operates on BrowserMode (renamed isFireMode() -> isSyncAvailable()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The browser-mode simplification removed JsMessaging.browserMode, but the Duck.ai sync handlers still read it via SyncFireModeGuard, breaking :duckchat-impl compilation (and with it Build, JVM tests, and Lint). Activity-scope the five sync handlers (Encrypt/Decrypt/SetUpSync/ SetAIChatHistoryEnabled/GetSyncStatus) and inject the host activity's frozen BrowserMode, matching the native-storage handler. The guard now operates on BrowserMode (renamed isFireMode() -> isSyncAvailable()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
527eba6 to
4144e90
Compare
30246c3 to
3278b3d
Compare
The browser-mode simplification removed JsMessaging.browserMode, but the Duck.ai sync handlers still read it via SyncFireModeGuard, breaking :duckchat-impl compilation (and with it Build, JVM tests, and Lint). Activity-scope the five sync handlers (Encrypt/Decrypt/SetUpSync/ SetAIChatHistoryEnabled/GetSyncStatus) and inject the host activity's frozen BrowserMode, matching the native-storage handler. The guard now operates on BrowserMode (renamed isFireMode() -> isSyncAvailable()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3278b3d to
8f64f40
Compare
4144e90 to
31870cb
Compare
The browser-mode simplification removed JsMessaging.browserMode, but the Duck.ai sync handlers still read it via SyncFireModeGuard, breaking :duckchat-impl compilation (and with it Build, JVM tests, and Lint). Activity-scope the five sync handlers (Encrypt/Decrypt/SetUpSync/ SetAIChatHistoryEnabled/GetSyncStatus) and inject the host activity's frozen BrowserMode, matching the native-storage handler. The guard now operates on BrowserMode (renamed isFireMode() -> isSyncAvailable()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8f64f40 to
91cc1c8
Compare
31870cb to
bdb5378
Compare
The browser-mode simplification removed JsMessaging.browserMode, but the Duck.ai sync handlers still read it via SyncFireModeGuard, breaking :duckchat-impl compilation (and with it Build, JVM tests, and Lint). Activity-scope the five sync handlers (Encrypt/Decrypt/SetUpSync/ SetAIChatHistoryEnabled/GetSyncStatus) and inject the host activity's frozen BrowserMode, matching the native-storage handler. The guard now operates on BrowserMode (renamed isFireMode() -> isSyncAvailable()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bdb5378 to
cdee100
Compare
91cc1c8 to
a79127d
Compare
The browser-mode simplification removed JsMessaging.browserMode, but the Duck.ai sync handlers still read it via SyncFireModeGuard, breaking :duckchat-impl compilation (and with it Build, JVM tests, and Lint). Activity-scope the five sync handlers (Encrypt/Decrypt/SetUpSync/ SetAIChatHistoryEnabled/GetSyncStatus) and inject the host activity's frozen BrowserMode, matching the native-storage handler. The guard now operates on BrowserMode (renamed isFireMode() -> isSyncAvailable()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cdee100 to
61a4375
Compare
a79127d to
677097c
Compare
The browser-mode simplification removed JsMessaging.browserMode, but the Duck.ai sync handlers still read it via SyncFireModeGuard, breaking :duckchat-impl compilation (and with it Build, JVM tests, and Lint). Activity-scope the five sync handlers (Encrypt/Decrypt/SetUpSync/ SetAIChatHistoryEnabled/GetSyncStatus) and inject the host activity's frozen BrowserMode, matching the native-storage handler. The guard now operates on BrowserMode (renamed isFireMode() -> isSyncAvailable()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
31d8dd2 to
e5cc923
Compare
61a4375 to
7c208af
Compare
The browser-mode simplification removed JsMessaging.browserMode, but the Duck.ai sync handlers still read it via SyncFireModeGuard, breaking :duckchat-impl compilation (and with it Build, JVM tests, and Lint). Activity-scope the five sync handlers (Encrypt/Decrypt/SetUpSync/ SetAIChatHistoryEnabled/GetSyncStatus) and inject the host activity's frozen BrowserMode, matching the native-storage handler. The guard now operates on BrowserMode (renamed isFireMode() -> isSyncAvailable()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7c208af to
b39544d
Compare
e5cc923 to
e19c081
Compare
e19c081 to
9b9a43f
Compare
b39544d to
95870fc
Compare
|
@0nko , what's the plan for the duck chat UI re setting up sync. I can see it in the settings for instance, where it offers to
|
|
@0nko Perhaps we're missing a tech design for how the FE should be handling fire tabs and syncing? I feel like this PR is rather reactive to discovering that fire tabs will need special considerations re sync and returning failures to all the API messages a bit of a blunt instrument. What you've done in this PR is maybe only part of the story but not sufficient and we require other parts in place like the FE updating too. |
Task/Issue URL: https://app.asana.com/1/137249556945/project/1207418217763355/task/1215444465437172?focus=true Tech Design: https://app.asana.com/1/137249556945/project/1207418217763355/task/1214570322752005?focus=true ### Description This PR separates the native Duck.ai chat data layer between **Regular** and **Fire** mode. Fire-mode chats are persisted in their own DB, attachment directory, and WebView profile, so they never appear in Regular chat history. **Deviation from the tech design:** The `JsMessaging` interface change was not needed. The tech design proposed extending the shared `JsMessaging` interface to thread the active `BrowserMode` through to the native-storage message handler so it could select the correct per-mode store. That turned out to be unnecessary: - `DuckAiNativeStorageJsMessageHandler` resolves its backend via `BrowserModeDataProvider<DuckAiBridgeStorage>.forMode(browserMode)`, where `browserMode` is injected directly rather than passed through the JS message - The injected `BrowserMode` is the activity-frozen value (provided once per activity by `provideActivityBrowserMode`, read from `BrowserModeStateHolder.currentMode` at component creation). Because switching mode recreates the activity, the one-mode-per-activity invariant holds and the handler always reads the correct mode's storage - To inject the activity-scoped `BrowserMode`, the native-storage content-scope message handler plugin (`DuckAiNativeStorageJsMessageHandler`) was moved from `@ContributesMultibinding(AppScope::class)` to `@ContributesMultibinding(ActivityScope::class)` because `BrowserMode` is only bound in `ActivityScope`, not `AppScope` **Note:** Sync is handled in a [subsequent PR](#8792). ### Steps to test this PR - [ ] Make sure `fireTabs` FF is enabled in dev settings (requires a restart) - [ ] In **Regular** mode, open Duck.ai and send a message (e.g. `regular-test`); confirm it shows in Duck.ai chat history. - [ ] Switch to **Fire** mode (tab switcher → mode toggle, or the "New Fire Tab" menu item). Open Duck.ai chat history → `regular-test` is **not** listed (Fire history is separate). - [ ] In **Fire** mode, send a message (e.g. `fire-test`); it appears in Fire history. - [ ] Switch back to **Regular** → `fire-test` is **not** listed; only `regular-test` is. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Touches chat persistence, sync eligibility, and migration flags across two databases; incorrect mode routing could leak Fire data into Regular history or sync. > > **Overview** > Introduces **separate native Duck.ai persistence for Regular vs Fire mode**: a second Room DB (`fire_mode_duck_ai.db`), attachment directory, and `@RegularMode` / `@FireMode` qualified DAO bundles behind `DuckAiBridgeStorage`, with `ModeAwareDuckAiChatStore` as the default unqualified store that follows `BrowserModeStateHolder`. > > **WebView / JS bridge:** `DuckAiNativeStorageJsMessageHandler` moves to **ActivityScope**, resolves storage via injected activity-frozen `BrowserMode` + `BrowserModeDataProvider`, and **ignores `markMigrationDone` in Fire** so shared migration flags are not set from ephemeral Fire sessions. > > **Sync & privacy:** `RealDuckChatSyncRepository` **no-ops pending sync metadata writes in Fire**; `DuckChatSyncDataManager` injects **`@RegularMode` `DuckAiChatStore`** so background sync never reads Fire chats. Internal debug server is Regular-only. > > **UI:** `RealNativeInputStateStore` tracks the **selected tab for the current mode** (not hard-coded Regular tabs). Tests updated/added across store, sync, JS handler, and native input. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 9b9a43f. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
95870fc to
4abf6e5
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4abf6e5. Configure here.
|
@CDRussell Thanks for the review!
Yeah, I agree the API responses everywhere might not have been the ideal solution.. I was trying to patch up all the places where I expected that Fire mode might be doing something with sync, so I was going overboard just to make sure nothing leaked. However, after going over the code again I realized that the only guards needed were actually a the write sites where chat IDs are added to the sync queue.
Good catch. I completely missed the fact that sync can be accessed outside the native settings. There's a parameter that tells the FE if the sync is available (already used by the sync FF), so now it's disabled in Fire mode. Ready for another round. |
# Conflicts: # duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/sync/DuckChatSyncRepository.kt
The browser-mode simplification removed JsMessaging.browserMode, but the Duck.ai sync handlers still read it via SyncFireModeGuard, breaking :duckchat-impl compilation (and with it Build, JVM tests, and Lint). Activity-scope the five sync handlers (Encrypt/Decrypt/SetUpSync/ SetAIChatHistoryEnabled/GetSyncStatus) and inject the host activity's frozen BrowserMode, matching the native-storage handler. The guard now operates on BrowserMode (renamed isFireMode() -> isSyncAvailable()). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt # app/src/test/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt # duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/helper/DuckChatJSHelper.kt # duckchat/duckchat-impl/src/test/kotlin/com/duckduckgo/duckchat/impl/helper/RealDuckChatJSHelperTest.kt
706e37c to
69eba25
Compare



Task/Issue URL: https://app.asana.com/1/137249556945/project/1207418217763355/task/1215462571225879?focus=true
Description
Duck.ai chats may sync in Regular mode but must never sync in Fire mode.
A shared rule —
BrowserMode.isSyncablegates the two places sync can start:DuckChatJSHelperreportssupportsAIChatSyncasfalsein Fire mode. The Duck.ai FE reads this capability and doesn't offer or attempt sync (no "Turn On Sync & Backup").browserModeis supplied per-surface by the calling tab / contextual sheet / standalone screen.RealDuckChatSyncRepository's write methods (recordDuckAiChatsDeleted/recordSingleChatDeletion/recordSingleChatUpdate) no-op in a non-syncable mode, so Fire chat IDs never enter the pending sync queues.DuckChatSyncDataManagerreads the@RegularModestore, so a background sync that fires during a Fire session still syncs Regular chats without touching Fire data.Steps to test this PR
Prerequisites:
fireTabsFF enabled in dev settings (needs a restart)Regular mode still syncs (no regression)
Fire mode never syncs
FE doesn't show sync options in Fire mode
UI Changes
Note
Medium Risk
Touches sync metadata writes and JS capability flags for privacy-sensitive Fire mode; scope is narrow and aligned with existing Regular-mode sync paths.
Overview
Prevents Duck.ai chats from syncing when the user is in Fire mode, while keeping sync behavior in Regular mode unchanged.
A shared
BrowserMode.isSyncablerule (Fire → false, Regular → true) gates both entry points:DuckChatJSHelpernow takesbrowserModefrom each Duck.ai surface and only reportssupportsAIChatSyncwhen sync is enabled and the mode is syncable, so the web UI does not offer sync in Fire.RealDuckChatSyncRepositoryuses the same rule on deletion/update queue writers instead of a Fire-only check, so Fire chat activity never enters pending sync metadata.Call sites (
BrowserTabViewModel, contextual fragment, standalone web view) passbrowserModeinto JS processing; tests cover Fire config and the updated mocks.Reviewed by Cursor Bugbot for commit 69eba25. Bugbot is set up for automated code reviews on this repo. Configure here.