Duck.ai Contextual: Multiple page contents#7814
Conversation
aitorvs
left a comment
There was a problem hiding this comment.
Reviewing the multiple page contents feature — a few things to address before this is ready to merge.
duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/RealDuckChat.kt
Outdated
Show resolved
Hide resolved
...src/main/java/com/duckduckgo/duckchat/impl/messaging/DuckChatContentScopeJsMessageHandler.kt
Outdated
Show resolved
Hide resolved
...uckchat-impl/src/test/kotlin/com/duckduckgo/duckchat/impl/helper/RealDuckChatJSHelperTest.kt
Show resolved
Hide resolved
...l/src/test/kotlin/com/duckduckgo/duckchat/impl/contextual/DuckChatContextualViewModelTest.kt
Show resolved
Hide resolved
duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/feature/DuckChatFeature.kt
Outdated
Show resolved
Hide resolved
malmstein
left a comment
There was a problem hiding this comment.
Replying inline to review feedback.
duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/RealDuckChat.kt
Outdated
Show resolved
Hide resolved
...src/main/java/com/duckduckgo/duckchat/impl/messaging/DuckChatContentScopeJsMessageHandler.kt
Outdated
Show resolved
Hide resolved
...uckchat-impl/src/test/kotlin/com/duckduckgo/duckchat/impl/helper/RealDuckChatJSHelperTest.kt
Show resolved
Hide resolved
...l/src/test/kotlin/com/duckduckgo/duckchat/impl/contextual/DuckChatContextualViewModelTest.kt
Show resolved
Hide resolved
duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/feature/DuckChatFeature.kt
Outdated
Show resolved
Hide resolved
|
Thanks again for the review, Aitor! I’ve addressed your feedback; could you please take another look when you get a chance? |
d0e764c to
08d8391
Compare
991bb2a to
00a31f3
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Bugbot Autofix prepared fixes for all 3 issues found in the latest run.
- ✅ Fixed: Dead code: safe parsing result silently discarded
- Removed the dead safe-parsing chain and redundant JSONObject creation by using the safe-parsed result directly with
.also{}for logging, matching the pattern ingenerateContextPrompt().
- Removed the dead safe-parsing chain and redundant JSONObject creation by using the safe-parsed result directly with
- ✅ Fixed: Missing mode check for multiple context config value
- Added
mode == Mode.CONTEXTUALguard toSUPPORTS_MULTIPLE_PAGE_CONTEXTto match the siblingSUPPORTS_PAGE_CONTEXTcheck and prevent inconsistent capability advertisement inMode.FULL.
- Added
- ✅ Fixed: Test fake returns wrong flow for native input
- Changed
observeNativeInputFieldUserSettingEnabled()to returnnativeInputFieldUserSettingEnabledinstead of the incorrectinputScreenUserSettingEnabledflow.
- Changed
Or push these changes by commenting:
@cursor push 1c7b0bea75
Preview (1c7b0bea75)
diff --git a/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/contextual/DuckChatContextualViewModel.kt b/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/contextual/DuckChatContextualViewModel.kt
--- a/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/contextual/DuckChatContextualViewModel.kt
+++ b/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/contextual/DuckChatContextualViewModel.kt
@@ -319,15 +319,14 @@
updatedPageContext
.takeIf { it.isNotBlank() }
?.let { runCatching { JSONObject(it) }.getOrNull() }
+ ?.also { json ->
+ val url = json.optString("url")
+ logcat { "Duck.ai: generatePageContextEventData for url $url" }
+ }
?: run {
logcat { "Duck.ai: no pageContext available" }
null
}
-
- val json = JSONObject(updatedPageContext)
- val url = json.optString("url")
- logcat { "Duck.ai: generatePageContextEventData for url $url" }
- json
} else {
logcat { "Duck.ai: pageContext is not valid" }
null
diff --git a/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/helper/DuckChatJSHelper.kt b/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/helper/DuckChatJSHelper.kt
--- a/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/helper/DuckChatJSHelper.kt
+++ b/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/helper/DuckChatJSHelper.kt
@@ -251,7 +251,8 @@
put(
SUPPORTS_MULTIPLE_PAGE_CONTEXT,
duckChat.isDuckChatContextualModeEnabled() &&
- duckChat.areMultipleContentAttachmentsEnabled(),
+ duckChat.areMultipleContentAttachmentsEnabled() &&
+ mode == Mode.CONTEXTUAL,
)
}.also { logcat { "DuckChat-Sync: getAIChatNativeConfigValues $it" } }
return JsCallbackData(jsonPayload, featureName, method, id)
diff --git a/duckchat/duckchat-impl/src/test/kotlin/com/duckduckgo/duckchat/impl/messaging/fakes/FakeDuckChatInternal.kt b/duckchat/duckchat-impl/src/test/kotlin/com/duckduckgo/duckchat/impl/messaging/fakes/FakeDuckChatInternal.kt
--- a/duckchat/duckchat-impl/src/test/kotlin/com/duckduckgo/duckchat/impl/messaging/fakes/FakeDuckChatInternal.kt
+++ b/duckchat/duckchat-impl/src/test/kotlin/com/duckduckgo/duckchat/impl/messaging/fakes/FakeDuckChatInternal.kt
@@ -78,7 +78,7 @@
override fun observeCosmeticInputScreenUserSettingEnabled(): Flow<Boolean?> = cosmeticInputScreenUserSettingEnabled
override fun observeAutomaticContextAttachmentUserSettingEnabled(): Flow<Boolean> = automaticContextAttachmentUserSettingEnabled
- override fun observeNativeInputFieldUserSettingEnabled(): Flow<Boolean> = inputScreenUserSettingEnabled
+ override fun observeNativeInputFieldUserSettingEnabled(): Flow<Boolean> = nativeInputFieldUserSettingEnabled
override suspend fun isStandaloneMigrationCompleted(): Boolean = standaloneMigrationCompleted.value| } else { | ||
| logcat { "Duck.ai: pageContext is not valid" } | ||
| null | ||
| } |
There was a problem hiding this comment.
Dead code: safe parsing result silently discarded
Medium Severity
In generatePageContextEventData(), the takeIf/let/runCatching/getOrNull chain (lines 319–325) evaluates to a JSONObject? but its result is discarded — it's not the last expression in the if block. Instead, the block always evaluates to json from val json = JSONObject(updatedPageContext) on line 327, which unconditionally creates a new JSONObject without the safe runCatching protection. Compare with the identical pattern in generateContextPrompt() where the chain's result IS used as the pageContext value. This appears to be a copy-paste error where extra lines were added after the chain, making the safe-parsing logic dead code and the pageContext variable never null in the valid branch.
Please tell me if this was useful or not with a 👍 or 👎.
| SUPPORTS_MULTIPLE_PAGE_CONTEXT, | ||
| duckChat.isDuckChatContextualModeEnabled() && | ||
| duckChat.areMultipleContentAttachmentsEnabled(), | ||
| ) |
There was a problem hiding this comment.
Missing mode check for multiple context config value
Medium Severity
SUPPORTS_MULTIPLE_PAGE_CONTEXT is missing the mode == Mode.CONTEXTUAL check that its sibling SUPPORTS_PAGE_CONTEXT includes. This means in Mode.FULL, supportsPageContext will be false but supportsMultipleContexts could be true — an inconsistent capability advertisement to the JS frontend. The condition likely needs && mode == Mode.CONTEXTUAL to match the pattern used by SUPPORTS_PAGE_CONTEXT on the preceding line.
Please tell me if this was useful or not with a 👍 or 👎.
| override fun observeCosmeticInputScreenUserSettingEnabled(): Flow<Boolean?> = cosmeticInputScreenUserSettingEnabled | ||
| override fun observeAutomaticContextAttachmentUserSettingEnabled(): Flow<Boolean> = automaticContextAttachmentUserSettingEnabled | ||
| override fun observeNativeInputFieldUserSettingEnabled(): Flow<Boolean> = nativeInputFieldUserSettingEnabled | ||
| override fun observeNativeInputFieldUserSettingEnabled(): Flow<Boolean> = inputScreenUserSettingEnabled |
There was a problem hiding this comment.
Test fake returns wrong flow for native input
Low Severity
observeNativeInputFieldUserSettingEnabled() now returns inputScreenUserSettingEnabled instead of nativeInputFieldUserSettingEnabled. These are two separate MutableStateFlow fields in the fake. This means calling setNativeInputFieldUserSetting() won't affect the flow returned by observeNativeInputFieldUserSettingEnabled(), and tests relying on this fake could silently produce incorrect results.
Please tell me if this was useful or not with a 👍 or 👎.
| params = params, | ||
| ) | ||
| } else { | ||
| val params = |
There was a problem hiding this comment.
we do this to show the “Attach Page Context” button again. Ideally the message should be explicit, like “showAttachPageContextCTA” but the FE decided to send an empty pageContext for this 🤷




Task/Issue URL: https://app.asana.com/1/137249556945/project/1174433894299346/task/1213030322231769?focus=true
Description
This PR enables multiple page content attachments to Duck.ai
Steps to test this PR
Automatic Page Page Content Enabled
Automatic Page Page Content Disabled
Note
Medium Risk
Changes Duck.ai contextual page-context collection/attachment flow and JS messaging, which could affect when/what page data is sent to the chat and introduce edge cases around sheet lifecycle timing. Feature behavior is gated via new config flags but touches core browser↔webview integration paths.
Overview
Enables multiple page content attachments for Duck.ai contextual mode by introducing a new capability toggle (
supportsMultipleContexts) and exposing it to the web client viagetAIChatNativeConfigValues.Updates the contextual sheet lifecycle to request/collect page context on open/reopen and after the chat finishes loading, routing requests through
DuckChatContextualSharedViewModeland moving collection initiation toBrowserTabViewModel.collectPageContext.Enhances page-context payloads by optionally embedding the page favicon as base64 (new
Bitmap.encodeBitmapToBase64extension) and, when in webview mode with auto-attach or multi-attach enabled, emitssubmitAIChatPageContextsubscription events to attach refreshed context to an existing chat. Tests are expanded/updated for the renamedPageContextJSHelper.collectPageContext, the new toggle, and the new attachment/event behavior.Written by Cursor Bugbot for commit 00a31f3. This will update automatically on new commits. Configure here.