Skip to content

feat(dream): add one-minute recap report - #46

Merged
moonrailgun merged 1 commit into
mainfrom
improve-dream-report
Aug 8, 2026
Merged

feat(dream): add one-minute recap report#46
moonrailgun merged 1 commit into
mainfrom
improve-dream-report

Conversation

@moonrailgun

@moonrailgun moonrailgun commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Background

Dream reports needed a richer one-minute recap experience that surfaces daily activity, measured focus rhythm, themes, weekly context, and memory candidates without writing unconfirmed model suggestions directly to long-term memory.

Changes

  • Redesign dao://dream around a responsive recap layout with activity heatmap, shared daily/weekly history rail, summary, rhythm, themes, stats, memory candidates, and folded full report.
  • Extend Dream material collection with measured foreground seconds by time bucket and uncapped aggregate signal counts while preserving material caps.
  • Persist structured recap data in existing report material stats and keep habit candidates pending until explicit user confirmation.
  • Add weekly report rendering, image sharing support, viewed marking, and localized English/Chinese copy.
  • Update Dream feature documentation, regression checklist, and design QA notes.

Testing

Patch adds C++ store/browser tests and WebUI runner/app tests for session counting, annual report history, uncapped stats, structured recap validation, measured rhythm buckets, weekly history selection, legacy markdown fallback, and non-destructive memory rejection. Design QA notes report npm run test:webui passed with 61 test files and 728 tests; browser-rendered visual comparison remains blocked by unrelated Chromium import failures.

Summary by CodeRabbit

  • New Features
    • Added daily and weekly Dream reports with structured recaps, activity heatmaps, rhythm insights, themes, and statistics.
    • Added report history, legacy-report support, sharing, image export, and responsive dark-mode presentation.
    • Added localized English and Simplified Chinese interface text.
    • Dream insights can now suggest memory candidates for user confirmation instead of saving them automatically.
  • Documentation
    • Expanded Dream feature documentation and validation checklists.
  • Bug Fixes
    • Improved report data normalization, history handling, and recap validation.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Dream feature now collects bucketed activity metrics, generates structured daily recaps, supports daily and weekly report history, defers habit persistence until confirmation, and provides redesigned responsive report views with expanded tests and localization.

Changes

Dream report pipeline

Layer / File(s) Summary
Collect independent Dream metrics
src/dao/browser/agent/dao_agent_memory_*, src/dao/browser/agent/dao_dream_material_collector.*, src/dao/browser/agent/dao_dream_browsertest.cc
The collector records foreground seconds by local-time bucket and obtains uncapped domain, query, and conversation-session counts. Report history retrieval now supports up to 371 daily records.
Generate and persist structured recaps
src/dao/browser/ui/webui/resources/agent/dao_dream_runner.ts, src/dao/browser/agent/dao_dream_service.*, src/dao/browser/agent/dao_dream_browsertest.cc, docs/features.md, docs/feature-checklist.md
Dream results now include normalized summaries, time buckets, and themes. Recaps persist in report statistics. Habits remain report candidates until confirmation.
Render daily and weekly reports
src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts, src/dao/browser/ui/webui/resources/agent/__tests__/*, src/dao/browser/ui/webui/resources/agent/i18n/locales/*, design-qa.md
The WebUI loads, normalizes, selects, renders, and exports daily and weekly reports. It adds activity heatmaps, rhythm, themes, responsive layouts, localization, and legacy-markdown fallback handling. Design QA records blocked browser-rendered fidelity verification.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DaoDreamApp
  participant GetDreamReports
  participant WeeklyReportAPI
  participant ReportRenderer
  DaoDreamApp->>GetDreamReports: load daily report history
  DaoDreamApp->>WeeklyReportAPI: load weekly report history
  GetDreamReports-->>DaoDreamApp: return daily reports
  WeeklyReportAPI-->>DaoDreamApp: return weekly reports
  DaoDreamApp->>ReportRenderer: render the selected report
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: adding a one-minute Dream recap report.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch improve-dream-report

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/dao/browser/agent/dao_dream_browsertest.cc (1)

4551-4568: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

确保失败路径退出 verify_loop

如果 r 为空,Line 4554 的 ASSERT_TRUE 会提前返回。verify_loop.Quit() 不会执行。该浏览器测试会挂起直到超时。先调用 verify_loop.Quit(),再验证结果。

建议修复
       runner.last_request().period_start,
       base::BindLambdaForTesting([&](std::optional<DreamReport> r) {
+        verify_loop.Quit();
         ASSERT_TRUE(r.has_value());
         EXPECT_EQ("completed", r->status);
         EXPECT_EQ("manual", r->trigger_kind);
         EXPECT_EQ("# nightly report", r->report_markdown);
@@
         const base::DictValue* stored_recap =
             material_stats->GetDict().FindDict("recap");
         ASSERT_TRUE(stored_recap);
         EXPECT_EQ("A focused day.", *stored_recap->FindString("summary"));
-        verify_loop.Quit();
       }));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dao/browser/agent/dao_dream_browsertest.cc` around lines 4551 - 4568, 在
GetDreamReportByDate 的回调中,先调用 verify_loop.Quit(),再执行 ASSERT_TRUE(r.has_value())
及后续断言,确保 r 为空等失败路径也能退出等待循环并避免测试挂起。
🧹 Nitpick comments (1)
src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts (1)

585-613: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the superseded legacy style block.

The stylesheet now declares two full themes. :host, .page, main, .history-layout, .history-list, .history-item, .markdown, .report-body, and .date each appear twice, and there are two @media (max-width: 640px) blocks (Line 540 and Line 1314).

The redesign wins by cascade order, so rendering is mostly correct. Two side effects remain:

  • At widths ≤640px the legacy rule .history-layout { display: block; } (Line 568-570) still applies. The new .history-layout { gap: 20px } is then ignored, so the rail and body lose their intended spacing.
  • The legacy .history-list { margin-bottom: 22px; border-right: 0; } (Line 572-577) still applies on top of the new .history-list rules.

Delete the legacy declarations that the redesign replaces. Keep only the rules that the new layout needs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts` around lines 585 -
613, Remove the superseded legacy style block in the stylesheet, including
duplicate :host, .page, main, .history-layout, .history-list, .history-item,
.markdown, .report-body, .date, and the earlier max-width: 640px media rules.
Preserve only the redesign’s declarations so .history-layout retains its gap and
.history-list no longer inherits legacy margin-bottom or border-right overrides.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@design-qa.md`:
- Around line 3-6: Remove the personal absolute filesystem path from the “Source
visual truth path” section in design-qa.md and replace it with a
repository-relative path to the version-controlled design artifact, or a
non-personal instruction for accessing the artifact without exposing a username.

In `@src/dao/browser/ui/webui/resources/agent/__tests__/dao_dream_app.test.ts`:
- Line 272: Make the formatted-date assertion in dao_dream_app.test.ts
locale-independent by configuring Vitest to use a fixed English locale, or by
asserting against a locale-independent representation instead of the hard-coded
English text. Preserve coverage of formatDreamDate_ and ensure the test passes
consistently across runner locales.

In `@src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts`:
- Around line 2053-2080: Remove the role="grid" and role="gridcell" attributes
from the activity heatmap elements in the cell-generation and heatmap-grid
markup, while retaining the buttons, their existing labels, and the heatmap
region’s aria-label.
- Around line 1420-1442: Update loadHistory_ so getWeeklyDreamReports failure or
timeout degrades to an empty weekly-report list instead of rejecting the
Promise.all operation; preserve successful daily report loading and avoid
clearing reports_ or setting the main error state when only the weekly request
fails.
- Around line 2586-2588: Update renderMarkdown_ so the Markdown output from
marked.parse is sanitized with DOMPurify.sanitize before being returned or
assigned via innerHTML. Ensure the weeklyReportMarkdown_ model-generated fields
remain rendered while both the parsed-Markdown path and fallback output are
safe.

In `@src/dao/browser/ui/webui/resources/agent/i18n/locales/en.ts`:
- Line 376: Update the dream.page.history_recent translation from “days” to a
neutral noun such as “reports” in both en.ts and zh-CN.ts, and update the
matching expected string in the dao_dream_app.test.ts fixture; keep
renderHistoryList_ behavior unchanged.

In `@src/dao/browser/ui/webui/resources/agent/i18n/locales/zh-CN.ts`:
- Around line 280-293: 统一昨日梦境报告中的时间指代:在 zh-CN 本地化条目
dream.page.rhythm_title、dream.page.theme_fallback 和 dream.page.stats_label
中,将“今日”改为“昨日”或移除时间指代,保持与 dream.page.recap_eyebrow 和 dream.page.recap_title 一致。

---

Outside diff comments:
In `@src/dao/browser/agent/dao_dream_browsertest.cc`:
- Around line 4551-4568: 在 GetDreamReportByDate 的回调中,先调用 verify_loop.Quit(),再执行
ASSERT_TRUE(r.has_value()) 及后续断言,确保 r 为空等失败路径也能退出等待循环并避免测试挂起。

---

Nitpick comments:
In `@src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts`:
- Around line 585-613: Remove the superseded legacy style block in the
stylesheet, including duplicate :host, .page, main, .history-layout,
.history-list, .history-item, .markdown, .report-body, .date, and the earlier
max-width: 640px media rules. Preserve only the redesign’s declarations so
.history-layout retains its gap and .history-list no longer inherits legacy
margin-bottom or border-right overrides.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5b35befc-75b1-4499-af66-ab8996b0e165

📥 Commits

Reviewing files that changed from the base of the PR and between 3701952 and 52c6b8d.

📒 Files selected for processing (19)
  • design-qa.md
  • docs/feature-checklist.md
  • docs/features.md
  • src/dao/browser/agent/dao_agent_memory_service.cc
  • src/dao/browser/agent/dao_agent_memory_service.h
  • src/dao/browser/agent/dao_agent_memory_store.cc
  • src/dao/browser/agent/dao_agent_memory_store.h
  • src/dao/browser/agent/dao_agent_memory_store_unittest.cc
  • src/dao/browser/agent/dao_dream_browsertest.cc
  • src/dao/browser/agent/dao_dream_material_collector.cc
  • src/dao/browser/agent/dao_dream_material_collector.h
  • src/dao/browser/agent/dao_dream_service.cc
  • src/dao/browser/agent/dao_dream_service.h
  • src/dao/browser/ui/webui/resources/agent/__tests__/dao_dream_app.test.ts
  • src/dao/browser/ui/webui/resources/agent/__tests__/dao_dream_runner.test.ts
  • src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts
  • src/dao/browser/ui/webui/resources/agent/dao_dream_runner.ts
  • src/dao/browser/ui/webui/resources/agent/i18n/locales/en.ts
  • src/dao/browser/ui/webui/resources/agent/i18n/locales/zh-CN.ts
💤 Files with no reviewable changes (1)
  • src/dao/browser/agent/dao_dream_service.h

Comment thread design-qa.md
Comment on lines +3 to 6
**Source visual truth path**

- Open Design HTML authority: `newtab.html`, `browsing.html`, `settings.html`, `history.html`, `bookmarks.html`, `downloads.html`, and `extensions.html`.
- Tokens and behavior: `DESIGN-TOKENS.md`, `HANDOFF.md`, and `ACCEPTANCE.md`.
- Exported visual reference: `image.png`.
- Desktop brand authority: the borderless `branding/dao_logo.png` source artwork.
- Runtime captures: API 34 Pixel 6 emulator at 1080 x 2400 in light and dark themes.
`/Users/moonrailgun/Library/Application Support/Open Design/namespaces/release-stable/data/projects/d7969910-805f-49b3-9d34-80d6d21d2420/dream-recap-redesign.html`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

移除本机绝对路径和用户名。

该路径公开了 moonrailgun 本机用户名。其他开发者也无法访问该路径。请改用仓库内受版本控制的相对路径,或使用不含个人标识符的设计制品访问说明。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@design-qa.md` around lines 3 - 6, Remove the personal absolute filesystem
path from the “Source visual truth path” section in design-qa.md and replace it
with a repository-relative path to the version-controlled design artifact, or a
non-personal instruction for accessing the artifact without exposing a username.

'getDreamReports', {limit: 371});
expect(el.shadowRoot!.textContent).toContain('2026-06-12');
expect(el.shadowRoot!.textContent).toContain('2026-06-11');
expect(el.shadowRoot!.textContent).toContain('Thu, Jun 11');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Pin the locale for the formatted-date assertion.

formatDreamDate_ calls new Intl.DateTimeFormat(undefined, ...). The output depends on the runner locale. The literal 'Thu, Jun 11' passes only under an English locale. A CI image with a different default locale fails this test.

Set the locale explicitly in the Vitest config, or assert on a locale-independent value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dao/browser/ui/webui/resources/agent/__tests__/dao_dream_app.test.ts` at
line 272, Make the formatted-date assertion in dao_dream_app.test.ts
locale-independent by configuring Vitest to use a fixed English locale, or by
asserting against a locale-independent representation instead of the hard-coded
English text. Preserve coverage of formatDreamDate_ and ensure the test passes
consistently across runner locales.

Comment on lines 1420 to 1442
private async loadHistory_() {
try {
const raw = await callNative('getDreamReports', {limit: 30});
const [raw, rawWeekly] = await Promise.all([
callNative('getDreamReports', {limit: 371}),
callNative('getWeeklyDreamReports', {limit: 53}),
]);
const reports = this.normalizeReports_(raw);
const weeklyReports = this.normalizeWeeklyReports_(rawWeekly);
this.reports_ = reports;
this.report_ = reports[0] || null;
this.habitStates_ =
this.report_ ? this.loadPersistedHabitStates_(this.report_) : {};
this.weeklyReports_ = weeklyReports;
this.report_ = this.historyReports_()[0] || null;
this.habitStates_ = this.report_?.reportKind === 'daily' ?
this.loadPersistedHabitStates_(this.report_) : {};
if (this.report_) {
this.markReportViewed_(this.report_);
}
} catch (e) {
this.error_ = e instanceof Error ? e.message : String(e);
this.report_ = null;
this.reports_ = [];
this.weeklyReports_ = [];
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the weekly Dream native message handlers exist.
rg -n -C3 'getWeeklyDreamReports|markWeeklyDreamReportViewed' --type=cc --type=cpp --type=ts

Repository: msgbyte/dao-browser

Length of output: 188


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching dao_dream_app.ts / relevant native message handlers:"
git ls-files | rg 'dao_dream_app\.ts$|dream|Dream|Report|report' | head -200

echo
echo "Search references:"
rg -n -C2 'getWeeklyDreamReports|markWeeklyDreamReportViewed|getDreamReports|markDreamReportViewed|callNative' || true

Repository: msgbyte/dao-browser

Length of output: 1610


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Find candidate handler files by name:"
git ls-files | rg '(^|/)(.*dream.*|.*report.*|webui.*|webui$).*\.(ts|cc|cpp|mm|h)$' | head -200

Repository: msgbyte/dao-browser

Length of output: 12697


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "DAO agent UI native message registration:"
rg -n -C3 'getDreamReports|getWeeklyDreamReports|markWeeklyDreamReportViewed|DreamReports|WeeklyDream' src/dao/browser src/dao -g '*.{ts,cc,cpp,h,hpp,mm}' || true

echo
echo "Dream bridge TypeScript native message registration:"
rg -n -C3 'getDreamReports|getWeeklyDreamReports|markWeeklyDreamReportViewed|onMessage|message|native' src/dao/browser/ui/webui/resources/agent/dream_bridge.ts src/dao/browser/ui/webui/resources/agent/daodream*.ts src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts || true

echo
echo "Relevant dao_dream_app loadHistory section:"
sed -n '1380,1455p' src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts

Repository: msgbyte/dao-browser

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Exact getWeeklyDreamReports / markWeeklyDreamReportViewed native method definitions:"
rg -n 'getWeeklyDreamReports|markWeeklyDreamReportViewed|GetWeeklyDreamReports|MarkWeeklyDreamReportViewed|weekly.*Report.*handler|dream::' src/dao/browser -g '*.{cc,cpp,h,hpp,mm}' --max-count 50 || true

echo
echo "CallDream tests around weekly handlers (lines 5009-5105):"
sed -n '5009,5105p' src/dao/browser/agent/dao_dream_browsertest.cc

echo
echo "Relevant loadHistory_ section:"
sed -n '1380,1455p' src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts

echo
echo "Native promise/error type evidence for callNative:"
sed -n '1,180p' src/dao/browser/ui/webui/resources/agent/agent_bridge.ts
rg -n -C3 'callNative|callDream' src/dao/browser/ui/webui/resources/agent/agent_bridge.ts src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts --max-count 40 || true

Repository: msgbyte/dao-browser

Length of output: 24167


让周报加载失败不屏蔽每日历史数据。

Promise.allgetWeeklyDreamReports 失败或超时而拒绝时,当前捕获逻辑会把已加载的 reports_ 清空为 []。将周报请求降级为失败返回空列表,这样主显示路径不会因辅助数据出错而彻底空白。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts` around lines 1420
- 1442, Update loadHistory_ so getWeeklyDreamReports failure or timeout degrades
to an empty weekly-report list instead of rejecting the Promise.all operation;
preserve successful daily report loading and avoid clearing reports_ or setting
the main error state when only the weekly request fails.

Comment on lines +2053 to +2080
cells.push(html`
<button class="heat-cell" data-level=${String(level)}
style=${`grid-column:${column};grid-row:${index % 7 + 1}`}
role="gridcell"
title=${label}
aria-label=${label}
?disabled=${!report}
@click=${report ? () => this.selectHistoryReport_(report) :
undefined}>
</button>`);
}
return html`
<section class="activity-heatmap">
<div class="heatmap-header">
<h2>${t('dream.page.activity_title')}</h2>
<span>${t('dream.page.activity_reports', {
count: this.reports_.length,
})}</span>
</div>
<div class="heatmap-scroll">
<div class="heatmap-months">
${monthLabels.map(month => html`
<span style=${`grid-column:${month.column}`}>${month.label}</span>`)}
</div>
<div class="heatmap-grid" role="grid"
aria-label=${t('dream.page.activity_label')}>
${cells}
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the ARIA grid structure.

role="grid" requires role="row" elements between the grid and its gridcell children. Here the cells are direct children of the grid container, so the exposed structure is invalid and screen readers report an inconsistent widget.

The visual layout uses CSS grid-auto-flow: column, so there are no DOM rows to mark up. Remove the roles and keep the buttons plus the region label.

♿ Proposed change
         <button class="heat-cell" data-level=${String(level)}
             style=${`grid-column:${column};grid-row:${index % 7 + 1}`}
-            role="gridcell"
             title=${label}
-          <div class="heatmap-grid" role="grid"
+          <div class="heatmap-grid" role="group"
               aria-label=${t('dream.page.activity_label')}>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cells.push(html`
<button class="heat-cell" data-level=${String(level)}
style=${`grid-column:${column};grid-row:${index % 7 + 1}`}
role="gridcell"
title=${label}
aria-label=${label}
?disabled=${!report}
@click=${report ? () => this.selectHistoryReport_(report) :
undefined}>
</button>`);
}
return html`
<section class="activity-heatmap">
<div class="heatmap-header">
<h2>${t('dream.page.activity_title')}</h2>
<span>${t('dream.page.activity_reports', {
count: this.reports_.length,
})}</span>
</div>
<div class="heatmap-scroll">
<div class="heatmap-months">
${monthLabels.map(month => html`
<span style=${`grid-column:${month.column}`}>${month.label}</span>`)}
</div>
<div class="heatmap-grid" role="grid"
aria-label=${t('dream.page.activity_label')}>
${cells}
</div>
cells.push(html`
<button class="heat-cell" data-level=${String(level)}
style=${`grid-column:${column};grid-row:${index % 7 + 1}`}
title=${label}
aria-label=${label}
?disabled=${!report}
`@click`=${report ? () => this.selectHistoryReport_(report) :
undefined}>
</button>`);
}
return html`
<section class="activity-heatmap">
<div class="heatmap-header">
<h2>${t('dream.page.activity_title')}</h2>
<span>${t('dream.page.activity_reports', {
count: this.reports_.length,
})}</span>
</div>
<div class="heatmap-scroll">
<div class="heatmap-months">
${monthLabels.map(month => html`
<span style=${`grid-column:${month.column}`}>${month.label}</span>`)}
</div>
<div class="heatmap-grid" role="group"
aria-label=${t('dream.page.activity_label')}>
${cells}
</div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts` around lines 2053
- 2080, Remove the role="grid" and role="gridcell" attributes from the activity
heatmap elements in the cell-generation and heatmap-grid markup, while retaining
the buttons, their existing labels, and the heatmap region’s aria-label.

Comment on lines +2586 to +2588
<div class="report-body">
${this.renderMarkdown_(this.weeklyReportMarkdown_(report))}
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the markdown renderer for HTML escaping or sanitization.
fd -i 'markdown' --extension ts | xargs rg -n -C5 'renderDaoMarkdown|sanitiz|escapeHtml|DOMPurify|allowedTags'

Repository: msgbyte/dao-browser

Length of output: 2367


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== dao_markdown.ts relevant lines =="
sed -n '1,130p' src/dao/browser/ui/webui/resources/agent/dao_markdown.ts | cat -n

echo
echo "== weekly report helper usages =="
rg -n "weeklyReportMarkdown_|renderMarkdown_\\(|renderDaoMarkdown\\(|escapeHtml\\(|sanitize|DOMPurify|xss|allowedTags" src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts src/dao/browser/ui/webui/resources/agent/dao_markdown.ts -C 3

echo
echo "== package references to marked/marked-sanitize/dompurify =="
rg -n "marked|marked-sanitize|mark.*sanitize|DOMPurify|purify|html-sanitizer|xss" package.json package-lock.json src/dao/browser/ui/webui/resources/agent src/dao -g '*.{ts,js,json}' || true

Repository: msgbyte/dao-browser

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== matching candidate files (limited) ==\n'
fd -i 'dao_markdown|dao_dream' src/dao/browser/ui/webui/resources/agent --extension ts

printf '\n== dao_markdown.ts first 130 lines ==\n'
sed -n '1,130p' src/dao/browser/ui/webui/resources/agent/dao_markdown.ts | cat -n

printf '\n== weekly/daily markdown references in daemon ==\n'
rg -n "weeklyReportMarkdown_|renderMarkdown_|copyReportImage_|statusSummary|headline|nextStep|outcome\\.text" src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts -C 3

printf '\n== deterministic Marked behavior probe (import from bundled vendor code if available) ==\n'
python3 - <<'PY'
from pathlib import Path
import re
p = Path("src/dao/browser/ui/webui/resources/agent/dao_markdown.ts")
s = p.read_text()
print("contains escapeHtml:", bool(re.search(r"function escapeHtml|export function renderDaoMarkdown", s)))
m = re.search(r"function escapeHtml\(s: string\): string \{\s*return s\.replace\(/&/g,\s*'\&amp;'\)(.*?)\n\}", s, re.S)
print("escapeHtml implementation:")
print(m.group(0) if m else "not found")
render = re.search(r"export function renderDaoMarkdown\(markdown: string\): string \{(?P<body>.*?)\n\}", s, re.S)
print("renderDaoMarkdown body:")
print(render.group("body") if render else "not found")
print("has escapeHtml calls:", bool(re.search(r"escapeHtml\(|DOMPurify|sanitize\\(", s)))
PY

Repository: msgbyte/dao-browser

Length of output: 12865


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' "== locate vendor bundled marked source =="
rg -n "export default marked|marked =|function marked|renderer:" src/dao/browser/ui/webui/resources/agent/vendor/pi_runtime_bundle.js -C 3 || true

printf '%s\n' "== inspect bundled marked around renderer/decodeRawHtml/sanitize =="
rg -n "decodeRawHtml|sanitize|DOMPurify|escape|renderer|renderers|markedOptions|gfm|breaks|sanitize:" src/dao/browser/ui/webui/resources/agent/vendor/pi_runtime_bundle.js -C 4 || true

printf '%s\n' "== exact line ranges if found =="
python3 - <<'PY'
from pathlib import Path
p=Path("src/dao/browser/ui/webui/resources/agent/vendor/pi_runtime_bundle.js")
s=p.read_text().splitlines()
needles=["renderRawHtml","decodeRawHtml","sanitize","DOMPurify","renderer:","renderers:","escapeHtml()","<p>", "</p>"]
for i,line in enumerate(s,1):
    if any(n in line for n in needles):
        start=max(1,i-8); end=min(len(s),i+8)
        print(f"-- lines {start}-{end}, match:{line.strip()} --")
        for j in range(start,end+1):
            print(f"{j:6d}\t{s[j-1]}")
        print()
PY

printf '%s\n' "== runtime sanitizer usage in agent source =="
rg -n "DOMPurify|sanitize\\(|escapeHtml\\(|innerHTML|textContent" src/dao/browser/ui/webui/resources/agent src/dao -g '*.{ts,js}' -C 2 || true

Repository: msgbyte/dao-browser

Length of output: 1297


🌐 Web query:

Marked.js sanitize option raw HTML passed through by default

💡 Result:

The sanitize and sanitizer options in Marked.js were removed in version 8.0.0 [1][2][3]. Consequently, Marked.js does not sanitize the HTML it outputs by default, and it no longer provides a built-in mechanism to do so [4][5]. Because Marked.js does not provide sanitization, it is important to filter the output HTML to prevent XSS (Cross-Site Scripting) attacks if you are processing potentially unsafe input [4][5]. The official documentation recommends passing the output of Marked.js through a dedicated sanitization library such as DOMPurify (recommended), sanitize-html, or insane [1][2][5]. Example usage with DOMPurify: import { marked } from 'marked'; import DOMPurify from 'dompurify'; const dirtyHtml = marked.parse(markdownString); const cleanHtml = DOMPurify.sanitize(dirtyHtml);

Citations:


🌐 Web query:

Marked.js encodeHtml escapeHtml default encode HTML options

💡 Result:

Marked.js does not provide specific configuration options named encodeHtml or escapeHtml to toggle HTML escaping behavior. In fact, Marked does not perform automatic sanitization of the output HTML [1]. Because Marked does not have built-in options to escape or encode HTML tags by default, you must manage security and HTML escaping using the following recommended approaches: 1. Use a dedicated sanitization library: The official Marked documentation explicitly warns that Marked does not sanitize output [1]. It strongly recommends using a library like DOMPurify, sanitize-html, or insane on the resulting HTML string before inserting it into the DOM [1]. 2. Customize rendering: If you need to force-escape HTML tags within the markdown content, you can create a custom renderer [2][3]. By overriding the html method in a custom renderer object, you can intercept HTML tokens and manually apply an escaping function to the text content before it is rendered [3]. Example of custom escaping using a renderer: const customRenderer = { html(token) { // Manually escape the token.text here return escapeHtml(token.text); } }; const markedInstance = new Marked({ renderer: customRenderer }); Marked has intentionally moved toward a philosophy of keeping the core parser focused on Markdown-to-HTML conversion and offloading specialized tasks (like XHTML self-closing tags or specific sanitization requirements) to extensions or external utilities [4].

Citations:


🌐 Web query:

Marked.js encodeHtml option true raw HTML escape default false no options

💡 Result:

There is no option named encodeHtml in the current version of Marked.js [1][2][3]. Additionally, Marked.js does not sanitize input HTML or escape raw HTML by default [4][5][6]. For security, the official documentation strongly advises against using Marked.js to parse untrusted input without an external sanitization library [4][5]. Developers are recommended to use a sanitizer such as DOMPurify, sanitize-html, or insane on the resulting HTML output [4][5][6]. While there is an internal helper function escape in the Marked codebase that accepts an encode parameter [7], this is not a public configuration option exposed to users for controlling raw HTML handling. Marked.js remains designed to output raw HTML tags found within the Markdown source by default [5].

Citations:


🌐 Web query:

site:marked.js.org Raw HTML markdown allowed syntax

💡 Result:

Yes, raw HTML is allowed in most standard Markdown implementations, including the CommonMark specification and popular parsers like Marked [1]. When you include raw HTML tags directly in a Markdown file, most processors will pass them through to the final output unchanged [1]. This allows you to use HTML features not natively supported by Markdown—such as complex tables, styled spans, or specific attributes—directly within your document. Key considerations when using raw HTML in Markdown: 1. Security: Because Markdown processors generally do not sanitize raw HTML by default, embedding arbitrary HTML can introduce Cross-Site Scripting (XSS) risks if the content comes from untrusted users [1]. It is a standard industry best practice to use a sanitization library (such as DOMPurify) on the generated output before rendering it in a browser [1]. 2. Parsing Behavior: While most parsers support raw HTML, some may require specific configurations or adherence to block-level rules (e.g., ensuring tags are on their own lines or properly enclosed) to be processed correctly [1]. 3. Parser Differences: Although "raw HTML" is a common feature, the extent of support and how strictly it is parsed can vary slightly between different Markdown engines (like GitHub Flavored Markdown, Pandoc, or Marked) [1]. Always verify the behavior against the documentation of the specific processor you are using.

Citations:


Sanitize renderDaoMarkdown before assigning via innerHTML.

renderMarkdown_ inserts the markdown output with innerHTML, but renderDaoMarkdown only escapes raw input in the fallback <pre> branch; the m.parse(...) path returns Markdown output without dedicated sanitization. Since weeklyReportMarkdown_ includes model-generated fields (statusSummary, nextStep, outcome.text), pass marked.parse(...) through a sanitizer such as DOMPurify.sanitize(...) before assignment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dao/browser/ui/webui/resources/agent/dao_dream_app.ts` around lines 2586
- 2588, Update renderMarkdown_ so the Markdown output from marked.parse is
sanitized with DOMPurify.sanitize before being returned or assigned via
innerHTML. Ensure the weeklyReportMarkdown_ model-generated fields remain
rendered while both the parsed-Markdown path and fallback output are safe.

'Run Dream Analysis from Agent settings to generate a report.',
'dream.page.error': 'Failed to load dream report: {error}',
'dream.page.history_title': 'History',
'dream.page.history_recent': 'Recent {count} days',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Correct the history_recent wording for a mixed list.

The string reads Recent {count} days. renderHistoryList_ passes the size of the combined daily-and-weekly list. Weekly entries are not days, so the count and the noun disagree.

Use a neutral noun, for example Recent {count} reports. Apply the same change to the zh-CN value at src/dao/browser/ui/webui/resources/agent/i18n/locales/zh-CN.ts Line 276 and to the test fixture at src/dao/browser/ui/webui/resources/agent/__tests__/dao_dream_app.test.ts Line 87.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dao/browser/ui/webui/resources/agent/i18n/locales/en.ts` at line 376,
Update the dream.page.history_recent translation from “days” to a neutral noun
such as “reports” in both en.ts and zh-CN.ts, and update the matching expected
string in the dao_dream_app.test.ts fixture; keep renderHistoryList_ behavior
unchanged.

Source: Coding guidelines

Comment on lines +280 to +293
'dream.page.rhythm_title': '今日节奏',
'dream.page.rhythm_hint': '按前台专注时长',
'dream.page.rhythm_morning': '上午',
'dream.page.rhythm_afternoon': '下午',
'dream.page.rhythm_evening': '晚上',
'dream.page.rhythm_night': '深夜',
'dream.page.minutes': '{count} 分钟',
'dream.page.themes_title': '你在做什么',
'dream.page.themes_count': '{count} 个主题',
'dream.page.theme_light': '轻量',
'dream.page.theme_medium': '专注',
'dream.page.theme_deep': '深度',
'dream.page.theme_fallback': '今日重点',
'dream.page.stats_label': '今日活动统计',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

统一时间指代:报告描述的是昨天。

dream.page.recap_eyebrow 为「昨日梦境回顾」,dream.page.recap_title 为「你昨天过得怎么样」。但同一页面的以下三个键使用「今日」:

  • Line 280 dream.page.rhythm_title:「今日节奏」
  • Line 292 dream.page.theme_fallback:「今日重点」
  • Line 293 dream.page.stats_label:「今日活动统计」

这些区块渲染的是同一份昨日报告的数据。请改为「昨日」,或改用不带时间指代的措辞。

🌐 建议修改
-  'dream.page.rhythm_title': '今日节奏',
+  'dream.page.rhythm_title': '当日节奏',
-  'dream.page.theme_fallback': '今日重点',
-  'dream.page.stats_label': '今日活动统计',
+  'dream.page.theme_fallback': '当日重点',
+  'dream.page.stats_label': '当日活动统计',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'dream.page.rhythm_title': '今日节奏',
'dream.page.rhythm_hint': '按前台专注时长',
'dream.page.rhythm_morning': '上午',
'dream.page.rhythm_afternoon': '下午',
'dream.page.rhythm_evening': '晚上',
'dream.page.rhythm_night': '深夜',
'dream.page.minutes': '{count} 分钟',
'dream.page.themes_title': '你在做什么',
'dream.page.themes_count': '{count} 个主题',
'dream.page.theme_light': '轻量',
'dream.page.theme_medium': '专注',
'dream.page.theme_deep': '深度',
'dream.page.theme_fallback': '今日重点',
'dream.page.stats_label': '今日活动统计',
'dream.page.rhythm_title': '当日节奏',
'dream.page.rhythm_hint': '按前台专注时长',
'dream.page.rhythm_morning': '上午',
'dream.page.rhythm_afternoon': '下午',
'dream.page.rhythm_evening': '晚上',
'dream.page.rhythm_night': '深夜',
'dream.page.minutes': '{count} 分钟',
'dream.page.themes_title': '你在做什么',
'dream.page.themes_count': '{count} 个主题',
'dream.page.theme_light': '轻量',
'dream.page.theme_medium': '专注',
'dream.page.theme_deep': '深度',
'dream.page.theme_fallback': '当日重点',
'dream.page.stats_label': '当日活动统计',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dao/browser/ui/webui/resources/agent/i18n/locales/zh-CN.ts` around lines
280 - 293, 统一昨日梦境报告中的时间指代:在 zh-CN 本地化条目
dream.page.rhythm_title、dream.page.theme_fallback 和 dream.page.stats_label
中,将“今日”改为“昨日”或移除时间指代,保持与 dream.page.recap_eyebrow 和 dream.page.recap_title 一致。

@moonrailgun
moonrailgun merged commit 3dde5c9 into main Aug 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant