# Conflicts: # CHANGELOG.md
This commit is contained in:
commit
5a33a51cec
5 changed files with 49 additions and 2 deletions
|
|
@ -3,6 +3,12 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [v0.51.394] — 2026-06-13 — Release NG (document-title attention badge for pending prompts, #4121)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Desktop/PWA wrappers can now detect approval/clarify attention from the document title without stealing chat-title ownership (#4121).** An active chat tab prepends a `● ` marker only while the current session has a pending approval or clarification prompt; the badge clears on prompt dismissal or session switch. It composes on top of the #4086 title-owner model — `syncTopbar()` still owns the underlying `"<session> — <assistant>"` format and the badge is layered in front of it. (#4121)
|
||||||
|
|
||||||
## [v0.51.393] — 2026-06-13 — Release NF (run /yolo immediately while the agent is busy)
|
## [v0.51.393] — 2026-06-13 — Release NF (run /yolo immediately while the agent is busy)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
||||||
|
|
@ -4127,6 +4127,14 @@ function _approvalPromptBelongsToActiveSession(sid) {
|
||||||
return !!(sid && _promptActiveSessionId() === sid);
|
return !!(sid && _promptActiveSessionId() === sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function activeSessionHasPendingPromptAttention() {
|
||||||
|
const sid = _promptActiveSessionId();
|
||||||
|
return !!(sid && (
|
||||||
|
_approvalPendingBySession.has(sid) ||
|
||||||
|
_clarifyPendingBySession.has(sid)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
function _rememberApprovalPending(pending, pendingCount) {
|
function _rememberApprovalPending(pending, pendingCount) {
|
||||||
if (!pending) return null;
|
if (!pending) return null;
|
||||||
const sid = pending._session_id || _promptActiveSessionId();
|
const sid = pending._session_id || _promptActiveSessionId();
|
||||||
|
|
@ -4137,7 +4145,10 @@ function _rememberApprovalPending(pending, pendingCount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _clearApprovalPendingForSession(sid) {
|
function _clearApprovalPendingForSession(sid) {
|
||||||
if (sid) _approvalPendingBySession.delete(sid);
|
if (sid) {
|
||||||
|
_approvalPendingBySession.delete(sid);
|
||||||
|
if (typeof syncTopbar === 'function') syncTopbar();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _hideApprovalCardIfOwner(sid, force=false) {
|
function _hideApprovalCardIfOwner(sid, force=false) {
|
||||||
|
|
@ -4205,6 +4216,7 @@ function showApprovalCard(pending, pendingCount) {
|
||||||
if (onceBtn && document.activeElement !== $('msg')) {
|
if (onceBtn && document.activeElement !== $('msg')) {
|
||||||
setTimeout(() => onceBtn.focus({preventScroll: true}), 50);
|
setTimeout(() => onceBtn.focus({preventScroll: true}), 50);
|
||||||
}
|
}
|
||||||
|
if (typeof syncTopbar === 'function') syncTopbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _syncApprovalCollapseButton(card) {
|
function _syncApprovalCollapseButton(card) {
|
||||||
|
|
@ -4591,7 +4603,10 @@ function _rememberClarifyPending(pending) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _clearClarifyPendingForSession(sid) {
|
function _clearClarifyPendingForSession(sid) {
|
||||||
if (sid) _clarifyPendingBySession.delete(sid);
|
if (sid) {
|
||||||
|
_clarifyPendingBySession.delete(sid);
|
||||||
|
if (typeof syncTopbar === 'function') syncTopbar();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _hideClarifyCardIfOwner(sid, force=false, reason="dismissed") {
|
function _hideClarifyCardIfOwner(sid, force=false, reason="dismissed") {
|
||||||
|
|
@ -4613,8 +4628,15 @@ function showClarifyForSession(sid, pending) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _renderPendingPromptsForActiveSession() {
|
function _renderPendingPromptsForActiveSession() {
|
||||||
|
const sid = _promptActiveSessionId();
|
||||||
_renderPendingApprovalForActiveSession();
|
_renderPendingApprovalForActiveSession();
|
||||||
_renderPendingClarifyForActiveSession();
|
_renderPendingClarifyForActiveSession();
|
||||||
|
if (
|
||||||
|
sid &&
|
||||||
|
typeof activeSessionHasPendingPromptAttention === 'function' &&
|
||||||
|
activeSessionHasPendingPromptAttention()
|
||||||
|
) return;
|
||||||
|
if (typeof syncTopbar === 'function') syncTopbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _ensureClarifyCardDom() {
|
function _ensureClarifyCardDom() {
|
||||||
|
|
@ -4967,6 +4989,7 @@ function showClarifyCard(pending) {
|
||||||
if (input && !sameClarify && document.activeElement !== $('msg')) {
|
if (input && !sameClarify && document.activeElement !== $('msg')) {
|
||||||
input.focus({preventScroll: true});
|
input.focus({preventScroll: true});
|
||||||
}
|
}
|
||||||
|
if (typeof syncTopbar === 'function') syncTopbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function respondClarify(response) {
|
async function respondClarify(response) {
|
||||||
|
|
|
||||||
|
|
@ -6275,6 +6275,9 @@ function syncTopbar(){
|
||||||
const sessionTitle=S.session.title||t('untitled');
|
const sessionTitle=S.session.title||t('untitled');
|
||||||
const _topbarTitle=$('topbarTitle');if(_topbarTitle)_topbarTitle.textContent=sessionTitle;
|
const _topbarTitle=$('topbarTitle');if(_topbarTitle)_topbarTitle.textContent=sessionTitle;
|
||||||
document.title=sessionTitle+' \u2014 '+assistantDisplayName();
|
document.title=sessionTitle+' \u2014 '+assistantDisplayName();
|
||||||
|
if(typeof activeSessionHasPendingPromptAttention==='function'&&activeSessionHasPendingPromptAttention()){
|
||||||
|
document.title='● '+document.title;
|
||||||
|
}
|
||||||
const _topbarMeta=$('topbarMeta');
|
const _topbarMeta=$('topbarMeta');
|
||||||
if(_topbarMeta){
|
if(_topbarMeta){
|
||||||
let sourceLabel=(S.session&&(S.session.source_label||S.session.source_tag||S.session.raw_source))||'';
|
let sourceLabel=(S.session&&(S.session.source_label||S.session.source_tag||S.session.raw_source))||'';
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,10 @@ def test_approval_card_render_is_gated_to_active_session_and_cached():
|
||||||
body = _function_body(MESSAGES_JS, "showApprovalCard")
|
body = _function_body(MESSAGES_JS, "showApprovalCard")
|
||||||
assert "_rememberApprovalPending(" in body
|
assert "_rememberApprovalPending(" in body
|
||||||
assert "_approvalPromptBelongsToActiveSession(sid)" in body
|
assert "_approvalPromptBelongsToActiveSession(sid)" in body
|
||||||
|
assert "syncTopbar()" in body
|
||||||
assert "return;" in body
|
assert "return;" in body
|
||||||
assert "let _approvalPendingBySession" in MESSAGES_JS
|
assert "let _approvalPendingBySession" in MESSAGES_JS
|
||||||
|
assert "function activeSessionHasPendingPromptAttention()" in MESSAGES_JS
|
||||||
assert "function _renderPendingPromptsForActiveSession()" in MESSAGES_JS
|
assert "function _renderPendingPromptsForActiveSession()" in MESSAGES_JS
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -71,6 +73,7 @@ def test_clarify_card_render_is_gated_to_active_session_and_cached():
|
||||||
body = _function_body(MESSAGES_JS, "showClarifyCard")
|
body = _function_body(MESSAGES_JS, "showClarifyCard")
|
||||||
assert "_rememberClarifyPending(" in body
|
assert "_rememberClarifyPending(" in body
|
||||||
assert "_clarifyPromptBelongsToActiveSession(sid)" in body
|
assert "_clarifyPromptBelongsToActiveSession(sid)" in body
|
||||||
|
assert "syncTopbar()" in body
|
||||||
assert "return;" in body
|
assert "return;" in body
|
||||||
assert "let _clarifyPendingBySession" in MESSAGES_JS
|
assert "let _clarifyPendingBySession" in MESSAGES_JS
|
||||||
assert "function _renderPendingPromptsForActiveSession()" in MESSAGES_JS
|
assert "function _renderPendingPromptsForActiveSession()" in MESSAGES_JS
|
||||||
|
|
@ -100,7 +103,16 @@ def test_load_session_rerenders_cached_prompt_for_new_active_session():
|
||||||
def test_prompt_rerender_hides_previous_session_cards_without_clearing_cache():
|
def test_prompt_rerender_hides_previous_session_cards_without_clearing_cache():
|
||||||
approval_body = _function_body(MESSAGES_JS, "_renderPendingApprovalForActiveSession")
|
approval_body = _function_body(MESSAGES_JS, "_renderPendingApprovalForActiveSession")
|
||||||
clarify_body = _function_body(MESSAGES_JS, "_renderPendingClarifyForActiveSession")
|
clarify_body = _function_body(MESSAGES_JS, "_renderPendingClarifyForActiveSession")
|
||||||
|
rerender_body = _function_body(MESSAGES_JS, "_renderPendingPromptsForActiveSession")
|
||||||
|
clear_approval_body = _function_body(MESSAGES_JS, "_clearApprovalPendingForSession")
|
||||||
|
clear_clarify_body = _function_body(MESSAGES_JS, "_clearClarifyPendingForSession")
|
||||||
assert "_approvalSessionId && _approvalSessionId !== sid" in approval_body
|
assert "_approvalSessionId && _approvalSessionId !== sid" in approval_body
|
||||||
assert "hideApprovalCard(true)" in approval_body
|
assert "hideApprovalCard(true)" in approval_body
|
||||||
assert "_clarifySessionId && _clarifySessionId !== sid" in clarify_body
|
assert "_clarifySessionId && _clarifySessionId !== sid" in clarify_body
|
||||||
assert "hideClarifyCard(true, 'session')" in clarify_body
|
assert "hideClarifyCard(true, 'session')" in clarify_body
|
||||||
|
assert "syncTopbar()" in rerender_body
|
||||||
|
assert "activeSessionHasPendingPromptAttention()" in rerender_body
|
||||||
|
assert "if (sid) {" in clear_approval_body
|
||||||
|
assert "syncTopbar()" in clear_approval_body
|
||||||
|
assert "if (sid) {" in clear_clarify_body
|
||||||
|
assert "syncTopbar()" in clear_clarify_body
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ def test_sync_topbar_does_not_count_only_loaded_tail_messages():
|
||||||
assert "const metaText=_topbarMessageMetaText();" in block
|
assert "const metaText=_topbarMessageMetaText();" in block
|
||||||
assert "t('n_messages',vis.length)" not in block
|
assert "t('n_messages',vis.length)" not in block
|
||||||
assert "S.messages.filter(m=>m&&m.role&&m.role!=='tool')" not in block
|
assert "S.messages.filter(m=>m&&m.role&&m.role!=='tool')" not in block
|
||||||
|
assert "document.title=sessionTitle+' \\u2014 '+assistantDisplayName();" in block
|
||||||
|
assert "document.title='● '+document.title;" in block
|
||||||
|
assert "const pendingPrefix=(typeof activeSessionHasPendingPromptAttention==='function'&&activeSessionHasPendingPromptAttention())?'● ':'';" not in block
|
||||||
|
|
||||||
sessions_js = (ROOT / "static" / "sessions.js").read_text()
|
sessions_js = (ROOT / "static" / "sessions.js").read_text()
|
||||||
fn = sessions_js[
|
fn = sessions_js[
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue