diff --git a/design-qa-agent-management-card-title.md b/design-qa-agent-management-card-title.md new file mode 100644 index 00000000..71f17812 --- /dev/null +++ b/design-qa-agent-management-card-title.md @@ -0,0 +1,61 @@ +# Agent Management Card Title Design QA + +**Source visual truth path** + +`/var/folders/0l/4dc990md3yn_g3b46dtmhp880000gn/T/orca-paste-1786355815911-c1768617-d9d5-4b7c-bc02-17762b0964a4.png` + +**Implementation screenshot paths** + +- Memory and Workspace: `/var/folders/0l/4dc990md3yn_g3b46dtmhp880000gn/T/orca-computer-use/8319c5ab-6c27-406a-b73d-46545f9b40f0-screenshot.png` +- Usage: `/var/folders/0l/4dc990md3yn_g3b46dtmhp880000gn/T/orca-computer-use/4239e809-2f34-4949-be50-d3d161209a31-screenshot.png` + +**Viewport and normalization** + +- Source: 1496 x 1036 pixels, cropped to the Settings content region; density metadata is unavailable. +- Implementation: 2796 x 1818 pixels at 2x, representing a 1398 x 909 CSS-pixel Dao Debug window. +- State: light theme, `dao://settings/agent`, Data and management section with loaded metrics. +- The source is a content crop rather than a full browser viewport. The comparison therefore uses the visible card-header region for alignment and hierarchy, not pixel-perfect browser-frame geometry. + +**Full-view comparison evidence** + +The source and both implementation screenshots were opened together in one comparison input. The implementation preserves the independent Memory, Workspace, and Usage cards, existing row density, action surfaces, card spacing, borders, radii, and shadows. Browser chrome and sidebar are expected implementation-only context. + +**Focused region comparison evidence** + +Focused comparison was required because the requested change concerns the compact 52px card headers. Memory, Workspace, and Usage each show the title visually centered between the header boundaries. Each title uses the same 14px semibold hierarchy, 3px Dao-blue inline-start border, and 10px text inset. No title is clipped, wrapped, or offset toward the top edge. + +**Required fidelity surfaces** + +- Fonts and typography: the system font remains unchanged; the titles are consistently 14px, weight 650, and visually stronger than metric labels without competing with the section heading. +- Spacing and layout rhythm: vertical centering is corrected; the existing 52px header, card insets, 16px desktop gap, row heights, radii, and shadows remain visually stable. +- Colors and visual tokens: the accent border uses the existing Dao Settings blue; text and surfaces retain their existing light-theme tokens. Dark-theme behavior is token-backed but was not separately captured. +- Image quality and asset fidelity: no image, icon, logo, illustration, or generated asset is part of this change. +- Copy and content: Memory, Workspace, and Usage copy and all metrics/actions remain unchanged. + +**Findings** + +- No actionable P0, P1, or P2 mismatch remains for the requested card-title treatment. +- P3 verification gap: a separate dark-theme screenshot was not captured; the implementation reuses the existing accent and text tokens rather than adding theme-specific colors. + +**Primary interactions checked** + +- The page loads at `dao://settings/agent` in the freshly rebuilt Dao Debug app. +- Scrolling exposes all three management cards without changing their layout. +- Existing action rows remain visible and aligned; no action was invoked because this change is presentation-only. + +**Comparison history** + +- Iteration 1: the source showed titles sitting too close to the top of their headers and lacking a card identity marker. +- Fix: shared header alignment changed to vertical center; shared titles gained 14px semibold text and a Dao-blue inline-start border. +- Post-fix evidence: the Memory and Workspace screenshot confirms the shared treatment in adjacent cards; the Usage screenshot confirms the third card receives the same treatment. + +**Implementation checklist** + +- [x] Vertically center all three titles in the existing 52px headers. +- [x] Preserve horizontal left alignment and content inset. +- [x] Apply the same 3px accent border, 14px type, 650 weight, and 10px inset to all three titles. +- [x] Preserve card content, actions, semantics, responsive spacing, and localization. +- [x] Import and rebuild successfully through the approved project commands. +- [x] Compare the supplied screenshot and freshly rendered implementation together. + +final result: passed diff --git a/docs/feature-checklist.md b/docs/feature-checklist.md index 44f01b79..0b1f6f50 100644 --- a/docs/feature-checklist.md +++ b/docs/feature-checklist.md @@ -102,8 +102,8 @@ Flagship feature. C++ services + `dao://dao-agent` WebUI + vendor runtime. |---|---------|--------------------|------|----------------------| | ☐ | 4 keyed-service factories registered (memory, skill, workspace, dream) | `profiles/chrome_browser_main_extra_parts_profiles.cc.patch` | 🟡 | Services instantiate per profile; agent features work | | ☐ | Agent WebUI host allowed to make network requests (LLM API) | `webui/chrome_web_ui_controller_factory.cc.patch` (`origin.host()=="agent"`) | 🟢 | `dao://agent` reaches external LLM endpoints | -| ☐ | Unified Profile-scoped Agent settings and legacy migration | `src/dao/.../agent/dao_agent_settings_handler.{h,cc}`, `resources/agent/agent_settings_{sync,native_bridge}.ts`, `resources/settings/dao_page/dao_page.{html,ts}.patch`, `webui/settings/settings_ui.cc.patch` | 🟡 | Run `agent_settings_sync.test.ts`, `dao_agent_app.test.ts`, `DaoPage`, and `DaoAgentSettingsHandlerTest`; verify legacy `dao://agent` local-storage values migrate once without overwriting Settings values, partial usage dictionaries receive validated defaults and derived totals, malformed/non-finite fields fail closed, canonical stored snapshots still require the complete schema, both WebUIs receive `dao-agent-settings-changed`, rapid tool toggles serialize cumulative disabled-tool arrays and resync after failure, the resume window defaults to 3 and accepts 0, runtime-only state is not migrated, and the Agent gear opens `dao://settings/#dao` without rendering a duplicate settings view | -| ☐ | Settings Agent management summaries and limited native facade | `src/dao/.../agent/dao_agent_settings_handler.{h,cc}`, `resources/settings/dao_page/dao_page.{html,ts}.patch`, `resources/agent/agent_settings_native_bridge.ts` | 🟡 | Verify legacy `dao_agent_stats` migration preserves existing counters once; a Settings-side reset updates an already-open Agent WebUI; Memory clear requires confirmation and refreshes aggregate counts; Workspace reveal works without registering workspace mutation messages; configuration loading/error/retry is independent, including when `getSettings()` fails; all three management cards remain visible, usable, and independently loadable in light and dark themes; and the existing Skills, Memory, and Dream secondary links still open | +| ☐ | Unified Profile-scoped Agent settings and legacy migration | `src/dao/.../agent/dao_agent_settings_handler.{h,cc}`, `resources/agent/agent_settings_{sync,native_bridge}.ts`, `resources/settings/dao_page/dao_agent_page*.patch`, `webui/settings/settings_ui.cc.patch` | 🟡 | Run `agent_settings_sync.test.ts`, `dao_agent_app.test.ts`, `DaoAgentPage`, and `DaoAgentSettingsHandlerTest`; verify legacy `dao://agent` local-storage values migrate once without overwriting Settings values, partial usage dictionaries receive validated defaults and derived totals, malformed/non-finite fields fail closed, canonical stored snapshots still require the complete schema, both WebUIs receive `dao-agent-settings-changed`, rapid tool toggles serialize cumulative disabled-tool arrays and resync after failure, the resume window defaults to 3 and accepts 0, and runtime-only state is not migrated. With no active Agent turn, the gear opens exactly one foreground `dao://settings/agent` tab through its fixed native command; success closes the sidebar and failure leaves it open | +| ☐ | Settings Agent management summaries and limited native facade | `src/dao/.../agent/dao_agent_settings_handler.{h,cc}`, `resources/settings/dao_page/dao_agent_page*.patch`, `resources/agent/agent_settings_native_bridge.ts` | 🟡 | Verify legacy `dao_agent_stats` migration preserves existing counters once; persona reset removes the override and restores the runtime default; manual Dream generation remains disabled until Memory and Dream analysis are enabled, then reports success and failure without duplicate submissions; a Settings-side usage reset updates an already-open Agent WebUI; Memory clear requires confirmation and refreshes aggregate counts; Workspace reveal works without registering workspace mutation messages; configuration loading/error/retry is independent, including when `getSettings()` fails; Memory, Workspace, and Usage remain visible, independently loadable inset cards with 16px desktop spacing, 12px narrow spacing, compact rows, the shared content inset, and visible keyboard focus in light and dark themes; and the existing Skills, Memory, and Dream secondary links still open | | ☐ | 7 Dao WebUI configs registered (agent, dream, index, memory, sidebar, skills, welcome) | `webui/chrome_web_ui_configs.cc.patch` | 🟡 | All 7 pages load; confirm upstream `SkillsUIConfig` still exists (dao takes over `dao://skills`) | | ☐ | Agent tab helpers, cursor overlay, lock banner | `ui/tab_helpers.cc.patch` + `src/dao/.../agent/`, `dao_agent_*_view.*` | 🟢 | AI action cursor + lock banner show while agent controls a tab | | ☐ | Window-scoped Agent/MCP tab tools with stable identities | `src/dao/.../automation/dao_tab_tools.{h,cc}`, `dao_browser_automation_session.{h,cc}`, `dao_tab_identity.{h,cc}`, `dao_agent_ui.cc`, `browser_tool_catalog.json` | 🟡 | Run `DaoMcpTabToolsBrowserTest.*`, `DaoMcpSessionTest.*`, `DaoSidebarTabIdentityBrowserTest.*`, and `agent_bridge_call_native.test.ts`; verify WebContents replacement keeps the target, restored IDs reconcile uniquely, duplicate selectors fail closed, open rejects non-HTTP(S) schemes without mutation, constrained insertion returns the actual new tab, and unload-gated close reports the accepted/cancelled outcome once before Agent target-state cleanup | @@ -228,7 +228,7 @@ Not in `src/patches/`. Import mechanically rewrites `chrome://`→`dao://` in: | ✔ | Feature | Patch(es) | Risk | Verify | |---|---------|-----------|------|--------| | ☐ | Open Design continuous settings overview | `resources/settings/settings_{ui,main,menu}/`, top-level `*_page_index.html.patch` files, `settings_shared.css.patch`, `settings_page/settings_section.html.patch` | 🟡 | Verify the 184px table of contents and content share one background; all visible top-level sections render continuously in a 680px column with 30px spacing; nested page-index views have non-zero flow height and never overlap the following section; menu clicks and scroll position synchronize selection/hash without Router navigation; legacy top-level paths normalize to hashes; secondary routes remain independent and restore overview context; the real `#searchField` Polymer key is wired; entering search captures overview scroll once, shows only matching menu IDs, clears selection on zero results, restores the full menu and captured scroll on clear, and never navigates top-level routes; href, `pageVisibility_`, pref, policy, toggle, and link-row inventories remain intact. Run the Settings main/menu/UI and local redesign contract tests in light, dark, and narrow layouts. | -| ☐ | Top-level Agent settings section beside You and Dao | `resources/settings/dao_page/dao_{page,agent_page}.{html,ts}.patch`, `settings_main/`, `settings_menu/`, `lazy_load.ts.patch`, `route.ts.patch`, `router_dao.ts.patch` | 🟡 | Verify exact inventory preservation in the independent `agent` section; `/agent`, `#agent`, menu `data-section`, Settings Main view ID, overview scroll selection, and search filtering agree; `You and Dao` contains no Agent summary or state; the Agent module remains in Chromium's shared Settings lazy bundle without dynamic chunks; all Agent categories remain searchable; English-first localization passes; and both sibling sections work in light and dark themes, desktop and below 760 px, mouse and keyboard navigation, visible focus, and reduced motion. | +| ☐ | Compact Agent overview entry and five-section secondary page | `resources/settings/dao_page/dao_agent_page*.patch`, `settings_main/`, `settings_menu/`, `lazy_load.ts.patch`, `route.ts.patch`, `router_dao.ts.patch` | 🟡 | Verify the continuous overview renders one compact Agent entry and no full Agent form; `#agent` scrolls to that entry; activating it or navigating directly to `/agent` opens only the secondary detail view; Back restores the overview and Agent scroll context; the Agent menu item remains selected on the child route; Settings search finds the compact entry; and the detail page renders exactly Model and connection, Behavior and context, Capabilities, Learning and analysis, and Data and management in that order with no section rail or tabs. Check mouse and keyboard activation, heading focus, visible focus, reduced motion, light/dark themes, desktop, and below 760 px. | | ☐ | Dao settings page UI (Profile feature controls + global MCP card + enhanced-PiP preview) | `resources/settings/dao_page/dao_page.{html,ts}.patch` (new files), `src/dao/.../mcp/dao_mcp_settings_handler.{h,cc}` | 🟡 | Page renders the Profile-backed feature toggles plus an independent MCP header/connection/enabled-only-setup card. Verify aligned, narrow-width-stacking setup controls; lease-only client/Stop controls; whitespace-preserving preview; native three-space Generic MCP JSON identical to copied content; malformed JSON failure without clipboard change; dynamic copy feedback; no standalone configuration action; and the animated PiP preview | | ☐ | Dao settings prefs exposed through SettingsPrivate allowlist | `extensions/api/settings_private/prefs_util.cc.patch`, `src/dao/browser/dao_pref_names.*` | 🟡 | Toggles read/write `dao.little_dao_enabled`, `dao.enhanced_pip_enabled`, and `dao.enhanced_command_bar_suggestions_enabled` without console errors | | ☐ | Page registered in build | `resources/settings/BUILD.gn.patch` | 🟢 | `dao_page.ts` compiled/bundled | diff --git a/docs/features.md b/docs/features.md index 81b6b4a6..121c38e3 100644 --- a/docs/features.md +++ b/docs/features.md @@ -148,7 +148,8 @@ The stack includes: **LLM tool calling**, **long-term memory** (SQLite + FTS5), **Application shell** (`src/dao/browser/ui/webui/resources/agent/`) - `agent.{html,css,ts}` + `dao_agent_app.ts` — Chat app entry; its settings - button opens the unified `dao://settings/#agent` overview section + button uses a fixed native navigation command to open the unified + `dao://settings/agent` secondary page and closes the sidebar only after the navigation succeeds - `skills.html` + `skills.ts` — Skill manager standalone entry - `agent_bridge.ts` — Mojo bridge - `agent_settings_{sync,native_bridge}.ts` — One-time migration from the @@ -172,21 +173,24 @@ The stack includes: **LLM tool calling**, **long-term memory** (SQLite + FTS5), - `skill_registry.ts` — Skill catalog and lookup - `tool_catalog.ts` — Tool catalog schema -**Unified Agent settings** (`dao://settings/#agent`, top-level route `/agent`) +**Unified Agent settings** (`dao://settings/agent`, overview entry `#agent`) - `DaoAgentSettingsHandler` stores durable Agent choices in Profile prefs and is shared by the Agent and Settings WebUIs -- `Agent` is a top-level Dao-exclusive Settings section beside `You and Dao`. - `You and Dao` owns browser capabilities only; all Agent configuration, - Memory, Workspace, Usage, Skills, and Dream controls appear exactly once in - the Agent section +- `Agent` remains a compact top-level Dao-exclusive Settings entry beside + `You and Dao`. `You and Dao` owns browser capabilities only; the complete + Agent configuration, Memory, Workspace, Usage, Skills, and Dream controls + appear exactly once on the Agent secondary page - The Agent module is packaged in Chromium's shared Settings lazy bundle and - rendered as its own continuous-overview section. Search delegates directly - to it, keeping provider, model, API key, session, personality, context, - tools, search, memory, proactive suggestions, Dream, workspace, skills, and - usage discoverable without a duplicate summary or intermediate subpage + reached from the compact overview entry. The detail page uses five vertical Settings sections: + Model and connection, Behavior and context, + Capabilities, Learning and analysis, and Data and management. It adds no + section rail or tabs - Model/provider credentials, session/display behavior, persona, page and conversation context, web search, memory/proactive suggestions, and Dream analysis are managed in the Agent Settings section +- The persona card can restore the runtime default, while the Dream card can + generate a report immediately when Memory and Dream analysis are enabled and + keeps the existing Dream report history link available - Tool group shortcuts and the expandable individual-permission list preserve the existing per-tool enable/disable controls. Rapid changes update one optimistic disabled-tool set and serialize complete-array writes before @@ -199,6 +203,8 @@ The stack includes: **LLM tool calling**, **long-term memory** (SQLite + FTS5), hours. Configuration has independent loading, error, and retry feedback, so a configuration failure does not hide or disable management summaries - The Agent section owns complete Memory, Workspace, and Usage management. + These modules render as independent compact cards inside Data and management + so their headings and actions remain visually separated. Memory and workspace data come from their Profile-keyed services; usage counters are stored in the Profile `dao.agent_usage_stats` pref, which is the source of truth shared with already-open Agent WebUIs diff --git a/scripts/commands/__tests__/settings_i18n.test.ts b/scripts/commands/__tests__/settings_i18n.test.ts index 203e33b5..8d26229e 100644 --- a/scripts/commands/__tests__/settings_i18n.test.ts +++ b/scripts/commands/__tests__/settings_i18n.test.ts @@ -177,6 +177,34 @@ const daoSettingsTranslations = [ ]; const daoAgentManagementTranslations = [ + ['DAO_AGENT_SOUL_RESET', 'daoAgentSoulReset', '恢复默认设置'], + [ + 'DAO_AGENT_SOUL_RESET_SUCCESS', + 'daoAgentSoulResetSuccess', + '已恢复默认人格', + ], + [ + 'DAO_AGENT_SOUL_RESET_ERROR', + 'daoAgentSoulResetError', + '无法恢复默认人格。', + ], + [ + 'DAO_AGENT_DREAM_RUN_DESCRIPTION', + 'daoAgentDreamRunDescription', + '请先启用记忆和 Dream 分析,再生成报告。', + ], + ['DAO_AGENT_DREAM_RUN_NOW', 'daoAgentDreamRunNow', '生成 Dream 报告'], + ['DAO_AGENT_DREAM_RUNNING', 'daoAgentDreamRunning', '正在生成…'], + [ + 'DAO_AGENT_DREAM_RUN_SUCCESS', + 'daoAgentDreamRunSuccess', + 'Dream 报告已生成', + ], + [ + 'DAO_AGENT_DREAM_RUN_ERROR', + 'daoAgentDreamRunError', + '无法生成 Dream 报告。', + ], ['DAO_AGENT_MANAGEMENT_TITLE', 'daoAgentManagementTitle', '数据与管理'], ['DAO_AGENT_MANAGEMENT_MEMORY_TITLE', 'daoAgentManagementMemoryTitle', '记忆'], ['DAO_AGENT_MANAGEMENT_WORKSPACE_TITLE', 'daoAgentManagementWorkspaceTitle', '工作区'], @@ -267,6 +295,24 @@ function getGritMessageId(message: string, meaning = ''): string { } describe('settings i18n patches', () => { + it('reuses localized Agent configure copy in the compact overview', () => { + const indexPatch = readFileSync(path.join( + process.cwd(), + 'src/patches/chrome/browser/resources/settings/dao_page/' + + 'dao_agent_page_index.html.patch'), 'utf-8'); + const providerPatch = readFileSync(path.join( + process.cwd(), + 'src/patches/chrome/browser/ui/webui/settings/' + + 'settings_localized_strings_provider.cc.patch'), 'utf-8'); + + expect(indexPatch).toContain('$i18n{daoAgentConfigureTitle}'); + expect(indexPatch).toContain('$i18n{daoAgentConfigureDescription}'); + expect(providerPatch).toMatch( + /\{"daoAgentConfigureTitle",\s*\+\s*IDS_SETTINGS_DAO_AGENT_CONFIGURE_TITLE\}/); + expect(providerPatch).toMatch( + /\{"daoAgentConfigureDescription",\s*\+\s*IDS_SETTINGS_DAO_AGENT_CONFIGURE_DESCRIPTION\}/); + }); + it('localizes every Agent management state through the settings provider', () => { const grdpPatch = readFileSync(path.join( process.cwd(), 'src/patches/chrome/app/settings_strings.grdp.patch'), @@ -286,7 +332,7 @@ describe('settings i18n patches', () => { const {source, meaning} = getGrdpMessage(grdpPatch, resourceName); const translationId = getGritMessageId(source, meaning); const providerPattern = new RegExp( - `\\{"${providerKey}",\\s*${resourceName}\\}`, 'g'); + `\\{"${providerKey}",\\s*(?:\\+\\s*)?${resourceName}\\}`, 'g'); const translationPattern = new RegExp( `` + `${escapeRegExp(translation)}`, @@ -299,15 +345,17 @@ describe('settings i18n patches', () => { } }); - it('keeps Agent management IDs distinct from Chromium shared labels', () => { + it('keeps Agent settings IDs distinct from Chromium shared labels', () => { const grdpPatch = readFileSync(path.join( process.cwd(), 'src/patches/chrome/app/settings_strings.grdp.patch'), 'utf-8'); const chromiumSharedTranslationIds = new Set([ '1293556467332435079', + '2332742915001411729', '5063480226653192405', '5154917547274118687', '5801568494490449797', + '5983716913605894570', '7144878232160441200', '7658239707568436148', '8633025649649592204', diff --git a/scripts/commands/__tests__/settings_redesign_contract.test.ts b/scripts/commands/__tests__/settings_redesign_contract.test.ts index df9e07d2..4b818925 100644 --- a/scripts/commands/__tests__/settings_redesign_contract.test.ts +++ b/scripts/commands/__tests__/settings_redesign_contract.test.ts @@ -84,6 +84,9 @@ const AGENT_CONTROL_AND_ACTION_IDS = [ "daoAgentJinaApiKey", "daoAgentThreshold", "daoAgentDreamExcludedDomains", + "resetAgentSoulButton", + "runDreamNowButton", + "openDreamHistoryLink", "agentSettingsRetry", "agentMemoryRetry", "agentMemoryRefreshRetry", @@ -121,6 +124,9 @@ const AGENT_CRITICAL_HANDLERS = [ "onAgentProviderChange_", "onAgentProviderFieldChange_", "onAgentDreamDomainsChange_", + "onResetAgentSoul_", + "onRunDreamNow_", + "isDreamRunDisabled_", "onAgentToolGroupChange_", "isAgentToolEnabled_", "onAgentToolChange_", @@ -351,6 +357,83 @@ describe("settings continuous overview contract", () => { expect(tsPatch).toContain("Object.values(AGENT_TOOL_GROUPS).flat()"); }); + it("preserves legacy Agent actions and one shared content inset", () => { + const htmlPatch = addedPayload( + readPatch("dao_page/dao_agent_page.html.patch"), + ); + const tsPatch = addedPayload( + readPatch("dao_page/dao_agent_page.ts.patch"), + ); + const proxyPatch = addedPayload( + readPatch("dao_page/dao_agent_settings_browser_proxy.ts.patch"), + ); + const handler = readDaoSource( + "src/dao/browser/agent/dao_agent_settings_handler.cc", + ); + + for (const id of AGENT_CONTROL_AND_ACTION_IDS) { + expect(htmlPatch, id).toContain(`id="${id}"`); + } + for (const handlerName of AGENT_CRITICAL_HANDLERS) { + expect(tsPatch, handlerName).toContain(handlerName); + } + expect(proxyPatch).toContain("startManualDream(): Promise"); + expect(proxyPatch).toContain("sendWithPromise('startManualDaoDream')"); + expect(handler).toContain('"startManualDaoDream"'); + + expect(htmlPatch).toContain("--dao-agent-content-inset: 18px"); + expect(htmlPatch).toContain("margin: 12px auto 0"); + expect(htmlPatch).not.toMatch(/padding:\s*0 16px/); + }); + + it("separates Agent management modules into compact inset cards", () => { + const htmlPatch = addedPayload( + readPatch("dao_page/dao_agent_page.html.patch"), + ); + + expect(htmlPatch).toContain("--dao-agent-management-gap: 16px"); + expect(htmlPatch).toContain("--dao-agent-management-padding: 16px"); + expect(htmlPatch).toContain("gap: var(--dao-agent-management-gap)"); + expect(htmlPatch).toContain( + "padding: var(--dao-agent-management-padding)", + ); + expect(htmlPatch).toMatch( + /\.dao-agent-management-card\s*\{[\s\S]*?border: 1px solid var\(--dao-settings-border\)/, + ); + expect(htmlPatch).toMatch( + /\.dao-agent-management-card\s*\{[\s\S]*?border-radius: 12px/, + ); + expect(htmlPatch).toMatch( + /\.dao-agent-management-card\s*\{[\s\S]*?box-shadow: var\(--dao-settings-shadow\)/, + ); + const managementCardHeader = htmlPatch.match( + /\.dao-agent-management-card-header \{[^}]*\}/, + )?.[0]; + expect(managementCardHeader).toBeDefined(); + expect(managementCardHeader).toContain("align-items: center;"); + + const managementCardTitle = htmlPatch.match( + /\.dao-agent-management-card-title \{[^}]*\}/, + )?.[0]; + expect(managementCardTitle).toBeDefined(); + expect(managementCardTitle).toContain( + "border-inline-start: 3px solid var(--dao-settings-accent);", + ); + expect(managementCardTitle).toContain("font-size: 14px;"); + expect(managementCardTitle).toContain("font-weight: 650;"); + expect(managementCardTitle).toContain("padding-inline-start: 10px;"); + expect(occurrenceCount( + htmlPatch, + '

