Skip to content

Add Control UI notification controls and web push test fixes#73894

Closed
hryousafzai15 wants to merge 9 commits into
openclaw:mainfrom
hryousafzai15:control-ui-response-completion-cue
Closed

Add Control UI notification controls and web push test fixes#73894
hryousafzai15 wants to merge 9 commits into
openclaw:mainfrom
hryousafzai15:control-ui-response-completion-cue

Conversation

@hryousafzai15

@hryousafzai15 hryousafzai15 commented Apr 29, 2026

Copy link
Copy Markdown

Summary

  • add response-completion sound cues with a configurable volume control
  • add a Communication → Notifications section for response cues and web push controls
  • expose browser web-push subscribe/unsubscribe/test actions in the Control UI
  • fix repeated web-push notifications being silently coalesced by giving default push notifications unique tags and renotify
  • restyle push notification action buttons to match the settings UI

Notes

Notifications is a virtual Communication section because push subscription state and response cue preferences are browser/runtime UI state, not gateway config schema.

Real behavior proof

  • Behavior or issue addressed: Response completion cues should only fire for visible assistant completions, should skip silent NO_REPLY final paths, and should not ring multiple times for the same completed run.
  • Real environment tested: Local OpenClaw checkout on macOS with the PR branch control-ui-response-completion-cue, Node 25.8.2, focused Control UI runtime modules under ui/src/ui.
  • Exact steps or command run after this patch: npm --prefix ui test -- response-completion-cue.node.test.ts app-gateway.node.test.ts
  • Evidence after fix: Terminal output from the patched branch:
> test
> vitest run --config vitest.config.ts response-completion-cue.node.test.ts app-gateway.node.test.ts

✓ unit-node src/ui/response-completion-cue.node.test.ts (8 tests) 5ms
✓ unit-node src/ui/app-gateway.node.test.ts (45 tests) 188ms

Test Files  2 passed (2)
Tests       53 passed (53)
  • Observed result after fix: The focused proof covers the final-state path after the patch: silent finals with no visible output do not cue, stream-only NO_REPLY finals do not cue, and repeated completion signals for the same run id dedupe to one cue. Existing app-gateway terminal event coverage still passes.
  • What was not tested: No external speaker recording is attached. Browser audio is still best-effort and covered by the existing AudioContext resume path plus focused cue logic tests.

Test plan

  • corepack pnpm exec oxfmt --check ui/public/sw.js ui/src/styles/config.css ui/src/ui/app-render.ts ui/src/ui/app-view-state.ts ui/src/ui/response-completion-cue.node.test.ts ui/src/ui/response-completion-cue.ts ui/src/ui/storage.ts ui/src/ui/views/config.ts
  • corepack pnpm exec oxlint ui/src/ui/app-render.ts ui/src/ui/app-view-state.ts ui/src/ui/response-completion-cue.node.test.ts ui/src/ui/response-completion-cue.ts ui/src/ui/storage.ts ui/src/ui/views/config.ts
  • PATH=/tmp/corepack-shims:$PATH pnpm check:test-types
  • corepack pnpm exec vitest run ui/src/ui/response-completion-cue.node.test.ts ui/src/ui/app-gateway.node.test.ts
  • PATH=/tmp/corepack-shims:$PATH pnpm ui:build

Manual:

  • verified web push test notification works in Chrome/Arc
  • verified response completion sound and volume control in preview

@greptile-apps

greptile-apps Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a response-completion sound cue with configurable volume, a Notifications section in the Control UI combining response cues and web-push controls, and fixes repeated push notifications being coalesced by assigning unique default tags. The implementation is clean and well-tested.

Two P2 concerns worth addressing before shipping:

  • renotify: true is set unconditionally in sw.js: when the server provides an explicit data.tag for intentional coalescing, this will re-alert on every replace.
  • AudioContext in playResponseCompletionSound is not resumed before scheduling notes; the context can start suspended with no recent user gesture, which is exactly the "hidden/unfocused" scenario the feature targets — leading to a silent failure.

Confidence Score: 4/5

Safe to merge; both findings are P2 and affect edge-case behaviour, not correctness of the main path.

Two P2 issues found (renotify coalescing edge case, AudioContext suspension). No P0/P1 defects. P2s alone cap at 4/5.

ui/public/sw.js (renotify unconditional), ui/src/ui/response-completion-cue.ts (AudioContext resume)

Prompt To Fix All With AI
This is a comment left during a code review.
Path: ui/public/sw.js
Line: 94

Comment:
**`renotify: true` defeats intentional tag-based coalescing**

`renotify: true` is applied unconditionally, even when the server provides an explicit `data.tag`. If a future server-side notification uses a stable tag to intentionally coalesce repeated updates (e.g., a long-running job status), `renotify: true` will re-alert the user on every replace — the opposite of coalescing behaviour. The flag is only relevant when a notification replaces another with the same tag, so scoping it to the default-tag path avoids the conflict:

```suggestion
    tag: data.tag || `openclaw-notification-${Date.now()}`,
    renotify: data.renotify ?? !data.tag,
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: ui/src/ui/response-completion-cue.ts
Line: 82-115

Comment:
**AudioContext may be suspended — sound silently skipped**

`new AudioContextCtor()` can start in the `"suspended"` state when there is no recent user gesture. This scenario is exactly when the cue is most useful — the user has the tab hidden or unfocused. A suspended context still lets you call `currentTime` and schedule events, but they only play once the context is running; without `resume()` the scheduled oscillators are silently dropped.

Adding a `resume()` call before reading `currentTime` fixes this:

```ts
const context = new AudioContextCtor();
// Resume in case the context was created while the page had no recent gesture.
void context.resume().catch(() => undefined);
const now = context.currentTime;
```

Because `resume()` is async, schedule notes relative to `0` (or keep the `now` reference after the promise resolves) if tight timing is needed; for a short ding at 350 ms the 1–2 ms resume latency is imperceptible.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(ui): add notification controls" | Re-trigger Greptile

Comment thread ui/public/sw.js Outdated
Comment thread ui/src/ui/response-completion-cue.ts
@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Keep this PR open: it still contains useful Control UI notification work that is not on current main, but it is not merge-ready because the head is dirty against main, browser-facing proof is still insufficient, and the new persisted UI settings/default notification behavior need maintainer review before landing.

Canonical path: Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

So I’m closing this here because the remaining work is already tracked in the canonical issue.

Review details

Best possible solution:

Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

Do we have a high-confidence way to reproduce the issue?

Yes for the web-push coalescing part at source level: current main uses a fixed default service-worker tag for pushes without data.tag. I did not run a live browser/audio reproduction, and the completion sound is a new capability rather than a current-main bug.

Is this the best way to solve the issue?

Unclear: the implementation direction is reasonable, but the dirty branch, insufficient browser proof, and open default-setting question mean it is not yet the best mergeable form. A rebased branch with real browser proof is the safer path.

Security review:

Security review cleared: No concrete security or supply-chain regression was found; the diff changes UI state, Web Audio cue logic, and existing service-worker notification options without adding dependencies, secrets, install hooks, or broader permissions.

AGENTS.md: found and applied where relevant.

What I checked:

  • stale F-rated PR: PR was opened 2026-04-29T00:45:37Z, is older than 30 days, and the latest review rated it F.
  • proof blocker: real behavior proof is insufficient and proof tier is F, so this branch is not merge-ready without contributor follow-up.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • steipete: Recent current-main history touches Control UI app-gateway/config surfaces and local blame shows recent app-gateway session-event work in this area. (role: recent area contributor; confidence: high; commits: a509c48f0ea2, b1117d98622f, 84ec0c27bf75; files: ui/src/ui/app-gateway.ts, ui/src/ui/views/config.ts, ui/src/ui/storage.ts)
  • BunsDev: Recent current-main commits added and maintained browser-local Control UI settings, config rendering, and UI behavior adjacent to this PR's storage/config surfaces. (role: recent area contributor; confidence: high; commits: 52370c59980b, 256377c029f6, 6b3cd9043ee6; files: ui/src/ui/storage.ts, ui/src/ui/views/config.ts, ui/src/ui/app-render.ts)
  • eduardocruz: The web-push Control UI and service-worker surface that this PR changes traces to the merged PWA web-push support commit. (role: introduced related behavior; confidence: high; commits: 21b7ad580540; files: ui/public/sw.js, ui/src/ui/push-subscription.ts, ui/src/ui/views/config.ts)
  • shakkernerd: Recent WebChat final-response behavior in app-gateway is adjacent to the completion cue hook and terminal-event handling changed by this PR. (role: adjacent owner; confidence: medium; commits: 5e2857477b9f; files: ui/src/ui/app-gateway.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against e1a98171417c.

@hryousafzai15

hryousafzai15 commented Apr 29, 2026

Copy link
Copy Markdown
Author

Addressed the two Greptile P2 findings in b374cec5:

  • renotify now defaults to true only for generated fallback tags, preserving intentional server-provided tag coalescing unless explicitly overridden.
  • response-completion audio now resumes a suspended AudioContext before reading currentTime and scheduling the cue.

Greptile’s visible summary still appears to reference the previous commit (d71c4d58), so it may be stale.

Re-review progress:

@SymbolStar

Copy link
Copy Markdown
Contributor

Friendly bump 🙏 — would be great to see this land.

While waiting, I packaged the same idea as a zero-build local patch so users on main can have the cue today, and ship it via ClawHub:

It's intentionally narrower than this PR (no settings UI, no service-worker fixes — those still need to come from #73894 proper). My apply.sh actually checks for responseCompletionSound in the bundled JS and auto-skips itself the moment this PR (or anything equivalent) ships, so there's no coexistence trap.

Real-world feedback from running it locally for a few hours:

  • Confirmed the state === "final" hook in handleTerminalChatEvent is the right anchor.
  • NO_REPLY / silent completions need to be filtered (your PR already does this — important).
  • Streaming long replies fired the cue multiple times until I added a hard rate-limit + content-fingerprint dedup; might be worth double-checking the same path here, especially with tool-heavy turns that re-render the assistant group.
  • Default behaviour: I started with "only-when-hidden" but flipped it to "always ring" because foreground users found the cue useful as an end-of-turn marker too. Worth considering as the default value for responseCompletionOnlyWhenHidden.

Happy to help if there's review bandwidth — even a "needs changes" pointer would help unblock the lane. Cc @steipete who has been moving things along on adjacent Control UI lanes.

SymbolStar added a commit to SymbolStar/SymbolStar that referenced this pull request May 15, 2026
Local zero-build OpenClaw Control UI patch that plays a short two-tone Web
Audio chime when an assistant reply finishes streaming. Stop-gap mirror of
openclaw/openclaw#73894 / issue #69186.

- ClawHub: https://clawhub.ai/symbolstar/echo-cue
- Source:  https://github.com/SymbolStar/echo-cue
@hryousafzai15

Copy link
Copy Markdown
Author

Thanks, this is really helpful, and appreciate you packaging a temporary workaround.

Good to know the final-state hook held up in real use. I will double-check the streaming and tool-heavy path for duplicate cue firing, and also think through whether the default should be always-on rather than hidden-only.

Glad the patch auto-skips once this lands. This PR is intended to be the proper upstream version with settings UI and push notification fixes included.

@hryousafzai15

Copy link
Copy Markdown
Author

Pushed a follow-up in a34c722d based on the duplicate-cue feedback.

Changes:

  • Pass the active run id and pre-final stream state into the completion cue path.
  • Skip cues when the final produced no visible output.
  • Skip silent stream-only finals like NO_REPLY even when there is no final message payload.
  • Add a short dedupe window keyed by run id, with text fallback, so repeated terminal/re-render paths do not ring multiple times for the same completion.
  • Added focused coverage for no-visible-output finals, silent stream-only finals, and duplicate run cues.

Verified locally:

  • npm --prefix ui test -- response-completion-cue.node.test.ts app-gateway.node.test.ts
  • 2 files passed, 53 tests passed.

@openclaw-barnacle openclaw-barnacle Bot added size: L triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. proof: supplied External PR includes structured after-fix real behavior proof. and removed size: M triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 15, 2026
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label May 31, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels May 31, 2026
@clawsweeper

clawsweeper Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

@clawsweeper clawsweeper Bot closed this May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: L stale Marked as stale due to inactivity status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants