Merge #3971 (hide Worklog reasoning when Thinking disabled) onto master
This commit is contained in:
@@ -121,6 +121,10 @@
|
||||
- **Internal Stable Assistant Turn Anchors Phase 0 scaffold.** The browser now ships an inert `HermesAssistantTurnAnchors` helper surface plus a documented state-layer inventory for #3926, pinning current live/replay/settled source classifications and event dedupe precedence without changing visible chat rendering.
|
||||
- **New RFC: Stable Assistant Turn Anchors for Live-to-Final rendering.** Defines a frontend presentation/reconciliation model for anchoring one assistant turn across live streaming, settlement, replay/reload/recovery, Compact Worklog, Transparent Stream, terminal states, artifacts, and side effects. (#3926)
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Hide Thinking now also hides Worklog reasoning rows without hiding tool activity.** The `show_thinking=false` path now suppresses `.wl-reason` entries that come from reasoning text and prunes already-rendered rows when Thinking is toggled off, while preserving tool cards and ordinary Worklog anchor/progress rows. (#3903)
|
||||
|
||||
## [v0.51.358] — 2026-06-11 — Release LV (first-run password bootstrap hardening)
|
||||
|
||||
### Security
|
||||
|
||||
@@ -1402,6 +1402,7 @@ function cmdReasoning(args){
|
||||
const on=(arg==='show'||arg==='on');
|
||||
// Update the UI render gate immediately for responsiveness.
|
||||
window._showThinking=on;
|
||||
if(!on&&typeof removeThinking==='function') removeThinking();
|
||||
if(typeof renderMessages==='function') renderMessages();
|
||||
// Persist via /api/reasoning → config.yaml display.show_reasoning
|
||||
// (CLI reads the same key). Also mirror into WebUI settings.json
|
||||
|
||||
@@ -7241,6 +7241,7 @@ function _renderWorklogReasonInto(row, text){
|
||||
row.innerHTML=html;
|
||||
}
|
||||
function _worklogReasonNodeFromText(text, attrs){
|
||||
if(window._showThinking===false) return null;
|
||||
const html=_worklogReasonHtmlFromText(text);
|
||||
if(!html) return null;
|
||||
const row=document.createElement('div');
|
||||
@@ -11477,9 +11478,10 @@ function removeThinking(){
|
||||
const turn=$('liveAssistantTurn');
|
||||
const blocks=_assistantTurnBlocks(turn);
|
||||
if(blocks) blocks.querySelectorAll('.agent-activity-thinking').forEach(el=>el.remove());
|
||||
if(blocks) blocks.querySelectorAll('.tool-call-group[data-agent-activity-group="1"]').forEach(group=>{
|
||||
if(blocks) blocks.querySelectorAll('.wl-reason[data-worklog-reason-source="reasoning"]').forEach(el=>el.remove());
|
||||
if(blocks) blocks.querySelectorAll('.live-worklog[data-live-worklog-shell="1"],.tool-worklog-group[data-live-tool-call-group="1"],.tool-call-group[data-live-tool-call-group="1"],.tool-call-group[data-agent-activity-group="1"]').forEach(group=>{
|
||||
_syncToolCallGroupSummary(group);
|
||||
if(!group.querySelector('.tool-card-row,.agent-activity-thinking')){
|
||||
if(!group.querySelector('.tool-card-row,.agent-activity-thinking,.wl-reason')){
|
||||
if(typeof _clearActivityElapsedTimer==='function') _clearActivityElapsedTimer();
|
||||
group.remove();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,23 @@ def read(rel):
|
||||
return (REPO / rel).read_text(encoding='utf-8')
|
||||
|
||||
|
||||
def function_body(src, name):
|
||||
start = src.find(f"function {name}")
|
||||
assert start != -1, f"{name} not found"
|
||||
brace = src.find("{", start)
|
||||
assert brace != -1, f"{name} body not found"
|
||||
depth = 0
|
||||
for idx in range(brace, len(src)):
|
||||
ch = src[idx]
|
||||
if ch == "{":
|
||||
depth += 1
|
||||
elif ch == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
return src[brace + 1:idx]
|
||||
raise AssertionError(f"{name} body did not close")
|
||||
|
||||
|
||||
# ── api/config.py ─────────────────────────────────────────────────────────────
|
||||
|
||||
class TestShowThinkingConfig:
|
||||
@@ -80,6 +97,36 @@ class TestUiJsThinkingGate:
|
||||
)
|
||||
break
|
||||
|
||||
def test_worklog_reasoning_rows_are_gated_by_show_thinking(self):
|
||||
src = read('static/ui.js')
|
||||
node_fn = function_body(src, "_worklogReasonNodeFromText")
|
||||
assert 'window._showThinking===false' in node_fn and 'return null' in node_fn, (
|
||||
"reasoning-source Worklog rows must not be created when thinking is hidden"
|
||||
)
|
||||
|
||||
def test_show_thinking_gate_does_not_hide_worklog_anchor_text(self):
|
||||
src = read('static/ui.js')
|
||||
html_fn = function_body(src, "_worklogReasonHtmlFromText")
|
||||
assert 'window._showThinking' not in html_fn, (
|
||||
"the low-level Worklog text renderer is also used for anchor/progress text; "
|
||||
"only reasoning-source rows should be gated"
|
||||
)
|
||||
|
||||
def test_remove_thinking_prunes_reasoning_rows_but_preserves_tool_or_anchor_rows(self):
|
||||
src = read('static/ui.js')
|
||||
fn = function_body(src, "removeThinking")
|
||||
assert '.wl-reason[data-worklog-reason-source="reasoning"]' in fn, (
|
||||
"removeThinking must sweep already-rendered reasoning Worklog rows"
|
||||
)
|
||||
assert '.tool-card-row,.agent-activity-thinking,.wl-reason' in fn, (
|
||||
"empty-group cleanup must preserve groups that still contain tool cards "
|
||||
"or non-reasoning Worklog anchor rows"
|
||||
)
|
||||
assert '.live-worklog[data-live-worklog-shell="1"]' in fn, (
|
||||
"live Worklog shells must be considered for cleanup after their reasoning "
|
||||
"rows are removed"
|
||||
)
|
||||
|
||||
|
||||
# ── static/messages.js ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -158,6 +205,9 @@ class TestReasoningCommand:
|
||||
assert 'renderMessages' in fn, (
|
||||
"show/hide branch must call renderMessages()"
|
||||
)
|
||||
assert "typeof removeThinking==='function'" in fn and "removeThinking()" in fn, (
|
||||
"hide/off must prune already-rendered live Thinking and Worklog reasoning rows"
|
||||
)
|
||||
# Persistence: POST to /api/reasoning (CLI-shared config.yaml) AND
|
||||
# /api/settings (boot.js mirror).
|
||||
assert "api('/api/reasoning'" in fn, (
|
||||
|
||||
Reference in New Issue
Block a user