', + )).toBe(3); + expect(htmlPatch).toContain("min-height: 54px"); + expect(htmlPatch).toContain( + "background: var(--dao-agent-management-action-surface)", + ); + expect(htmlPatch).toContain("--dao-agent-management-gap: 12px"); + expect(htmlPatch).toContain("--dao-agent-management-padding: 12px"); + }); + it("packages Agent settings in the shared Settings lazy bundle", () => { const lazyLoad = "lazy_load.ts.patch"; @@ -364,8 +447,9 @@ describe("settings continuous overview contract", () => { ); }); - it("owns Agent as a top-level Settings section beside You and Dao", () => { + it("keeps a compact Agent overview entry and routes details to a subpage", () => { const routePatch = addedPayload(readPatch("route.ts.patch")); + const routerPatch = addedPayload(readPatch("router_dao.ts.patch")); const menuPatch = addedPayload( readPatch("settings_menu/settings_menu.html.patch"), ); @@ -374,6 +458,14 @@ describe("settings continuous overview contract", () => { ); const daoHtml = addedPayload(readPatch("dao_page/dao_page.html.patch")); const daoTs = addedPayload(readPatch("dao_page/dao_page.ts.patch")); + const indexTsPath = "dao_page/dao_agent_page_index.ts.patch"; + const indexHtmlPath = "dao_page/dao_agent_page_index.html.patch"; + const hasIndexPatches = + existsSync(patchPath(indexTsPath)) && + existsSync(patchPath(indexHtmlPath)); + expect(hasIndexPatches).toBe(true); + if (!hasIndexPatches) return; + const indexHtml = addedPayload(readPatch(indexHtmlPath)); const agentHtml = addedPayload( readPatch("dao_page/dao_agent_page.html.patch"), ); @@ -381,9 +473,13 @@ describe("settings continuous overview contract", () => { expect(routePatch).toContain( "r.DAO_AGENT = r.BASIC.createSection(\n" + - " '/agent', 'agent', " + - "loadTimeData.getString('daoAgentSettingsTitle'));", + " '/agentOverview', 'agent',\n" + + " loadTimeData.getString('daoAgentSettingsTitle'));", + ); + expect(routePatch).toContain( + "r.DAO_AGENT_DETAILS = r.DAO_AGENT.createChild('/agent');", ); + expect(routerPatch).toContain("DAO_AGENT_DETAILS: Route;"); expect(routePatch).not.toContain("r.DAO.createChild('/dao/agent')"); const daoMenuIndex = menuPatch.indexOf( @@ -400,10 +496,17 @@ describe("settings continuous overview contract", () => { expect(mainPatch).toContain(''); expect(mainPatch).toContain('
'); expect(mainPatch).toContain( + '', + ); + expect(mainPatch).not.toContain( '', ); expect(mainPatch).not.toContain(" { ); expect(menuTest).toContain("tracksAgentAsSiblingDaoExclusiveSection"); expect(mainTest).toContain( - "#switcher > #agent settings-dao-agent-page", + "#switcher > #agent settings-dao-agent-page-index", ); expect(agentTest).toContain( - "rendersAsTopLevelAgentSectionWithoutSubpageBackControl", + "rendersFiveVerticalSettingsSections", ); }); @@ -526,60 +629,57 @@ describe("settings continuous overview contract", () => { expect(translation![1]).toMatch(/[\u3400-\u9fff]/u); } - for (const [sectionId, headingId, messageId] of [ + for (const [sectionId, messageId] of [ [ "modelAndConnection", - "modelAndConnectionHeading", "daoAgentGroupModelAndConnection", ], [ "behaviorAndContext", - "behaviorAndContextHeading", "daoAgentGroupBehaviorAndContext", ], - ["capabilities", "capabilitiesHeading", "daoAgentGroupCapabilities"], + ["capabilities", "daoAgentGroupCapabilities"], [ "learningAndAnalysis", - "learningAndAnalysisHeading", "daoAgentGroupLearningAndAnalysis", ], [ "dataAndManagement", - "dataAndManagementHeading", "daoAgentGroupDataAndManagement", ], ] as const) { - expect(agentHtml, sectionId).toContain(`
]*>\\s*\\$i18n\\{${messageId}\\}\\s*

`, + ` { + it("documents the compact Agent entry and secondary page contract", () => { const features = readDaoSource("docs/features.md"); const checklist = readDaoSource("docs/feature-checklist.md"); for (const marker of [ - "`dao://settings/#agent`", - "top-level Dao-exclusive Settings section", - "all Agent configuration,", + "`dao://settings/agent`", + "compact top-level Dao-exclusive Settings entry", + "five vertical Settings sections", "Chromium's shared Settings lazy bundle", - "without a duplicate summary or intermediate subpage", + "closes the sidebar only after the navigation succeeds", ]) { expect(features, marker).toContain(marker); } for (const marker of [ - "exact inventory", - "Chromium's shared Settings lazy bundle", - "You and Dao` contains no Agent summary or state", - "overview scroll selection", - "search filtering", - "English-first", - "light and dark", + "one compact Agent entry and no full Agent form", + "navigating directly to `/agent`", + "Back restores the overview", + "Settings search finds the compact entry", + "light/dark themes", "below 760 px", "keyboard", "reduced motion", @@ -596,6 +696,7 @@ describe("settings continuous overview contract", () => { const proxy = '"dao_page/dao_agent_settings_browser_proxy.ts"'; const components = [ '"dao_page/dao_agent_page.ts"', + '"dao_page/dao_agent_page_index.ts"', '"dao_page/dao_page.ts"', ]; const webComponentHunk = hunks.find((hunk) => @@ -635,7 +736,7 @@ describe("settings continuous overview contract", () => { .filter((line) => line.startsWith("+") && !line.startsWith("+++")).length; expect(hunkHeader, "new-file hunk header").not.toBeNull(); - expect(payloadCount).toBe(177); + expect(payloadCount).toBe(182); expect(Number(hunkHeader![1]), "declared new-file line count").toBe( payloadCount, ); @@ -682,6 +783,7 @@ describe("settings continuous overview contract", () => { const testTargets = [ ["dao_page_test", "DaoPage"], + ["dao_agent_page_index_test", "DaoAgentPageIndex"], ["dao_agent_page_test", "DaoAgentPage"], ] as const; for (const [source, runner] of testTargets) { @@ -706,7 +808,7 @@ describe("settings continuous overview contract", () => { runnerPayload, "IN_PROC_BROWSER_TEST_F(SettingsTest, DISABLED_Dao", ), - ).toBe(2); + ).toBe(3); expect(runnerPayload).not.toMatch( /IN_PROC_BROWSER_TEST_F\(SettingsTest, Dao(?:Agent)?Page(?:Index)?\)/, ); @@ -925,7 +1027,7 @@ describe("settings continuous overview contract", () => { "dao_agent_page_test.ts.patch", ); const managementStart = htmlPatch.indexOf( - '
", @@ -1074,7 +1176,7 @@ describe("settings continuous overview contract", () => { const resetDialog = extractBetween( htmlPatch, 'id="resetUsageStatsDialog"', - "
", + "
", ); expect(memoryCard).toMatch( @@ -1115,7 +1217,7 @@ describe("settings continuous overview contract", () => { 7, ); expect(htmlPatch).toMatch( - /\.dao-agent-management-state \{[\s\S]*?min-height: 62px;/, + /\.dao-agent-management-state \{[\s\S]*?min-height: 54px;/, ); expect(htmlPatch).toMatch( /@media \(max-width: 760px\)[\s\S]*?\.dao-agent-management-state\.error \{[\s\S]*?flex-direction: column;/, diff --git a/src/dao/browser/agent/dao_agent_settings_handler.cc b/src/dao/browser/agent/dao_agent_settings_handler.cc index d3df016f..d82fb512 100644 --- a/src/dao/browser/agent/dao_agent_settings_handler.cc +++ b/src/dao/browser/agent/dao_agent_settings_handler.cc @@ -24,6 +24,8 @@ #include "dao/browser/agent/dao_agent_workspace_service.h" #include "dao/browser/agent/dao_agent_workspace_service_factory.h" #include "dao/browser/agent/dao_dream_domain_utils.h" +#include "dao/browser/agent/dao_dream_service.h" +#include "dao/browser/agent/dao_dream_service_factory.h" #include "dao/browser/dao_pref_names.h" namespace dao { @@ -516,6 +518,10 @@ void DaoAgentSettingsHandler::RegisterMessages() { "openDaoAgentWorkspace", base::BindRepeating(&DaoAgentSettingsHandler::HandleOpenWorkspace, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "startManualDaoDream", + base::BindRepeating(&DaoAgentSettingsHandler::HandleStartManualDream, + base::Unretained(this))); web_ui()->RegisterMessageCallback( "getDaoAgentUsageStats", base::BindRepeating(&DaoAgentSettingsHandler::HandleGetUsageStats, @@ -703,6 +709,39 @@ void DaoAgentSettingsHandler::HandleOpenWorkspace(const base::ListValue& args) { ResolveJavascriptCallback(args[0], base::Value(true)); } +void DaoAgentSettingsHandler::HandleStartManualDream( + const base::ListValue& args) { + AllowJavascript(); + if (args.size() != 1 || !args[0].is_string()) { + return; + } + const std::string callback_id = args[0].GetString(); + Profile* profile = Profile::FromWebUI(web_ui()); + DaoDreamService* service = + profile ? DaoDreamServiceFactory::GetForProfile(profile) : nullptr; + if (!service) { + RejectJavascriptCallback(base::Value(callback_id), + base::Value("dream service unavailable")); + return; + } + service->StartManualDream(base::BindOnce( + [](base::WeakPtr handler, + std::string callback_id, bool success, const std::string& error) { + if (!handler) { + return; + } + if (success) { + handler->ResolveJavascriptCallback(base::Value(callback_id), + base::Value(true)); + return; + } + handler->RejectJavascriptCallback( + base::Value(callback_id), + base::Value(error.empty() ? "dream run failed" : error)); + }, + weak_factory_.GetWeakPtr(), callback_id)); +} + void DaoAgentSettingsHandler::HandleGetUsageStats(const base::ListValue& args) { AllowJavascript(); if (args.size() != 1 || !args[0].is_string()) { diff --git a/src/dao/browser/agent/dao_agent_settings_handler.h b/src/dao/browser/agent/dao_agent_settings_handler.h index df22a988..2b2c4ee5 100644 --- a/src/dao/browser/agent/dao_agent_settings_handler.h +++ b/src/dao/browser/agent/dao_agent_settings_handler.h @@ -66,6 +66,7 @@ class DaoAgentSettingsHandler : public content::WebUIMessageHandler { void HandleClearAllMemory(const base::ListValue& args); void HandleGetWorkspaceSummary(const base::ListValue& args); void HandleOpenWorkspace(const base::ListValue& args); + void HandleStartManualDream(const base::ListValue& args); void HandleGetUsageStats(const base::ListValue& args); void HandleResetUsageStats(const base::ListValue& args); void HandleRecordApiUsage(const base::ListValue& args); diff --git a/src/dao/browser/agent/dao_agent_settings_handler_unittest.cc b/src/dao/browser/agent/dao_agent_settings_handler_unittest.cc index 1372cf58..be83f0ab 100644 --- a/src/dao/browser/agent/dao_agent_settings_handler_unittest.cc +++ b/src/dao/browser/agent/dao_agent_settings_handler_unittest.cc @@ -234,6 +234,18 @@ TEST_F(DaoAgentSettingsHandlerTest, ValidatesNativeBooleanSettings) { EXPECT_TRUE(prefs_.GetBoolean(prefs::kDaoAgentMemoryEnabled)); } +TEST_F(DaoAgentSettingsHandlerTest, ResettingSoulRemovesPersonaOverride) { + EXPECT_TRUE(SetDaoAgentSetting(&prefs_, "dao_agent_soul", + base::Value("Custom soul"))); + EXPECT_EQ("Custom soul", + *prefs_.GetDict(prefs::kDaoAgentSettings) + .FindString("dao_agent_soul")); + + EXPECT_TRUE(SetDaoAgentSetting(&prefs_, "dao_agent_soul", base::Value())); + EXPECT_FALSE( + prefs_.GetDict(prefs::kDaoAgentSettings).contains("dao_agent_soul")); +} + TEST_F(DaoAgentSettingsHandlerTest, NormalizesDreamExcludedDomains) { EXPECT_TRUE(SetDaoAgentSetting( &prefs_, kDaoDreamExcludedDomainsSetting, diff --git a/src/dao/browser/agent/dao_dream_browsertest.cc b/src/dao/browser/agent/dao_dream_browsertest.cc index ab86a08c..85af97f5 100644 --- a/src/dao/browser/agent/dao_dream_browsertest.cc +++ b/src/dao/browser/agent/dao_dream_browsertest.cc @@ -4556,7 +4556,7 @@ IN_PROC_BROWSER_TEST_F(DaoDreamBrowserTest, EXPECT_EQ("manual", r->trigger_kind); EXPECT_EQ("# nightly report", r->report_markdown); std::optional material_stats = - base::JSONReader::Read(r->material_stats); + base::JSONReader::Read(r->material_stats, base::JSON_PARSE_RFC); ASSERT_TRUE(material_stats.has_value()); ASSERT_TRUE(material_stats->is_dict()); const base::DictValue* stored_recap = diff --git a/src/dao/browser/mcp/dao_mcp_helper_browsertest.cc b/src/dao/browser/mcp/dao_mcp_helper_browsertest.cc index efab1b4f..4c23ee68 100644 --- a/src/dao/browser/mcp/dao_mcp_helper_browsertest.cc +++ b/src/dao/browser/mcp/dao_mcp_helper_browsertest.cc @@ -37,6 +37,12 @@ namespace { constexpr char kMcpProtocolVersion[] = "2025-11-25"; +#if defined(DAO_MCP_VERSION) +constexpr char kExpectedDaoMcpVersion[] = DAO_MCP_VERSION; +#else +constexpr char kExpectedDaoMcpVersion[] = "missing DAO_MCP_VERSION"; +#endif + base::DictValue InitializeRequest(base::Value id) { return base::DictValue() .Set("jsonrpc", "2.0") @@ -517,7 +523,7 @@ TEST_F(DaoMcpHelperBrowserTest, const base::DictValue* server_info = result->FindDict("serverInfo"); ASSERT_TRUE(server_info); EXPECT_EQ("dao-browser", *server_info->FindString("name")); - EXPECT_EQ("1.0.79.0", *server_info->FindString("version")); + EXPECT_EQ(kExpectedDaoMcpVersion, *server_info->FindString("version")); ASSERT_TRUE(result->FindDict("capabilities")); EXPECT_TRUE(result->FindDict("capabilities")->FindDict("tools")); diff --git a/src/dao/browser/mcp/dao_mcp_settings_handler_browsertest.cc b/src/dao/browser/mcp/dao_mcp_settings_handler_browsertest.cc index 54a2567a..67d2660e 100644 --- a/src/dao/browser/mcp/dao_mcp_settings_handler_browsertest.cc +++ b/src/dao/browser/mcp/dao_mcp_settings_handler_browsertest.cc @@ -109,7 +109,11 @@ class TestDaoMcpSettingsHandler : public DaoMcpSettingsHandler { class DaoMcpSettingsHandlerTest : public testing::Test { public: DaoMcpSettingsHandlerTest() - : handler_(std::make_unique(&service_)) { + : user_data_override_( + chrome::DIR_USER_DATA, + base::FilePath(FILE_PATH_LITERAL("/Users/Test/Dao Debug")), + true), + handler_(std::make_unique(&service_)) { handler_->set_web_ui(&web_ui_); } @@ -131,6 +135,7 @@ class DaoMcpSettingsHandlerTest : public testing::Test { private: content::BrowserTaskEnvironment task_environment_; + base::ScopedPathOverride user_data_override_; content::TestWebUI web_ui_; FakeDaoMcpSettingsService service_; std::unique_ptr handler_; @@ -407,6 +412,9 @@ TEST_F(DaoMcpSettingsHandlerTest, ObservesOnlyWhileJavascriptIsAllowed) { TEST(DaoMcpSettingsConfigurationTest, UsesProvidedApplicationBundlePath) { const base::FilePath bundle_path( FILE_PATH_LITERAL("/Volumes/Test/Dao Debug.app")); + base::ScopedPathOverride user_data_override( + chrome::DIR_USER_DATA, + base::FilePath(FILE_PATH_LITERAL("/Users/Test/Dao Debug")), true); const std::string configuration = BuildDaoMcpConfigurationForBundle(bundle_path); const std::optional parsed = diff --git a/src/dao/browser/ui/views/dao_browser_browsertest.cc b/src/dao/browser/ui/views/dao_browser_browsertest.cc index 2b163da0..a0d89157 100644 --- a/src/dao/browser/ui/views/dao_browser_browsertest.cc +++ b/src/dao/browser/ui/views/dao_browser_browsertest.cc @@ -9344,6 +9344,25 @@ IN_PROC_BROWSER_TEST_F(DaoAgentSidebarViewBrowserTest, WidthClampedToBounds) { EXPECT_GE(V::kResizeAreaWidth, 1); } +IN_PROC_BROWSER_TEST_F(DaoAgentSidebarViewBrowserTest, + AgentGearOpensSettingsSubpageWithoutTurn) { + ASSERT_TRUE(LoadAgentWebUI()); + const int tab_count_before = browser()->tab_strip_model()->count(); + ui_test_utils::TabAddedWaiter tab_waiter(browser()); + + ASSERT_TRUE(content::ExecJs(agent_contents_, R"( + document.querySelector( + 'dao-agent-app .dao-app-tab-bar .dao-app-tab:last-child').click(); + )")); + tab_waiter.Wait(); + + EXPECT_EQ(tab_count_before + 1, browser()->tab_strip_model()->count()); + content::WebContents* active = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_NE(nullptr, active); + EXPECT_EQ(GURL("dao://settings/agent"), active->GetVisibleURL()); +} + IN_PROC_BROWSER_TEST_F(DaoAgentSidebarViewBrowserTest, PrefillPromptInvokesOnlyPrefillHookWithJsonEscapedText) { ASSERT_TRUE(LoadAgentWebUI()); diff --git a/src/dao/browser/ui/views/dao_system_dialog.cc b/src/dao/browser/ui/views/dao_system_dialog.cc index ae454e06..6cfe7987 100644 --- a/src/dao/browser/ui/views/dao_system_dialog.cc +++ b/src/dao/browser/ui/views/dao_system_dialog.cc @@ -23,7 +23,9 @@ #include "ui/views/border.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/label.h" +#include "ui/views/controls/scroll_view.h" #include "ui/views/layout/proposed_layout.h" +#include "ui/views/view_utils.h" #include "ui/views/view_class_properties.h" #include "ui/views/window/dialog_delegate.h" @@ -43,6 +45,15 @@ constexpr gfx::Insets kDaoDialogTitleMargins = gfx::Insets::TLBR(20, 22, 8, 22); constexpr gfx::Insets kDaoDialogButtonPadding = gfx::Insets::VH(7, 13); +void ClearScrollViewBackgrounds(views::View* root) { + if (auto* scroll_view = views::AsViewClass(root)) { + scroll_view->SetBackgroundColor(std::nullopt); + } + for (views::View* child : root->children()) { + ClearScrollViewBackgrounds(child); + } +} + SkColor DialogButtonBackground(ui::ButtonStyle style) { if (style == ui::ButtonStyle::kProminent) { return IsDarkMode() ? SkColorSetRGB(248, 250, 252) @@ -312,6 +323,12 @@ void ConfigureDaoSystemDialog(views::DialogDelegate* delegate, } } +void ConfigureDaoSystemDialogContent(views::View* content) { + if (content) { + ClearScrollViewBackgrounds(content); + } +} + std::unique_ptr CreateDaoDialogButton( views::Button::PressedCallback callback, std::u16string_view label, diff --git a/src/dao/browser/ui/views/dao_system_dialog.h b/src/dao/browser/ui/views/dao_system_dialog.h index f3812fa3..74a25167 100644 --- a/src/dao/browser/ui/views/dao_system_dialog.h +++ b/src/dao/browser/ui/views/dao_system_dialog.h @@ -17,6 +17,7 @@ namespace views { class DialogDelegate; +class View; } // namespace views namespace dao { @@ -39,6 +40,9 @@ void ConfigureDaoSystemDialog( views::DialogDelegate* delegate, const DaoSystemDialogOptions& options = DaoSystemDialogOptions()); +// Makes nested scrollable content inherit the custom dialog surface. +void ConfigureDaoSystemDialogContent(views::View* content); + std::unique_ptr CreateDaoDialogButton( views::Button::PressedCallback callback, std::u16string_view label, diff --git a/src/dao/browser/ui/webui/dao_agent_ui.cc b/src/dao/browser/ui/webui/dao_agent_ui.cc index 02686c81..bffd44fd 100644 --- a/src/dao/browser/ui/webui/dao_agent_ui.cc +++ b/src/dao/browser/ui/webui/dao_agent_ui.cc @@ -499,6 +499,10 @@ void DaoAgentUIHandler::RegisterMessages() { "clearConsoleMessages", base::BindRepeating(&DaoAgentUIHandler::HandleClearConsoleMessages, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "openAgentSettings", + base::BindRepeating(&DaoAgentUIHandler::HandleOpenAgentSettings, + base::Unretained(this))); web_ui()->RegisterMessageCallback( "closeSidebar", base::BindRepeating(&DaoAgentUIHandler::HandleCloseSidebar, @@ -1488,6 +1492,33 @@ void DaoAgentUIHandler::HandleSearchInResources(const base::ListValue& args) { args[1].GetDict().Clone()); } +void DaoAgentUIHandler::HandleOpenAgentSettings( + const base::ListValue& args) { + AllowJavascript(); + if (args.empty() || !args[0].is_string()) { + return; + } + const std::string callback_id = args[0].GetString(); + + Browser* browser = FindLastActiveBrowserForMigration(); + if (!browser) { + ResolveJavascriptCallback( + base::Value(callback_id), + base::DictValue().Set("success", false)); + return; + } + + const GURL settings_url(std::string(content::kChromeUIScheme) + + "://settings/agent"); + NavigateParams params(browser, settings_url, ui::PAGE_TRANSITION_TYPED); + params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; + Navigate(¶ms); + ResolveJavascriptCallback( + base::Value(callback_id), + base::DictValue().Set( + "success", params.navigated_or_inserted_contents != nullptr)); +} + void DaoAgentUIHandler::HandleCloseSidebar(const base::ListValue& args) { Browser* browser = FindLastActiveBrowserForMigration(); if (!browser) { diff --git a/src/dao/browser/ui/webui/dao_agent_ui.h b/src/dao/browser/ui/webui/dao_agent_ui.h index 9c71f4ea..4f65b371 100644 --- a/src/dao/browser/ui/webui/dao_agent_ui.h +++ b/src/dao/browser/ui/webui/dao_agent_ui.h @@ -196,6 +196,7 @@ class DaoAgentUIHandler : public content::WebUIMessageHandler, void HandleSearchInResources(const base::ListValue& args); // Sidebar control. + void HandleOpenAgentSettings(const base::ListValue& args); void HandleCloseSidebar(const base::ListValue& args); void HandleFocusAgentSidebar(const base::ListValue& args); diff --git a/src/dao/browser/ui/webui/dao_sidebar_ui.cc b/src/dao/browser/ui/webui/dao_sidebar_ui.cc index 0fef0167..16b51da5 100644 --- a/src/dao/browser/ui/webui/dao_sidebar_ui.cc +++ b/src/dao/browser/ui/webui/dao_sidebar_ui.cc @@ -1493,17 +1493,17 @@ void DaoSidebarUIHandler::PinTabAtIndex(int index, int pinned_target_index) { return; } + PersistBackingIdentity(contents); + model->SetTabPinned(index, true); DaoPinnedTabItem& pinned_item = pinned_tab_model_.AddOrUpdate(base::UTF16ToUTF8(contents->GetTitle()), contents->GetVisibleURL().spec(), FaviconToDataUrl(contents), GetSidebarTabId(contents)); - PersistBackingIdentity(contents); if (pinned_target_index >= 0) { pinned_tab_model_.Move(pinned_item.id, static_cast(pinned_target_index)); } - model->SetTabPinned(index, true); SavePinnedItems(); if (IsJavascriptAllowed()) { PushFullState(); diff --git a/src/dao/browser/ui/webui/resources/agent/__tests__/dao_agent_app.test.ts b/src/dao/browser/ui/webui/resources/agent/__tests__/dao_agent_app.test.ts index 4805bddb..a5bcc069 100644 --- a/src/dao/browser/ui/webui/resources/agent/__tests__/dao_agent_app.test.ts +++ b/src/dao/browser/ui/webui/resources/agent/__tests__/dao_agent_app.test.ts @@ -96,18 +96,36 @@ describe('dao-agent-app i18n refresh', () => { expect(mocks.chatRequestUpdate).toHaveBeenCalled(); }); - it('opens unified settings from the settings button', async () => { + it('closes the sidebar after unified settings opens', async () => { + mocks.callNative.mockResolvedValueOnce({success: true}); const {el, send} = await loadApp(); const settingsButton = el.shadowRoot!.querySelector( '.dao-app-tab-bar .dao-app-tab:not(.active)'); expect(settingsButton).not.toBeNull(); settingsButton!.click(); - expect(mocks.callNative).toHaveBeenCalledWith( - 'openTab', {url: 'dao://settings/#dao'}); + await vi.waitFor(() => { + expect(mocks.callNative).toHaveBeenCalledWith('openAgentSettings'); + }); + expect(mocks.callNative).not.toHaveBeenCalledWith( + 'openTab', expect.anything()); expect(send).toHaveBeenCalledWith('closeSidebar'); }); + it('keeps the sidebar open when unified settings cannot open', async () => { + mocks.callNative.mockRejectedValueOnce(new Error('navigation failed')); + const {el, send} = await loadApp(); + const settingsButton = el.shadowRoot!.querySelector( + '.dao-app-tab-bar .dao-app-tab:not(.active)'); + expect(settingsButton).not.toBeNull(); + settingsButton!.click(); + + await vi.waitFor(() => { + expect(mocks.callNative).toHaveBeenCalledWith('openAgentSettings'); + }); + expect(send).not.toHaveBeenCalledWith('closeSidebar'); + }); + it('routes external prefill requests to the chat view', async () => { const {el} = await loadApp(); expect(el.shadowRoot?.querySelector('dao-chat-view')).not.toBeNull(); diff --git a/src/dao/browser/ui/webui/resources/agent/dao_agent_app.ts b/src/dao/browser/ui/webui/resources/agent/dao_agent_app.ts index a4515edb..e0f9267b 100644 --- a/src/dao/browser/ui/webui/resources/agent/dao_agent_app.ts +++ b/src/dao/browser/ui/webui/resources/agent/dao_agent_app.ts @@ -291,9 +291,16 @@ export class DaoAgentApp extends CrLitElement { } } - private openUnifiedSettings_() { - void callNative('openTab', {url: 'dao://settings/#dao'}); - chrome.send('closeSidebar'); + private async openUnifiedSettings_() { + try { + const result = await callNative('openAgentSettings') as + {success?: boolean}; + if (result.success) { + chrome.send('closeSidebar'); + } + } catch { + // Keep the sidebar open when native navigation is unavailable. + } } private onNewChatClick_() { diff --git a/src/patches/chrome/app/resources/generated_resources_zh-CN.xtb.patch b/src/patches/chrome/app/resources/generated_resources_zh-CN.xtb.patch index aa4fd646..e187ef43 100644 --- a/src/patches/chrome/app/resources/generated_resources_zh-CN.xtb.patch +++ b/src/patches/chrome/app/resources/generated_resources_zh-CN.xtb.patch @@ -1,7 +1,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/resources/generated_resources_zh-CN.xtb --- a/chrome/app/resources/generated_resources_zh-CN.xtb +++ b/chrome/app/resources/generated_resources_zh-CN.xtb -@@ -1,6 +1,114 @@ +@@ -1,6 +1,122 @@ @@ -36,6 +36,14 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res +显示调试信息 +智能体人格 +描述智能体应遵循的人格、原则和沟通方式。 ++恢复默认设置 ++已恢复默认人格 ++无法恢复默认人格。 ++请先启用记忆和 Dream 分析,再生成报告。 ++生成 Dream 报告 ++正在生成… ++Dream 报告已生成 ++无法生成 Dream 报告。 +使用当前页面作为上下文 +使用对话上下文 +网页搜索 @@ -116,7 +124,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 已与您共享该网络 点击即可切换权限。 标签页重新变为活动状态 -@@ -538,6 +644,7 @@ +@@ -538,6 +652,7 @@ 闲置标签页外观焕然一新 拼写和语法 您的设备可通过 Smart Lock 解锁。按 Enter 键即可解锁。 @@ -124,7 +132,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 手动添加 未使用 发生机械问题,请检查打印机 -@@ -894,6 +1001,7 @@ +@@ -894,6 +1009,7 @@ 此用户未与网域关联 与 Gemini 聊天,在 Google AI 的帮助下撰写内容、制定规划、学习新知识等等。 设置完成后,选择屏幕底部任务栏上的 Gemini 应用,即可开始使用 Gemini。 @@ -132,7 +140,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 可对所选项执行的更多操作 应用设置 青色 -@@ -1011,6 +1119,7 @@ +@@ -1011,6 +1127,7 @@ 打开新的无痕式窗口 大号鼠标光标 要开启光标浏览模式吗? @@ -140,7 +148,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 启用滑行输入 管理员已禁止更新此应用,所以此应用可能无法正常运行 网站通常会发送通知,告知您重大新闻或聊天消息。 -@@ -1117,6 +1226,7 @@ +@@ -1117,6 +1234,7 @@ 创建应用快捷方式 在新标签页中打开 Beta 版 @@ -148,7 +156,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 打开新的窗口(&N) 已退出全屏模式 将密码安全地保存到您的 Google 账号中,彻底为您免除再次输入的麻烦 -@@ -1362,6 +1472,7 @@ +@@ -1362,6 +1480,7 @@ 指纹 屏幕锁定 PIN 码 将 OneDrive 连接到“文件”应用即可从 Chromebook 中管理您存储的文档。您需要使用自己的 Microsoft 账号登录。 @@ -156,7 +164,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 更新 任意串行端口 读取您在所有登录过的设备上的浏览记录 -@@ -1620,6 +1731,7 @@ +@@ -1620,6 +1739,7 @@ Android 应用 自动关闭热点 已隐藏 @@ -164,7 +172,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res JavaScript 优化已停用。 上次检查时间: {COUNT,plural, =1{电话号码}other{# 个电话号码}} -@@ -1784,6 +1896,7 @@ +@@ -1784,6 +1904,7 @@ 反向滚动 每次访问时都询问 设置 密码,以便更轻松地登录 @@ -172,7 +180,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 报告问题 PIN 码或密码 在日出和日落时切换主题 -@@ -2350,6 +2463,7 @@ +@@ -2350,6 +2471,7 @@ 自定义工具栏按钮 有新版 Chrome 可用 用户名已复制到剪贴板 @@ -180,7 +188,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res {NUM_SITES,plural, =1{关闭了 1 个不安全的扩展程序}other{关闭了 {NUM_SITES} 个不安全的扩展程序}} 向上滑动即可开始使用 清理计算机 -@@ -2714,10 +2828,12 @@ +@@ -2714,10 +2836,12 @@ 崩溃 要使用网络“”,请先在下方连接互联网。 {NUM_GROUPS,plural, =1{删除分组}other{删除分组}} @@ -193,7 +201,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 提供方 登录“ 如需完成 Linux 设置,请更新 Chrome 操作系统并重试。 -@@ -2910,6 +3026,7 @@ +@@ -2910,6 +3034,7 @@ 加入并打开 工具和操作 每次浏览时都可畅享 Google 搜索功能和 Google 智能工具 @@ -201,7 +209,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 从分组中移除 与他人协作 当 Chromebook 处于离线状态且热点可用时 -@@ -4134,6 +4251,7 @@ +@@ -4134,6 +4259,7 @@ 翻译页面和快速解答时使用的语言 主题列表,这些主题是 Chrome 根据您近期的浏览记录推测的 学校账号 @@ -209,7 +217,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 您可以保留此分组,以便日后添加标签页;如果不再想访问它,也可以退出分组。 Chrome 颜色 打开您计算机的代理设置 -@@ -5570,6 +5688,7 @@ +@@ -5570,6 +5696,7 @@ 正在安装操作系统更新 麦克风静音 暂停或恢复面部控制 @@ -217,7 +225,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 通过打印服务器找到了 1 台打印机 撅起嘴唇 隐藏 PIN 码 -@@ -5937,9 +6056,11 @@ +@@ -5937,9 +6064,11 @@ 添加新卡 后退 固定 Gemini @@ -229,7 +237,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 发送关于标签栏的反馈 网站列表,这些网站已被您屏蔽,因为您不想让它们向其他网站建议广告 无痕式往返缓存版子框架: -@@ -7106,6 +7227,7 @@ +@@ -7106,6 +7235,7 @@ 已启用 收集的所有数据都会遵照 Google《隐私权政策》加以使用。 管理员已停用固件更新。 @@ -237,7 +245,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 您的管理员已禁止连接到此网络 禁止在新访问的网站上使用 JavaScript 优化工具。需要启用安全浏览功能 点击“自定义 Chrome” -@@ -8216,6 +8338,7 @@ +@@ -8216,6 +8346,7 @@ 运行 ChromeOS 诊断测试 SSL 客户端证书 不允许网站使用您的摄像头 @@ -245,7 +253,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res {NUM_PAGES,plural, =0{}=1{ 及另外 1 个标签页}other{ 及另外 # 个标签页}} Google 服务设置 上一项 -@@ -8242,10 +8365,12 @@ +@@ -8242,10 +8373,12 @@ 控制您正在投放的媒体 电源 选择其他资料 @@ -258,7 +266,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 尾号为 的 IBAN 请输入用户名 森林上空闪耀着明亮的北极光。 -@@ -9002,6 +9127,7 @@ +@@ -9002,6 +9135,7 @@ 此应用无响应。请选择“强制关闭”以关闭此应用。 自助服务终端和数字标牌设备注册已完成 如果您降低阈值,就可以做出轻微的动作。如果您提高阈值,可能需要做出更夸张的动作。 @@ -266,7 +274,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 按下“c”键表示松开鼠标按键 共享分组 系统默认文字转语音声音 -@@ -9130,6 +9256,7 @@ +@@ -9130,6 +9264,7 @@ 视频流畅性 {COUNT,plural, =1{有 {COUNT} 个密码仅保存在此设备上}other{有 {COUNT} 个密码仅保存在此设备上}} 出了点问题 @@ -274,7 +282,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 附近的所有联系人都可与您分享内容。仅当您接受后,系统才会开始传输内容。 请验证是您本人在操作 正在安装 Linux… -@@ -9610,6 +9737,7 @@ +@@ -9610,6 +9745,7 @@ (在 中)。 已屏蔽。时间表当前设为 - ,只能手动更新。 ChromeOS Shill(连接管理器)日志 @@ -282,7 +290,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 您想如何处理现有的资料数据? 停用 Linux 中所有正被转发的端口 个应用 -@@ -11030,6 +11158,7 @@ +@@ -11030,6 +11166,7 @@ 您可以右键点击任意标签页,然后选择“水平显示标签页”,将标签页移回顶部 打包扩展程序错误 邮箱已自动验证 @@ -290,7 +298,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 很抱歉,文件过大,计算机无法处理。 版本 由于无法验证您的密码,因此登录失败了。请与管理员联系或重试。 -@@ -11285,6 +11414,7 @@ +@@ -11285,6 +11422,7 @@ Crostini 麦克风使用权限 {PASSWORD_COUNT,plural, =1{有 1 个密码只保存到此设备上。若要在其他设备上使用它,请将它保存到您的 Google 账号中。此操作还会清理所有重复项。}other{有 {PASSWORD_COUNT} 个密码只保存到此设备上。若要在其他设备上使用它们,请将它们保存到您的 Google 账号中。此操作还会清理所有重复项。}} 已被禁止对 MIDI 设备进行控制和重新编程 @@ -298,7 +306,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 无法登录 请让家长批准安装“ 节省适量内存 -@@ -11433,6 +11563,7 @@ +@@ -11433,6 +11571,7 @@ 该网站正在跟踪您的位置 对您的书签、历史记录、密码及其他设置所做的更改将不再同步到您的 Google 账号。但是,您的现有数据依然会存储在您的 Google 账号中,而且您可以通过 Google 信息中心管理这些数据。 您的功能和扩展程序快捷字词 @@ -306,7 +314,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res Google 账号 启用自动扫描 更新程序 -@@ -11641,6 +11772,7 @@ +@@ -11641,6 +11780,7 @@ 不允许此设备运行虚拟机 您的连接在部分网络流量中不是私密连接 显示原始网页 @@ -314,7 +322,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res 未命名的文件夹 {NUM_ATTEMPTS,plural, =1{您还剩 1 次尝试机会。}other{您还剩 # 次尝试机会。}} 您想为 Chrome 操作系统启用 ChromeVox(内置屏幕阅读器)吗?如要启用,请同时按住两个音量键 5 秒钟。 -@@ -11708,6 +11840,7 @@ +@@ -11708,6 +11848,7 @@ 未选择“”。按搜索键 + 空格键即可选择。 添加受限用户 系统已根据企业政策屏蔽 @@ -322,7 +330,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res Wi-Fi SSID 管理 输入法 -@@ -11852,6 +11985,7 @@ +@@ -11852,6 +11993,7 @@ 不允许网站播放受保护内容 Google 文档 {COUNT,plural, =1{应用}other{# 个应用}} @@ -330,7 +338,7 @@ diff --git a/chrome/app/resources/generated_resources_zh-CN.xtb b/chrome/app/res {NUM_APPS,plural,offset:2 =1{管理员可以使用“”录制您的屏幕。录制开始时,您不会收到通知。}=2{管理员可以使用“”和“”录制您的屏幕。录制开始时,您不会收到通知。}=3{管理员可以使用“”“”和另外 1 个应用录制您的屏幕。录制开始时,您不会收到通知。}other{管理员可以使用“”“”和另外 # 个应用录制您的屏幕。录制开始时,您不会收到通知。}} 使用孩子的 Google 账号登录 {TAB_COUNT,plural, =1{正在共享 1 个标签页}other{正在共享 # 个标签页}} -@@ -12003,4 +12137,13 @@ +@@ -12003,4 +12145,13 @@ 同步标签页分组 检查(&N) 配置的政策不允许执行此操作。 diff --git a/src/patches/chrome/app/settings_strings.grdp.patch b/src/patches/chrome/app/settings_strings.grdp.patch index 708d5055..612c9914 100644 --- a/src/patches/chrome/app/settings_strings.grdp.patch +++ b/src/patches/chrome/app/settings_strings.grdp.patch @@ -164,7 +164,7 @@ diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp Sites listed below follow a custom setting instead of the default -@@ -3836,7 +3836,484 @@ +@@ -3836,7 +3836,508 @@ You and Google @@ -280,6 +280,15 @@ diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp + + Describe the personality, principles, and communication style the agent should follow. + ++ ++ Reset to default ++ ++ ++ Default persona restored ++ ++ ++ Could not restore the default persona. ++ + + Context + @@ -367,6 +376,21 @@ diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp + + Excluded domains, one per line + ++ ++ Enable memory and Dream analysis before generating a report. ++ ++ ++ Generate Dream report ++ ++ ++ Generating... ++ ++ ++ Dream report generated ++ ++ ++ Could not generate the Dream report. ++ + + Skills and data + @@ -649,7 +673,7 @@ diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp Google Profile photo -@@ -4473,40 +4935,40 @@ +@@ -4473,40 +4959,40 @@ Your organization turned off saving passwords @@ -702,7 +726,7 @@ diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp Go to notification settings -@@ -4516,21 +4978,21 @@ +@@ -4516,21 +5002,21 @@ Safe Browsing @@ -728,7 +752,7 @@ diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp {NUM_SITES, plural, =1 {You can stop this site from sending future notifications.} other {You can stop these sites from sending future notifications.}} -@@ -4558,31 +5020,31 @@ +@@ -4558,31 +5044,31 @@ Safe Browsing is off diff --git a/src/patches/chrome/browser/resources/settings/BUILD.gn.patch b/src/patches/chrome/browser/resources/settings/BUILD.gn.patch index 683a53cd..6d81c349 100644 --- a/src/patches/chrome/browser/resources/settings/BUILD.gn.patch +++ b/src/patches/chrome/browser/resources/settings/BUILD.gn.patch @@ -2,11 +2,12 @@ diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resourc index c2759ca2b0..c4e77c7225 100644 --- a/chrome/browser/resources/settings/BUILD.gn +++ b/chrome/browser/resources/settings/BUILD.gn -@@ -100,6 +100,8 @@ build_webui("build") { +@@ -100,6 +100,9 @@ build_webui("build") { "controls/settings_radio_group.ts", "controls/settings_slider.ts", "controls/settings_toggle_button.ts", + "dao_page/dao_agent_page.ts", ++ "dao_page/dao_agent_page_index.ts", + "dao_page/dao_page.ts", "downloads_page/downloads_page.ts", "glic_page/glic_page.ts", diff --git a/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page.html.patch b/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page.html.patch index 374f6558..d6d5c088 100644 --- a/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page.html.patch +++ b/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page.html.patch @@ -3,8 +3,15 @@ new file mode 100644 index 0000000000..0000000001 --- /dev/null +++ b/chrome/browser/resources/settings/dao_page/dao_agent_page.html -@@ -0,0 +1,1196 @@ +@@ -0,0 +1,1295 @@ + + -+ +
$i18n{daoAgentSettingsTitle}
+
+ $i18n{daoAgentSettingsDescription} @@ -454,12 +515,10 @@ index 0000000000..0000000001 +
+ + + -+
-+

-+ $i18n{daoAgentGroupDataAndManagement} -+

++ +
+
@@ -1198,5 +1298,4 @@ index 0000000000..0000000001 +
+ + -+
-+
++ diff --git a/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page.ts.patch b/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page.ts.patch index 4a874138..5f0ad225 100644 --- a/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page.ts.patch +++ b/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page.ts.patch @@ -3,7 +3,7 @@ new file mode 100644 index 0000000000..0000000001 --- /dev/null +++ b/chrome/browser/resources/settings/dao_page/dao_agent_page.ts -@@ -0,0 +1,811 @@ +@@ -0,0 +1,871 @@ +// Copyright 2026 Dao Browser Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. @@ -74,6 +74,8 @@ index 0000000000..0000000001 + agentBaseUrl_: {type: String, value: 'https://api.openai.com/v1'}, + agentModel_: {type: String, value: 'gpt-5'}, + agentSoul_: {type: String, value: ''}, ++ agentSoulResetSucceeded_: {type: Boolean, value: false}, ++ agentSoulResetError_: {type: Boolean, value: false}, + agentShowToolDetails_: {type: Boolean, value: false}, + agentDebugMode_: {type: Boolean, value: false}, + agentResumeLastSession_: {type: Boolean, value: true}, @@ -84,6 +86,9 @@ index 0000000000..0000000001 + agentMemoryEnabled_: {type: Boolean, value: true}, + agentDreamEnabled_: {type: Boolean, value: true}, + agentDreamDebug_: {type: Boolean, value: false}, ++ dreamRunning_: {type: Boolean, value: false}, ++ dreamRunSucceeded_: {type: Boolean, value: false}, ++ dreamRunError_: {type: Boolean, value: false}, + agentProactiveThreshold_: {type: String, value: 'balanced'}, + agentSearchSource_: {type: String, value: 'auto'}, + agentJinaApiKey_: {type: String, value: ''}, @@ -161,6 +166,8 @@ index 0000000000..0000000001 + declare private agentBaseUrl_: string; + declare private agentModel_: string; + declare private agentSoul_: string; ++ declare private agentSoulResetSucceeded_: boolean; ++ declare private agentSoulResetError_: boolean; + declare private agentShowToolDetails_: boolean; + declare private agentDebugMode_: boolean; + declare private agentResumeLastSession_: boolean; @@ -171,6 +178,9 @@ index 0000000000..0000000001 + declare private agentMemoryEnabled_: boolean; + declare private agentDreamEnabled_: boolean; + declare private agentDreamDebug_: boolean; ++ declare private dreamRunning_: boolean; ++ declare private dreamRunSucceeded_: boolean; ++ declare private dreamRunError_: boolean; + declare private agentProactiveThreshold_: string; + declare private agentSearchSource_: string; + declare private agentJinaApiKey_: string; @@ -716,9 +726,17 @@ index 0000000000..0000000001 + private async setAgentSetting_(key: string, value: string|null) { + const accepted = await this.agentSettingsBrowserProxy_.setSetting( + key, value); -+ if (accepted && value !== null) { ++ if (!accepted) { ++ return false; ++ } ++ if (value === null) { ++ const values = {...this.agentSettingsValues_}; ++ delete values[key]; ++ this.agentSettingsValues_ = values; ++ } else { + this.agentSettingsValues_ = {...this.agentSettingsValues_, [key]: value}; + } ++ return true; + } + + private onAgentBooleanSettingChange_(event: Event) { @@ -764,6 +782,48 @@ index 0000000000..0000000001 + 'dao_dream_excluded_domains', JSON.stringify(domains)); + } + ++ private async onResetAgentSoul_(): Promise { ++ this.agentSoulResetSucceeded_ = false; ++ this.agentSoulResetError_ = false; ++ try { ++ if (!await this.setAgentSetting_('dao_agent_soul', null)) { ++ this.agentSoulResetError_ = true; ++ return; ++ } ++ this.agentSoul_ = ''; ++ this.agentSoulResetSucceeded_ = true; ++ } catch { ++ this.agentSoulResetError_ = true; ++ } ++ } ++ ++ private isDreamRunDisabled_(memoryEnabled: boolean, dreamEnabled: boolean, ++ running: boolean): boolean { ++ return !memoryEnabled || !dreamEnabled || running; ++ } ++ ++ private async onRunDreamNow_(): Promise { ++ if (this.isDreamRunDisabled_( ++ this.agentMemoryEnabled_, this.agentDreamEnabled_, ++ this.dreamRunning_)) { ++ return; ++ } ++ this.dreamRunning_ = true; ++ this.dreamRunSucceeded_ = false; ++ this.dreamRunError_ = false; ++ try { ++ if (await this.agentSettingsBrowserProxy_.startManualDream()) { ++ this.dreamRunSucceeded_ = true; ++ } else { ++ this.dreamRunError_ = true; ++ } ++ } catch { ++ this.dreamRunError_ = true; ++ } finally { ++ this.dreamRunning_ = false; ++ } ++ } ++ + private onAgentToolGroupChange_(event: Event) { + const target = event.currentTarget as HTMLElement & {checked: boolean}; + const group = target.dataset['toolGroup']; diff --git a/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page_index.html.patch b/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page_index.html.patch new file mode 100644 index 00000000..7ab9be86 --- /dev/null +++ b/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page_index.html.patch @@ -0,0 +1,36 @@ +diff --git a/chrome/browser/resources/settings/dao_page/dao_agent_page_index.html b/chrome/browser/resources/settings/dao_page/dao_agent_page_index.html +new file mode 100644 +index 0000000000..0000000001 +--- /dev/null ++++ b/chrome/browser/resources/settings/dao_page/dao_agent_page_index.html +@@ -0,0 +1,30 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ diff --git a/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page_index.ts.patch b/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page_index.ts.patch new file mode 100644 index 00000000..73125c7f --- /dev/null +++ b/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_page_index.ts.patch @@ -0,0 +1,89 @@ +diff --git a/chrome/browser/resources/settings/dao_page/dao_agent_page_index.ts b/chrome/browser/resources/settings/dao_page/dao_agent_page_index.ts +new file mode 100644 +index 0000000000..0000000001 +--- /dev/null ++++ b/chrome/browser/resources/settings/dao_page/dao_agent_page_index.ts +@@ -0,0 +1,83 @@ ++// Copyright 2026 Dao Browser Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++import 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js'; ++import 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js'; ++import '/shared/settings/prefs/prefs.js'; ++import '../settings_page/settings_section.js'; ++import '../settings_shared.css.js'; ++ ++import type {CrViewManagerElement} from 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js'; ++import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; ++ ++import {routes} from '../route.js'; ++import {RouteObserverMixin, Router} from '../router.js'; ++import type {Route, SettingsRoutes} from '../router.js'; ++import type {SettingsPlugin} from '../settings_main/settings_plugin.js'; ++import {SearchableViewContainerMixin} from '../settings_page/searchable_view_container_mixin.js'; ++ ++import {getTemplate} from './dao_agent_page_index.html.js'; ++ ++export interface SettingsDaoAgentPageIndexElement { ++ $: { ++ viewManager: CrViewManagerElement, ++ }; ++} ++ ++const SettingsDaoAgentPageIndexElementBase = ++ SearchableViewContainerMixin(RouteObserverMixin(PolymerElement)); ++ ++export class SettingsDaoAgentPageIndexElement extends ++ SettingsDaoAgentPageIndexElementBase implements SettingsPlugin { ++ static get is() { ++ return 'settings-dao-agent-page-index'; ++ } ++ ++ static get template() { ++ return getTemplate(); ++ } ++ ++ static get properties() { ++ return { ++ prefs: Object, ++ routes_: { ++ type: Object, ++ value: () => routes, ++ }, ++ }; ++ } ++ ++ declare prefs: {[key: string]: any}; ++ declare private routes_: SettingsRoutes; ++ ++ override currentRouteChanged(newRoute: Route, oldRoute?: Route) { ++ super.currentRouteChanged(newRoute, oldRoute); ++ queueMicrotask(() => { ++ if (newRoute === routes.DAO_AGENT_DETAILS) { ++ this.$.viewManager.switchView( ++ 'agentSettings', 'no-animation', 'no-animation'); ++ } else if (newRoute === routes.DAO_AGENT || newRoute === routes.BASIC) { ++ this.$.viewManager.switchView( ++ 'parent', 'no-animation', 'no-animation'); ++ } ++ }); ++ } ++ ++ private shouldRenderDetails_(route: Route|null): boolean { ++ return route === routes.DAO_AGENT_DETAILS; ++ } ++ ++ private onOpenAgentSettings_() { ++ Router.getInstance().navigateTo(routes.DAO_AGENT_DETAILS); ++ } ++} ++ ++declare global { ++ interface HTMLElementTagNameMap { ++ 'settings-dao-agent-page-index': SettingsDaoAgentPageIndexElement; ++ } ++} ++ ++customElements.define( ++ SettingsDaoAgentPageIndexElement.is, SettingsDaoAgentPageIndexElement); diff --git a/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_settings_browser_proxy.ts.patch b/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_settings_browser_proxy.ts.patch index 9e90dbc5..a6407179 100644 --- a/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_settings_browser_proxy.ts.patch +++ b/src/patches/chrome/browser/resources/settings/dao_page/dao_agent_settings_browser_proxy.ts.patch @@ -3,7 +3,7 @@ new file mode 100644 index 0000000000..0000000001 --- /dev/null +++ b/chrome/browser/resources/settings/dao_page/dao_agent_settings_browser_proxy.ts -@@ -0,0 +1,177 @@ +@@ -0,0 +1,182 @@ +// Copyright 2026 Dao Browser Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. @@ -94,6 +94,7 @@ index 0000000000..0000000001 + clearAllMemory(): Promise; + getWorkspaceSummary(): Promise; + openWorkspace(): Promise; ++ startManualDream(): Promise; + getUsageStats(): Promise; + resetUsageStats(): Promise; +} @@ -124,6 +125,10 @@ index 0000000000..0000000001 + return sendWithPromise('openDaoAgentWorkspace'); + } + ++ startManualDream(): Promise { ++ return sendWithPromise('startManualDaoDream'); ++ } ++ + getUsageStats(): Promise { + return sendWithPromise('getDaoAgentUsageStats'); + } diff --git a/src/patches/chrome/browser/resources/settings/route.ts.patch b/src/patches/chrome/browser/resources/settings/route.ts.patch index e175e730..83d1cdb4 100644 --- a/src/patches/chrome/browser/resources/settings/route.ts.patch +++ b/src/patches/chrome/browser/resources/settings/route.ts.patch @@ -2,7 +2,7 @@ diff --git a/chrome/browser/resources/settings/route.ts b/chrome/browser/resourc index f3473b12d1..0000000001 100644 --- a/chrome/browser/resources/settings/route.ts +++ b/chrome/browser/resources/settings/route.ts -@@ -200,6 +200,13 @@ function createRoutes(): SettingsRoutes { +@@ -200,6 +200,15 @@ function createRoutes(): SettingsRoutes { r.SYNC_ADVANCED = r.SYNC.createChild('/syncSetup/advanced'); } - @@ -11,7 +11,9 @@ index f3473b12d1..0000000001 100644 + r.DAO = r.BASIC.createSection( + '/dao', 'dao', loadTimeData.getString('daoPageTitle')); + r.DAO_AGENT = r.BASIC.createSection( -+ '/agent', 'agent', loadTimeData.getString('daoAgentSettingsTitle')); ++ '/agentOverview', 'agent', ++ loadTimeData.getString('daoAgentSettingsTitle')); ++ r.DAO_AGENT_DETAILS = r.DAO_AGENT.createChild('/agent'); + } + if (visibility.ai !== false && loadTimeData.getBoolean('showAiPage')) { diff --git a/src/patches/chrome/browser/resources/settings/router_dao.ts.patch b/src/patches/chrome/browser/resources/settings/router_dao.ts.patch index 340f59c8..2e57762b 100644 --- a/src/patches/chrome/browser/resources/settings/router_dao.ts.patch +++ b/src/patches/chrome/browser/resources/settings/router_dao.ts.patch @@ -2,12 +2,13 @@ diff --git a/chrome/browser/resources/settings/router.ts b/chrome/browser/resour index 2977bf7cb1..0000000002 100644 --- a/chrome/browser/resources/settings/router.ts +++ b/chrome/browser/resources/settings/router.ts -@@ -20,6 +20,8 @@ export interface SettingsRoutes { +@@ -20,6 +20,9 @@ export interface SettingsRoutes { COMPARE: Route; COOKIES: Route; DEFAULT_BROWSER: Route; + DAO: Route; + DAO_AGENT: Route; ++ DAO_AGENT_DETAILS: Route; DOWNLOADS: Route; EDIT_DICTIONARY: Route; FONTS: Route; diff --git a/src/patches/chrome/browser/resources/settings/settings.ts.patch b/src/patches/chrome/browser/resources/settings/settings.ts.patch index 0c769362..49f7ab75 100644 --- a/src/patches/chrome/browser/resources/settings/settings.ts.patch +++ b/src/patches/chrome/browser/resources/settings/settings.ts.patch @@ -2,12 +2,14 @@ diff --git a/chrome/browser/resources/settings/settings.ts b/chrome/browser/reso index f221bf6cd2..0905c92e55 100644 --- a/chrome/browser/resources/settings/settings.ts +++ b/chrome/browser/resources/settings/settings.ts -@@ -75,6 +75,10 @@ export {SettingsCheckboxListEntryElement} from './controls/settings_checkbox_lis +@@ -75,6 +75,12 @@ export {SettingsCheckboxListEntryElement} from './controls/settings_checkbox_lis export {DefaultBrowserBrowserProxyImpl} from './default_browser_page/default_browser_browser_proxy.js'; export type {DefaultBrowserBrowserProxy, DefaultBrowserInfo} from './default_browser_page/default_browser_browser_proxy.js'; export {SettingsDefaultBrowserPageElement} from './default_browser_page/default_browser_page.js'; +export {DaoAgentSettingsBrowserProxyImpl} from './dao_page/dao_agent_settings_browser_proxy.js'; +export type {DaoAgentMemorySummary, DaoAgentSettingsBrowserProxy, DaoAgentSettingsSnapshot, DaoAgentUsageStats, DaoAgentWorkspaceActivity, DaoAgentWorkspaceSummary} from './dao_page/dao_agent_settings_browser_proxy.js'; ++export {SettingsDaoAgentPageElement} from './dao_page/dao_agent_page.js'; ++export {SettingsDaoAgentPageIndexElement} from './dao_page/dao_agent_page_index.js'; +export {DaoMcpBrowserProxyImpl, SettingsDaoPageElement} from './dao_page/dao_page.js'; +export type {DaoMcpBrowserProxy, DaoMcpStatus} from './dao_page/dao_page.js'; //
diff --git a/src/patches/chrome/browser/resources/settings/settings_main/settings_main.html.patch b/src/patches/chrome/browser/resources/settings/settings_main/settings_main.html.patch index a44debc7..320690e0 100644 --- a/src/patches/chrome/browser/resources/settings/settings_main/settings_main.html.patch +++ b/src/patches/chrome/browser/resources/settings/settings_main/settings_main.html.patch @@ -35,8 +35,8 @@ diff --git a/chrome/browser/resources/settings/settings_main/settings_main.html +
+ +
+ diff --git a/src/patches/chrome/browser/resources/settings/settings_main/settings_main.ts.patch b/src/patches/chrome/browser/resources/settings/settings_main/settings_main.ts.patch index 0f3da40e..27bdb0b8 100644 --- a/src/patches/chrome/browser/resources/settings/settings_main/settings_main.ts.patch +++ b/src/patches/chrome/browser/resources/settings/settings_main/settings_main.ts.patch @@ -1,10 +1,11 @@ diff --git a/chrome/browser/resources/settings/settings_main/settings_main.ts b/chrome/browser/resources/settings/settings_main/settings_main.ts --- a/chrome/browser/resources/settings/settings_main/settings_main.ts +++ b/chrome/browser/resources/settings/settings_main/settings_main.ts -@@ -17,6 +17,7 @@ +@@ -17,6 +17,8 @@ import '../on_startup_page/on_startup_page.js'; import '../people_page/people_page_index.js'; import '../performance_page/performance_page_index.js'; ++import '../dao_page/dao_agent_page_index.js'; +import '../dao_page/dao_page.js'; import '../privacy_page/privacy_page_index.js'; import '../reset_page/reset_profile_banner.js'; @@ -78,7 +79,7 @@ diff --git a/chrome/browser/resources/settings/settings_main/settings_main.ts b/ + this.rememberOverviewScroll_(); } -+ if (this.overviewMode_ || ++ if (this.overviewMode_ || route === routes.DAO_AGENT_DETAILS || + (routes.ADVANCED && routes.ADVANCED.contains(route))) { + await ensureLazyLoaded(); + } diff --git a/src/patches/chrome/browser/ui/BUILD.gn.patch b/src/patches/chrome/browser/ui/BUILD.gn.patch index ddfdaa68..f44a8d9d 100644 --- a/src/patches/chrome/browser/ui/BUILD.gn.patch +++ b/src/patches/chrome/browser/ui/BUILD.gn.patch @@ -2,15 +2,16 @@ diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 5ef79ef4e8bba..9c64e04ad9481 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn -@@ -25,6 +25,7 @@ import("//rlz/buildflags/buildflags.gni") +@@ -25,6 +25,8 @@ import("//rlz/buildflags/buildflags.gni") import("//services/screen_ai/buildflags/features.gni") import("//third_party/protobuf/proto_library.gni") import("//tools/metrics/generate_allowlist_from_histograms_file.gni") ++import("//dao/browser/dao_version.gni") +import("//dao/browser/ui/dao_ui_sources.gni") import("//ui/base/ui_features.gni") import("//ui/views/features.gni") -@@ -46,7 +47,7 @@ generate_allowlist_from_histograms_file("webui_name_variants") { +@@ -46,7 +48,7 @@ generate_allowlist_from_histograms_file("webui_name_variants") { # The two are already semantically a part of the same target as they allow # circular includes from each other. static_library("ui") { @@ -19,7 +20,7 @@ index 5ef79ef4e8bba..9c64e04ad9481 100644 "accelerator_utils.h", "browser_ui_prefs.cc", "browser_ui_prefs.h", -@@ -257,7 +258,7 @@ static_library("ui") { +@@ -257,7 +259,7 @@ static_library("ui") { "//chrome/browser/ui/webui/subresource_filter", ] @@ -28,7 +29,7 @@ index 5ef79ef4e8bba..9c64e04ad9481 100644 ":browser_element_identifiers", ":ui_features", ":url_identity", -@@ -6021,3 +6022,26 @@ if (use_aura) { +@@ -6021,3 +6023,29 @@ if (use_aura) { ] } } @@ -36,7 +37,10 @@ index 5ef79ef4e8bba..9c64e04ad9481 100644 +source_set("dao_browser_tests") { + testonly = true + sources = dao_browser_ui_test_sources -+ defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] ++ defines = [ ++ "DAO_MCP_VERSION=\"$dao_display_version\"", ++ "HAS_OUT_OF_PROC_TEST_RUNNER", ++ ] + deps = dao_browser_ui_test_deps + [ + ":ui", + "//base", diff --git a/src/patches/chrome/browser/ui/views/javascript_tab_modal_dialog_view_views.cc.patch b/src/patches/chrome/browser/ui/views/javascript_tab_modal_dialog_view_views.cc.patch index 786f869b..736180ed 100644 --- a/src/patches/chrome/browser/ui/views/javascript_tab_modal_dialog_view_views.cc.patch +++ b/src/patches/chrome/browser/ui/views/javascript_tab_modal_dialog_view_views.cc.patch @@ -68,17 +68,18 @@ index e0c7c6df6f..db97367c4b 100644 SetAcceptCallback(base::BindOnce( [](JavaScriptTabModalDialogViewViews* dialog) { -@@ -135,8 +177,9 @@ JavaScriptTabModalDialogViewViews::JavaScriptTabModalDialogViewViews( +@@ -135,8 +177,10 @@ JavaScriptTabModalDialogViewViews::JavaScriptTabModalDialogViewViews( message_box_view_ = new views::MessageBoxView( message_text, /* detect_directionality = */ true); ++ dao::ConfigureDaoSystemDialogContent(message_box_view_); if (dialog_type == content::JAVASCRIPT_DIALOG_TYPE_PROMPT) { message_box_view_->SetPromptField(default_prompt_text); + ConfigureDaoPromptTextfield(message_box_view_->GetVisiblePromptField()); } SetLayoutManager(std::make_unique()); -@@ -151,10 +193,23 @@ JavaScriptTabModalDialogViewViews::CreateAlertDialogForTesting( +@@ -151,10 +194,23 @@ JavaScriptTabModalDialogViewViews::CreateAlertDialogForTesting( Browser* browser, std::u16string title, std::u16string message) { diff --git a/src/patches/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc.patch b/src/patches/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc.patch index a008cfc9..35c73eb5 100644 --- a/src/patches/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc.patch +++ b/src/patches/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc.patch @@ -45,7 +45,7 @@ diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provide std::u16string license = l10n_util::GetStringFUTF16( IDS_VERSION_UI_LICENSE, chrome::kChromiumProjectURL, -@@ -4145,6 +4153,230 @@ +@@ -4145,6 +4153,242 @@ IDS_SETTINGS_EXTENSIONS_CHECKBOX_LABEL); } @@ -108,6 +108,11 @@ diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provide + {"daoAgentSoulTitle", IDS_SETTINGS_DAO_AGENT_SOUL_TITLE}, + {"daoAgentSoulDescription", + IDS_SETTINGS_DAO_AGENT_SOUL_DESCRIPTION}, ++ {"daoAgentSoulReset", IDS_SETTINGS_DAO_AGENT_SOUL_RESET}, ++ {"daoAgentSoulResetSuccess", ++ IDS_SETTINGS_DAO_AGENT_SOUL_RESET_SUCCESS}, ++ {"daoAgentSoulResetError", ++ IDS_SETTINGS_DAO_AGENT_SOUL_RESET_ERROR}, + {"daoAgentContextTitle", IDS_SETTINGS_DAO_AGENT_CONTEXT_TITLE}, + {"daoAgentPageContextLabel", + IDS_SETTINGS_DAO_AGENT_PAGE_CONTEXT_LABEL}, @@ -150,6 +155,13 @@ diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provide + IDS_SETTINGS_DAO_AGENT_DREAM_DEBUG_LABEL}, + {"daoAgentDreamExcludedDomainsLabel", + IDS_SETTINGS_DAO_AGENT_DREAM_EXCLUDED_DOMAINS_LABEL}, ++ {"daoAgentDreamRunDescription", ++ IDS_SETTINGS_DAO_AGENT_DREAM_RUN_DESCRIPTION}, ++ {"daoAgentDreamRunNow", IDS_SETTINGS_DAO_AGENT_DREAM_RUN_NOW}, ++ {"daoAgentDreamRunning", IDS_SETTINGS_DAO_AGENT_DREAM_RUNNING}, ++ {"daoAgentDreamRunSuccess", ++ IDS_SETTINGS_DAO_AGENT_DREAM_RUN_SUCCESS}, ++ {"daoAgentDreamRunError", IDS_SETTINGS_DAO_AGENT_DREAM_RUN_ERROR}, + {"daoAgentDataTitle", IDS_SETTINGS_DAO_AGENT_DATA_TITLE}, + {"daoAgentManageSkills", IDS_SETTINGS_DAO_AGENT_MANAGE_SKILLS}, + {"daoAgentInspectMemory", IDS_SETTINGS_DAO_AGENT_INSPECT_MEMORY}, @@ -276,7 +288,7 @@ diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provide void AddSecurityKeysStrings(content::WebUIDataSource* html_source) { static constexpr webui::LocalizedString kSecurityKeysStrings[] = { {"securityBundleResetToDefaultsButtonLabel", -@@ -4294,6 +4521,7 @@ +@@ -4294,6 +4533,7 @@ AddClearBrowsingDataStrings(html_source, profile); AddCommonStrings(html_source, profile); AddDownloadsStrings(html_source); diff --git a/src/patches/chrome/test/data/webui/settings/BUILD.gn.patch b/src/patches/chrome/test/data/webui/settings/BUILD.gn.patch index 421205c2..021e41e4 100644 --- a/src/patches/chrome/test/data/webui/settings/BUILD.gn.patch +++ b/src/patches/chrome/test/data/webui/settings/BUILD.gn.patch @@ -2,11 +2,12 @@ diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/s index 9021964823e24..0bd8989228d7d 100644 --- a/chrome/test/data/webui/settings/BUILD.gn +++ b/chrome/test/data/webui/settings/BUILD.gn -@@ -51,6 +51,8 @@ build_webui_tests("build") { +@@ -51,6 +51,9 @@ build_webui_tests("build") { "cr_policy_pref_indicator_test.ts", "do_not_track_toggle_test.ts", "downloads_page_test.ts", + "dao_page_test.ts", ++ "dao_agent_page_index_test.ts", + "dao_agent_page_test.ts", "dropdown_menu_test.ts", "extension_controlled_indicator_test.ts", diff --git a/src/patches/chrome/test/data/webui/settings/dao_agent_page_index_test.ts.patch b/src/patches/chrome/test/data/webui/settings/dao_agent_page_index_test.ts.patch new file mode 100644 index 00000000..8ce010c1 --- /dev/null +++ b/src/patches/chrome/test/data/webui/settings/dao_agent_page_index_test.ts.patch @@ -0,0 +1,46 @@ +diff --git a/chrome/test/data/webui/settings/dao_agent_page_index_test.ts b/chrome/test/data/webui/settings/dao_agent_page_index_test.ts +new file mode 100644 +index 0000000000..0000000001 +--- /dev/null ++++ b/chrome/test/data/webui/settings/dao_agent_page_index_test.ts +@@ -0,0 +1,40 @@ ++// Copyright 2026 Dao Browser Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++import 'chrome://settings/settings.js'; ++ ++import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; ++import type {SettingsDaoAgentPageIndexElement} from 'chrome://settings/settings.js'; ++import {resetRouterForTesting, Router, routes} from 'chrome://settings/settings.js'; ++import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; ++ ++suite('DaoAgentPageIndexTest', function() { ++ let page: SettingsDaoAgentPageIndexElement; ++ ++ setup(function() { ++ document.body.innerHTML = window.trustedTypes!.emptyHTML; ++ resetRouterForTesting(); ++ Router.getInstance().navigateTo(routes.BASIC); ++ page = document.createElement('settings-dao-agent-page-index'); ++ document.body.appendChild(page); ++ flush(); ++ }); ++ ++ teardown(function() { ++ page.remove(); ++ }); ++ ++ test('rendersCompactOverviewEntry', function() { ++ const parent = page.$.viewManager.querySelector('#parent'); ++ assertTrue(!!parent); ++ assertTrue(!!parent.querySelector('#openAgentSettings')); ++ assertFalse(!!parent.querySelector('settings-dao-agent-page')); ++ }); ++ ++ test('overviewEntryNavigatesToDetailsRoute', function() { ++ page.shadowRoot!.querySelector('#openAgentSettings')!.click(); ++ assertEquals( ++ routes.DAO_AGENT_DETAILS, Router.getInstance().getCurrentRoute()); ++ }); ++}); diff --git a/src/patches/chrome/test/data/webui/settings/dao_agent_page_test.ts.patch b/src/patches/chrome/test/data/webui/settings/dao_agent_page_test.ts.patch index 1540d508..2f3a7cce 100644 --- a/src/patches/chrome/test/data/webui/settings/dao_agent_page_test.ts.patch +++ b/src/patches/chrome/test/data/webui/settings/dao_agent_page_test.ts.patch @@ -3,24 +3,24 @@ new file mode 100644 index 0000000000..0000000001 --- /dev/null +++ b/chrome/test/data/webui/settings/dao_agent_page_test.ts -@@ -0,0 +1,792 @@ +@@ -0,0 +1,891 @@ +// Copyright 2026 Dao Browser Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + -+import 'chrome://settings/dao_page/dao_agent_page.js'; ++import 'chrome://settings/settings.js'; + +import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js'; +import {webUIListenerCallback} from 'chrome://resources/js/cr.js'; +import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; -+import type {SettingsDaoAgentPageElement} from 'chrome://settings/dao_page/dao_agent_page.js'; ++import type {SettingsDaoAgentPageElement} from 'chrome://settings/settings.js'; +import { + DaoAgentSettingsBrowserProxyImpl, -+} from 'chrome://settings/dao_page/dao_agent_settings_browser_proxy.js'; ++} from 'chrome://settings/settings.js'; +import type { + DaoAgentSettingsBrowserProxy, + DaoAgentSettingsSnapshot, -+} from 'chrome://settings/dao_page/dao_agent_settings_browser_proxy.js'; ++} from 'chrome://settings/settings.js'; +import {loadTimeData} from 'chrome://settings/settings.js'; +import { + assertDeepEquals, @@ -90,6 +90,7 @@ index 0000000000..0000000001 + }), + dao_agent_memory_enabled: 'true', + dao_dream_enabled: 'false', ++ dao_agent_soul: 'Custom soul', + }, + usageStats: { + apiCalls: 0, @@ -106,6 +107,7 @@ index 0000000000..0000000001 + private usageStatsRequests_: Array> = []; + private clearMemoryRequests_: Array> = []; + private resetUsageRequests_: Array> = []; ++ private manualDreamRequests_: Array> = []; + private setSettingRequests_: DeferredSetting[] = []; + private deferSettingWrites_ = false; + private rejectNextSettingsRequest_ = false; @@ -120,6 +122,7 @@ index 0000000000..0000000001 + 'openWorkspace', + 'getUsageStats', + 'resetUsageStats', ++ 'startManualDream', + ]); + } + @@ -135,7 +138,11 @@ index 0000000000..0000000001 + setSetting(key: string, value: string|null): Promise { + this.methodCalled('setSetting', key, value); + if (!this.deferSettingWrites_) { -+ if (value !== null) { ++ if (value === null) { ++ const values = {...this.snapshot_.values}; ++ delete values[key]; ++ this.snapshot_.values = values; ++ } else { + this.snapshot_.values = {...this.snapshot_.values, [key]: value}; + } + return Promise.resolve(true); @@ -185,6 +192,13 @@ index 0000000000..0000000001 + }); + } + ++ startManualDream(): Promise { ++ this.methodCalled('startManualDream'); ++ return new Promise((resolve, reject) => { ++ this.manualDreamRequests_.push({resolve, reject}); ++ }); ++ } ++ + resolveMemorySummary(summary: unknown, request = 0): void { + const deferred = this.memorySummaryRequests_[request]; + assertTrue(!!deferred); @@ -251,6 +265,18 @@ index 0000000000..0000000001 + deferred.reject(new Error('reset usage failed')); + } + ++ resolveManualDream(success: boolean): void { ++ const deferred = this.manualDreamRequests_.shift(); ++ assertTrue(!!deferred); ++ deferred.resolve(success); ++ } ++ ++ rejectManualDream(): void { ++ const deferred = this.manualDreamRequests_.shift(); ++ assertTrue(!!deferred); ++ deferred.reject(new Error('manual dream failed')); ++ } ++ + deferSettingWrites(): void { + this.deferSettingWrites_ = true; + } @@ -292,9 +318,19 @@ index 0000000000..0000000001 + page.remove(); + }); + -+ test('rendersAsTopLevelAgentSectionWithoutSubpageBackControl', function() { -+ assertTrue(!!page.shadowRoot!.querySelector('settings-section')); -+ assertNull(page.shadowRoot!.querySelector('settings-subpage')); ++ test('rendersFiveVerticalSettingsSections', function() { ++ const sections = Array.from( ++ page.shadowRoot!.querySelectorAll( ++ ':scope > settings-section')); ++ assertDeepEquals([ ++ 'modelAndConnection', ++ 'behaviorAndContext', ++ 'capabilities', ++ 'learningAndAnalysis', ++ 'dataAndManagement', ++ ], sections.map(section => section.id)); ++ assertNull(page.shadowRoot!.querySelector( ++ '[role=tablist], .section-navigation, .section-rail')); + }); + + async function resolveAllManagementSummaries(): Promise { @@ -329,6 +365,9 @@ index 0000000000..0000000001 + 'daoAgentJinaApiKey', + 'daoAgentThreshold', + 'daoAgentDreamExcludedDomains', ++ 'resetAgentSoulButton', ++ 'runDreamNowButton', ++ 'openDreamHistoryLink', + 'daoAgentMemoryManagement', + 'daoAgentWorkspaceManagement', + 'daoAgentUsageManagement', @@ -343,6 +382,66 @@ index 0000000000..0000000001 + assertTrue(!!page.shadowRoot!.querySelector('[data-tool-name]')); + }); + ++ test('resetSoulRestoresTheRuntimeDefault', async function() { ++ const textarea = page.shadowRoot!.querySelector( ++ '#daoAgentSoul')!; ++ assertEquals('Custom soul', textarea.value); ++ ++ page.shadowRoot!.querySelector('#resetAgentSoulButton')! ++ .click(); ++ const args = await agentSettingsBrowserProxy.whenCalled('setSetting'); ++ assertEquals('dao_agent_soul', args[0]); ++ assertNull(args[1]); ++ await flushTasks(); ++ ++ assertEquals('', textarea.value); ++ assertTrue(!!page.shadowRoot!.querySelector('#agentSoulResetSuccess')); ++ }); ++ ++ test('manualDreamRequiresEnabledDependenciesAndReportsSuccess', ++ async function() { ++ const button = page.shadowRoot!.querySelector('#runDreamNowButton')!; ++ assertTrue(button.disabled); ++ ++ const dreamToggle = page.shadowRoot!.querySelector('[data-setting="dao_dream_enabled"]')!; ++ dreamToggle.checked = true; ++ dreamToggle.dispatchEvent(new Event('change')); ++ await flushTasks(); ++ assertFalse(button.disabled); ++ ++ button.click(); ++ await agentSettingsBrowserProxy.whenCalled('startManualDream'); ++ assertTrue(button.disabled); ++ agentSettingsBrowserProxy.resolveManualDream(true); ++ await flushTasks(); ++ flush(); ++ ++ assertFalse(button.disabled); ++ assertTrue(!!page.shadowRoot!.querySelector('#dreamRunSuccess')); ++ }); ++ ++ test('manualDreamFailureUsesActionSpecificFeedback', async function() { ++ const dreamToggle = page.shadowRoot!.querySelector('[data-setting="dao_dream_enabled"]')!; ++ dreamToggle.checked = true; ++ dreamToggle.dispatchEvent(new Event('change')); ++ await flushTasks(); ++ ++ page.shadowRoot!.querySelector('#runDreamNowButton')!.click(); ++ await agentSettingsBrowserProxy.whenCalled('startManualDream'); ++ agentSettingsBrowserProxy.rejectManualDream(); ++ await flushTasks(); ++ flush(); ++ ++ assertTrue(!!page.shadowRoot!.querySelector('#dreamRunError')); ++ assertNull(page.shadowRoot!.querySelector('#dreamRunSuccess')); ++ }); ++ + test('agentManagementSummariesLoadIndependently', async function() { + assertEquals( + 1, agentSettingsBrowserProxy.getCallCount('getMemorySummary')); diff --git a/src/patches/chrome/test/data/webui/settings/settings_browsertest.cc.patch b/src/patches/chrome/test/data/webui/settings/settings_browsertest.cc.patch index b8160c91..40467dc8 100644 --- a/src/patches/chrome/test/data/webui/settings/settings_browsertest.cc.patch +++ b/src/patches/chrome/test/data/webui/settings/settings_browsertest.cc.patch @@ -2,7 +2,7 @@ diff --git a/chrome/test/data/webui/settings/settings_browsertest.cc b/chrome/te index 382856939e..0000000001 100644 --- a/chrome/test/data/webui/settings/settings_browsertest.cc +++ b/chrome/test/data/webui/settings/settings_browsertest.cc -@@ -253,6 +253,18 @@ IN_PROC_BROWSER_TEST_F(SettingsTest, DownloadsPage) { +@@ -253,6 +253,22 @@ IN_PROC_BROWSER_TEST_F(SettingsTest, DownloadsPage) { RunTest("settings/downloads_page_test.js", "mocha.run()"); } - @@ -18,6 +18,10 @@ index 382856939e..0000000001 100644 +IN_PROC_BROWSER_TEST_F(SettingsTest, DISABLED_DaoAgentPage) { + RunTest("settings/dao_agent_page_test.js", "mocha.run()"); +} ++ ++IN_PROC_BROWSER_TEST_F(SettingsTest, DISABLED_DaoAgentPageIndex) { ++ RunTest("settings/dao_agent_page_index_test.js", "mocha.run()"); ++} + IN_PROC_BROWSER_TEST_F(SettingsTest, DropdownMenu) { RunTest("settings/dropdown_menu_test.js", "mocha.run()"); diff --git a/src/patches/chrome/test/data/webui/settings/settings_main_test.ts.patch b/src/patches/chrome/test/data/webui/settings/settings_main_test.ts.patch index 6268f4c2..88e83c34 100644 --- a/src/patches/chrome/test/data/webui/settings/settings_main_test.ts.patch +++ b/src/patches/chrome/test/data/webui/settings/settings_main_test.ts.patch @@ -12,7 +12,7 @@ index 4de6fefb8b..0000000001 100644 import {TestSearchManager} from './test_search_manager.js'; // clang-format on -@@ -35,6 +36,99 @@ suite('MainPageTests', function() { +@@ -35,6 +36,101 @@ suite('MainPageTests', function() { settingsMain.remove(); }); @@ -27,6 +27,8 @@ index 4de6fefb8b..0000000001 100644 + assertTrue(Array.from(activeViews).some(view => view.id === 'dao')); + assertTrue(Array.from(activeViews).some(view => view.id === 'agent')); + assertTrue(!!settingsMain.shadowRoot!.querySelector( ++ '#switcher > #agent settings-dao-agent-page-index')); ++ assertFalse(!!settingsMain.shadowRoot!.querySelector( + '#switcher > #agent settings-dao-agent-page')); + + const privacyIndex = settingsMain.shadowRoot!.querySelector< @@ -49,13 +51,13 @@ index 4de6fefb8b..0000000001 100644 + + test('secondaryRouteReplacesOverviewAndReturnRestoresIt', async function() { + await settingsMain.whenViewSwitchingDone(); -+ Router.getInstance().navigateTo(routes.SITE_SETTINGS); ++ Router.getInstance().navigateTo(routes.DAO_AGENT_DETAILS); + await settingsMain.whenViewSwitchingDone(); + + let activeViews = settingsMain.shadowRoot!.querySelectorAll( + '#switcher > [slot=view].active'); + assertEquals(1, activeViews.length); -+ assertEquals('privacy', activeViews[0]!.id); ++ assertEquals('agent', activeViews[0]!.id); + assertFalse(settingsMain.hasAttribute('overview-mode')); + + Router.getInstance().navigateTo(routes.BASIC); diff --git a/src/patches/chrome/test/data/webui/settings/settings_menu_test.ts.patch b/src/patches/chrome/test/data/webui/settings/settings_menu_test.ts.patch index 2dac1fc0..68ad0b55 100644 --- a/src/patches/chrome/test/data/webui/settings/settings_menu_test.ts.patch +++ b/src/patches/chrome/test/data/webui/settings/settings_menu_test.ts.patch @@ -77,8 +77,8 @@ index df9cb79..6fc229b 100644 - assertTrue(!!selector.selected); - assertEquals('/reset', selector.selected.toString()); + test('selects parent section for a secondary route', function() { -+ Router.getInstance().navigateTo(routes.SITE_SETTINGS); -+ assertEquals('privacy', settingsMenu.$.menu.selected); ++ Router.getInstance().navigateTo(routes.DAO_AGENT_DETAILS); ++ assertEquals('agent', settingsMenu.$.menu.selected); }); - test('navigateToAnotherSection', async function() {