clay-server 4.0.0-beta.24 → 4.0.0-beta.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/public/app.js +1 -0
- package/lib/public/css/messages.css +40 -1
- package/lib/public/css/pane.css +45 -10
- package/lib/public/css/worker-proposal.css +2 -2
- package/lib/public/modules/app-messages.js +9 -5
- package/lib/public/modules/split-pair-ui.js +15 -4
- package/lib/public/modules/split-worker-runtime.js +24 -0
- package/lib/public/modules/thinking-lifecycle.js +175 -0
- package/lib/public/modules/thinking-summary.js +28 -0
- package/lib/public/modules/thinking-view.js +75 -0
- package/lib/public/modules/tools.js +10 -122
- package/lib/public/modules/worker-proposal.js +0 -8
- package/lib/sdk-bridge.js +3 -0
- package/package.json +1 -1
package/lib/public/app.js
CHANGED
|
@@ -377,6 +377,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
377
377
|
currentMsgEl: null,
|
|
378
378
|
currentFullText: "",
|
|
379
379
|
matePreThinkingEl: null,
|
|
380
|
+
thinkingState: null,
|
|
380
381
|
// panels
|
|
381
382
|
fileViewerFullscreen: false,
|
|
382
383
|
markdownSlidesActive: false,
|
|
@@ -693,6 +693,8 @@ pre.mermaid-error {
|
|
|
693
693
|
|
|
694
694
|
.thinking-header {
|
|
695
695
|
display: inline-flex;
|
|
696
|
+
max-width: 100%;
|
|
697
|
+
box-sizing: border-box;
|
|
696
698
|
align-items: center;
|
|
697
699
|
gap: 6px;
|
|
698
700
|
cursor: pointer;
|
|
@@ -726,9 +728,44 @@ pre.mermaid-error {
|
|
|
726
728
|
.thinking-label {
|
|
727
729
|
font-size: 13px;
|
|
728
730
|
color: var(--text-muted);
|
|
731
|
+
min-width: 0;
|
|
732
|
+
overflow: hidden;
|
|
733
|
+
text-overflow: ellipsis;
|
|
734
|
+
white-space: nowrap;
|
|
729
735
|
}
|
|
730
736
|
|
|
737
|
+
.thinking-live .thinking-header {
|
|
738
|
+
padding: 6px 0;
|
|
739
|
+
background: transparent;
|
|
740
|
+
border-radius: 0;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
.thinking-live .thinking-chevron { display: none; }
|
|
744
|
+
|
|
745
|
+
.thinking-live .thinking-label {
|
|
746
|
+
background: linear-gradient(100deg, var(--text-muted) 40%, var(--text-dimmer) 50%, var(--text-muted) 60%);
|
|
747
|
+
background-size: 300% 100%;
|
|
748
|
+
background-clip: text;
|
|
749
|
+
-webkit-background-clip: text;
|
|
750
|
+
color: transparent;
|
|
751
|
+
animation: thinking-summary-shimmer 2.8s ease-in-out infinite;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
@keyframes thinking-summary-shimmer {
|
|
755
|
+
from { background-position: 100% 0; }
|
|
756
|
+
to { background-position: 0% 0; }
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
@media (prefers-reduced-motion: reduce) {
|
|
760
|
+
.thinking-live .thinking-label { animation: none; background: none; color: var(--text-muted); }
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.thinking-header[aria-disabled="true"] { cursor: default; }
|
|
764
|
+
.thinking-header:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
765
|
+
.thinking-item .thinking-spinner { display: none; }
|
|
766
|
+
|
|
731
767
|
.thinking-duration {
|
|
768
|
+
flex-shrink: 0;
|
|
732
769
|
color: var(--text-dimmer);
|
|
733
770
|
font-size: 12px;
|
|
734
771
|
}
|
|
@@ -753,7 +790,7 @@ pre.mermaid-error {
|
|
|
753
790
|
font-size: 13px;
|
|
754
791
|
line-height: 1.55;
|
|
755
792
|
color: var(--text-muted);
|
|
756
|
-
white-space:
|
|
793
|
+
white-space: normal;
|
|
757
794
|
word-break: break-word;
|
|
758
795
|
max-height: 300px;
|
|
759
796
|
overflow-y: auto;
|
|
@@ -762,6 +799,8 @@ pre.mermaid-error {
|
|
|
762
799
|
}
|
|
763
800
|
|
|
764
801
|
.thinking-item.expanded .thinking-content { display: block; }
|
|
802
|
+
.thinking-content > :first-child { margin-top: 0; }
|
|
803
|
+
.thinking-content > :last-child { margin-bottom: 0; }
|
|
765
804
|
|
|
766
805
|
/* ==========================================================================
|
|
767
806
|
Tool Items
|
package/lib/public/css/pane.css
CHANGED
|
@@ -104,6 +104,49 @@ body.pane-mode #input-area {
|
|
|
104
104
|
flex-shrink: 0;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
.split-pane-header.has-worker-runtime {
|
|
108
|
+
flex-wrap: nowrap;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.split-pane-header.has-worker-runtime .split-pane-context {
|
|
112
|
+
order: 1;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.split-pane-header.has-worker-runtime .split-pane-close {
|
|
116
|
+
order: 2;
|
|
117
|
+
flex-shrink: 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.split-worker-runtime {
|
|
121
|
+
flex: 0 1 auto;
|
|
122
|
+
min-width: 0;
|
|
123
|
+
display: flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
gap: 8px;
|
|
126
|
+
font-size: 11px;
|
|
127
|
+
line-height: 18px;
|
|
128
|
+
white-space: nowrap;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.split-worker-runtime-model {
|
|
132
|
+
min-width: 0;
|
|
133
|
+
overflow: hidden;
|
|
134
|
+
text-overflow: ellipsis;
|
|
135
|
+
white-space: nowrap;
|
|
136
|
+
color: var(--text);
|
|
137
|
+
font-weight: 600;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.split-worker-runtime-thinking {
|
|
141
|
+
min-width: 0;
|
|
142
|
+
overflow: hidden;
|
|
143
|
+
text-overflow: ellipsis;
|
|
144
|
+
padding: 0 6px;
|
|
145
|
+
border-radius: 4px;
|
|
146
|
+
background: var(--bg-alt);
|
|
147
|
+
color: var(--text-secondary);
|
|
148
|
+
}
|
|
149
|
+
|
|
107
150
|
.split-pane-title {
|
|
108
151
|
flex: 0 1 auto;
|
|
109
152
|
max-width: 46%;
|
|
@@ -192,8 +235,7 @@ body.pane-mode #input-area {
|
|
|
192
235
|
@keyframes pair-divider-flow-left { 0%, 100% { transform: translateX(3px); opacity: 0.65; } 50% { transform: translateX(-3px); opacity: 1; } }
|
|
193
236
|
@keyframes pair-status-pulse { 50% { opacity: 0.55; } }
|
|
194
237
|
|
|
195
|
-
/* Worker
|
|
196
|
-
delegated turn. Informational only -- the composer stays enabled. */
|
|
238
|
+
/* Worker activity notice shown while this pane processes a delegated task. */
|
|
197
239
|
.pane-delegation-notice {
|
|
198
240
|
display: none;
|
|
199
241
|
align-items: center;
|
|
@@ -202,7 +244,7 @@ body.pane-mode #input-area {
|
|
|
202
244
|
max-width: var(--content-width);
|
|
203
245
|
margin: 0 auto;
|
|
204
246
|
padding: 4px 10px;
|
|
205
|
-
border-radius: 8px
|
|
247
|
+
border-radius: 8px;
|
|
206
248
|
background: color-mix(in srgb, var(--accent) 8%, var(--bg));
|
|
207
249
|
color: var(--text-secondary);
|
|
208
250
|
font-size: 11px;
|
|
@@ -210,13 +252,6 @@ body.pane-mode #input-area {
|
|
|
210
252
|
transition: box-shadow 0.2s;
|
|
211
253
|
}
|
|
212
254
|
.pane-delegation-notice.visible { display: flex; }
|
|
213
|
-
.pane-delegation-notice.visible:has(+ #input-wrapper #input-row:focus-within) {
|
|
214
|
-
box-shadow: 0 0 0 1px var(--border);
|
|
215
|
-
clip-path: inset(-1px -1px 0 -1px);
|
|
216
|
-
}
|
|
217
|
-
.pane-delegation-notice.visible + #input-wrapper #input-row {
|
|
218
|
-
border-radius: 0 0 8px 8px;
|
|
219
|
-
}
|
|
220
255
|
.pane-delegation-notice::before {
|
|
221
256
|
content: "";
|
|
222
257
|
width: 6px;
|
|
@@ -141,9 +141,9 @@
|
|
|
141
141
|
.worker-proposal-action:not(:disabled):hover { filter: brightness(1.12); }
|
|
142
142
|
.worker-proposal-action:disabled, .worker-proposal-select:disabled, .worker-proposal-effort-btn:disabled { cursor: default; opacity: 0.58; }
|
|
143
143
|
|
|
144
|
-
.worker-proposal-error
|
|
144
|
+
.worker-proposal-error { margin-top: 10px; padding: 8px 10px; border-radius: 8px; font-size: 10px; line-height: 1.45; }
|
|
145
145
|
.worker-proposal-error { color: var(--error); background: var(--error-8); }
|
|
146
|
-
.worker-proposal-
|
|
146
|
+
.worker-proposal-error.hidden { display: none; }
|
|
147
147
|
.worker-proposal-card[data-status="starting"] .worker-proposal-status,
|
|
148
148
|
.worker-proposal-card[data-status="running"] .worker-proposal-status { animation: worker-proposal-pulse 1.6s ease-in-out infinite; }
|
|
149
149
|
.worker-proposal-card[data-status="completed"] { border-color: color-mix(in srgb, var(--success) 35%, var(--border)); }
|
|
@@ -29,6 +29,7 @@ import { handleSessionActionMessage } from './session-actions.js';
|
|
|
29
29
|
import { renderWorkerProposal, updateWorkerProposal } from './worker-proposal.js';
|
|
30
30
|
import { handleInputSync, autoResize, builtinCommands, setScheduleBtnDisabled, sendShellResultToAgent } from './input.js';
|
|
31
31
|
import { startThinking, appendThinking, stopThinking, resetThinkingGroup, createToolItem, updateToolExecuting, updateToolResult, markAllToolsDone, closeToolGroup, removeToolFromGroup, resetToolState, getTools, getPlanContent, setPlanContent, renderPlanBanner, renderPlanCard, getTodoTools, handleTodoWrite, handleTaskCreate, handleTaskUpdate, applyDeadSessionTodoCompaction, isPlanFilePath, enableMainInput, addTurnMeta, updateSubagentActivity, addSubagentToolEntry, markSubagentDone, initSubagentStop, updateSubagentProgress, updateSubagentTaskStatus, renderAskUserQuestion, markAskUserAnswered, renderPermissionRequest, markPermissionCancelled, markPermissionResolved, renderElicitationRequest, markElicitationResolved, renderUserDialogRequest, markUserDialogResolved, updateThinkingTokens } from './tools.js';
|
|
32
|
+
import { finishThinkingTurn, resumeThinkingAfterReplay } from './thinking-lifecycle.js';
|
|
32
33
|
import { showDoneNotification, playDoneSound, isNotifAlertEnabled, isNotifSoundEnabled } from './notifications.js';
|
|
33
34
|
import { handleFsList, handleFsRead, handleFileChanged, handleDirChanged, handleFileHistory, handleGitDiff, handleFileAt, refreshIfOpen, getPendingNavigate, handleFsSearch, presentMarkdownEdit } from './filebrowser.js';
|
|
34
35
|
import { beginMarkdownTurn, finishMarkdownTurn, markdownPathFromToolInput } from './markdown-live-edit.js';
|
|
@@ -189,6 +190,7 @@ export function processMessage(msg) {
|
|
|
189
190
|
|
|
190
191
|
case "history_done":
|
|
191
192
|
store.set({ replayingHistory: false });
|
|
193
|
+
resumeThinkingAfterReplay(store.get('sessionIsProcessing'));
|
|
192
194
|
// Batched syntax highlight + mermaid pass for the entire replayed
|
|
193
195
|
// transcript. Per-message highlights are skipped during replay
|
|
194
196
|
// (see markdown.js) to avoid cascading reflows that the sticky-
|
|
@@ -1028,7 +1030,6 @@ export function processMessage(msg) {
|
|
|
1028
1030
|
if (typeof msg.text !== "string") break;
|
|
1029
1031
|
removeMatePreThinking();
|
|
1030
1032
|
stopThinking();
|
|
1031
|
-
resetThinkingGroup();
|
|
1032
1033
|
setActivity(null);
|
|
1033
1034
|
appendDelta(msg.text);
|
|
1034
1035
|
break;
|
|
@@ -1228,7 +1229,7 @@ export function processMessage(msg) {
|
|
|
1228
1229
|
// were missed by the client).
|
|
1229
1230
|
removeMatePreThinking();
|
|
1230
1231
|
setActivity(null);
|
|
1231
|
-
|
|
1232
|
+
finishThinkingTurn();
|
|
1232
1233
|
markAllToolsDone();
|
|
1233
1234
|
closeToolGroup();
|
|
1234
1235
|
clearAllImageGenerationProgress();
|
|
@@ -1248,7 +1249,7 @@ export function processMessage(msg) {
|
|
|
1248
1249
|
case "done":
|
|
1249
1250
|
removeMatePreThinking();
|
|
1250
1251
|
setActivity(null);
|
|
1251
|
-
|
|
1252
|
+
finishThinkingTurn();
|
|
1252
1253
|
hideWorkerDelegationNotice();
|
|
1253
1254
|
markAllToolsDone();
|
|
1254
1255
|
closeToolGroup();
|
|
@@ -1271,6 +1272,7 @@ export function processMessage(msg) {
|
|
|
1271
1272
|
case "error":
|
|
1272
1273
|
removeMatePreThinking();
|
|
1273
1274
|
setActivity(null);
|
|
1275
|
+
finishThinkingTurn();
|
|
1274
1276
|
addSystemMessage(msg.text, true);
|
|
1275
1277
|
break;
|
|
1276
1278
|
|
|
@@ -1322,13 +1324,14 @@ export function processMessage(msg) {
|
|
|
1322
1324
|
case "process_conflict":
|
|
1323
1325
|
removeMatePreThinking();
|
|
1324
1326
|
setActivity(null);
|
|
1327
|
+
finishThinkingTurn();
|
|
1325
1328
|
addConflictMessage(msg);
|
|
1326
1329
|
break;
|
|
1327
1330
|
|
|
1328
1331
|
case "session_writer_conflict":
|
|
1329
1332
|
removeMatePreThinking();
|
|
1330
1333
|
setActivity(null);
|
|
1331
|
-
|
|
1334
|
+
finishThinkingTurn();
|
|
1332
1335
|
markAllToolsDone();
|
|
1333
1336
|
closeToolGroup();
|
|
1334
1337
|
addSystemMessage(msg.text || "This session is already open in another process.", true);
|
|
@@ -1337,13 +1340,14 @@ export function processMessage(msg) {
|
|
|
1337
1340
|
case "context_overflow":
|
|
1338
1341
|
removeMatePreThinking();
|
|
1339
1342
|
setActivity(null);
|
|
1343
|
+
finishThinkingTurn();
|
|
1340
1344
|
addContextOverflowMessage(msg);
|
|
1341
1345
|
break;
|
|
1342
1346
|
|
|
1343
1347
|
case "auth_required":
|
|
1344
1348
|
removeMatePreThinking();
|
|
1345
1349
|
setActivity(null);
|
|
1346
|
-
|
|
1350
|
+
finishThinkingTurn();
|
|
1347
1351
|
markAllToolsDone();
|
|
1348
1352
|
closeToolGroup();
|
|
1349
1353
|
appendDelta((msg.text || "Authentication required.") + "\n");
|
|
@@ -3,6 +3,8 @@ import { getWs } from './ws-ref.js';
|
|
|
3
3
|
import { VENDOR_AVATARS } from './app-rendering.js';
|
|
4
4
|
import { buildAgentVendorSelect, fillAgentModels, buildAgentEffortSelect, fillAgentEffort } from './agent-config-selects.js';
|
|
5
5
|
import { showToast } from './utils.js';
|
|
6
|
+
import { getCachedSessions } from './sidebar-sessions.js';
|
|
7
|
+
import { appendWorkerRuntime } from './split-worker-runtime.js';
|
|
6
8
|
|
|
7
9
|
var pairDialog = null;
|
|
8
10
|
// Set when the dialog was opened via "Add Split Worker" on an existing session:
|
|
@@ -190,8 +192,7 @@ export function handlePairCreated(msg) {
|
|
|
190
192
|
|
|
191
193
|
// --- Worker-pane composer notice -------------------------------------
|
|
192
194
|
// Shown inside a pane iframe while ITS session is processing a delegated
|
|
193
|
-
// turn
|
|
194
|
-
// makes it visible that a new message joins the Driver's turn.
|
|
195
|
+
// turn. Identifies the Driver responsible for the current task.
|
|
195
196
|
var workerNoticeEl = null;
|
|
196
197
|
|
|
197
198
|
export function showWorkerDelegationNotice(driverTitle) {
|
|
@@ -205,7 +206,7 @@ export function showWorkerDelegationNotice(driverTitle) {
|
|
|
205
206
|
inputArea.insertBefore(workerNoticeEl, inputWrapper);
|
|
206
207
|
}
|
|
207
208
|
var who = driverTitle ? "“" + driverTitle + "”" : "the Driver";
|
|
208
|
-
workerNoticeEl.textContent = "
|
|
209
|
+
workerNoticeEl.textContent = "Working on a task from " + who + ".";
|
|
209
210
|
workerNoticeEl.classList.add("visible");
|
|
210
211
|
}
|
|
211
212
|
|
|
@@ -239,8 +240,10 @@ export function syncPairChrome(host, split) {
|
|
|
239
240
|
for (var i = 0; i < groups.length; i++) {
|
|
240
241
|
if (groups[i].id === split.groupId) { group = groups[i]; break; }
|
|
241
242
|
}
|
|
242
|
-
var oldBadges = host.querySelectorAll(".split-pair-role, .split-pair-live, .split-pair-set-driver");
|
|
243
|
+
var oldBadges = host.querySelectorAll(".split-pair-role, .split-pair-live, .split-pair-set-driver, .split-worker-runtime");
|
|
243
244
|
for (var bi = 0; bi < oldBadges.length; bi++) oldBadges[bi].remove();
|
|
245
|
+
var runtimeHeaders = host.querySelectorAll(".has-worker-runtime");
|
|
246
|
+
for (var ri = 0; ri < runtimeHeaders.length; ri++) runtimeHeaders[ri].classList.remove("has-worker-runtime");
|
|
244
247
|
host.classList.remove("split-delegating", "split-direction-right", "split-direction-left");
|
|
245
248
|
if (!group) return;
|
|
246
249
|
var headers = host.querySelectorAll(".split-pane-header");
|
|
@@ -272,6 +275,14 @@ export function syncPairChrome(host, split) {
|
|
|
272
275
|
var title = header.querySelector(".split-pane-title");
|
|
273
276
|
var access = header.querySelector(".split-pane-full-access");
|
|
274
277
|
header.insertBefore(badge, access ? access.nextSibling : (title ? title.nextSibling : null));
|
|
278
|
+
if (!isDriver) {
|
|
279
|
+
var sessions = getCachedSessions() || [];
|
|
280
|
+
var workerSession = null;
|
|
281
|
+
for (var wi = 0; wi < sessions.length; wi++) {
|
|
282
|
+
if (sessions[wi].id === sessionId) { workerSession = sessions[wi]; break; }
|
|
283
|
+
}
|
|
284
|
+
appendWorkerRuntime(header, workerSession);
|
|
285
|
+
}
|
|
275
286
|
})(split.panes[pi].sessionId, headers[pi]);
|
|
276
287
|
}
|
|
277
288
|
var active = (store.get('splitDelegations') || {})[group.id];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Read the Worker's own session metadata, never the active Driver's settings.
|
|
2
|
+
export function workerRuntimeLabels(session) {
|
|
3
|
+
var model = session && typeof session.model === "string" ? session.model : "";
|
|
4
|
+
var effort = session && typeof session.effort === "string" ? session.effort : "";
|
|
5
|
+
var level = effort === "xhigh" ? "X-High" : (effort ? effort.charAt(0).toUpperCase() + effort.slice(1) : "Default");
|
|
6
|
+
return { model: model || "Default model", thinking: "Thinking: " + level };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function appendWorkerRuntime(header, session) {
|
|
10
|
+
var labels = workerRuntimeLabels(session);
|
|
11
|
+
var runtime = document.createElement("div");
|
|
12
|
+
runtime.className = "split-worker-runtime";
|
|
13
|
+
var model = document.createElement("span");
|
|
14
|
+
model.className = "split-worker-runtime-model";
|
|
15
|
+
model.textContent = labels.model;
|
|
16
|
+
model.title = "Model: " + labels.model;
|
|
17
|
+
var thinking = document.createElement("span");
|
|
18
|
+
thinking.className = "split-worker-runtime-thinking";
|
|
19
|
+
thinking.textContent = labels.thinking;
|
|
20
|
+
runtime.appendChild(model);
|
|
21
|
+
runtime.appendChild(thinking);
|
|
22
|
+
header.classList.add("has-worker-runtime");
|
|
23
|
+
header.appendChild(runtime);
|
|
24
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { escapeHtml } from './utils.js';
|
|
2
|
+
import { iconHtml, refreshIcons } from './icons.js';
|
|
3
|
+
import { store } from './store.js';
|
|
4
|
+
import { addToMessages, scrollToBottom } from './chat-render-runtime.js';
|
|
5
|
+
import { prepareThinkingView, bindThinkingView, updateThinkingView, beginThinkingView, setThinkingViewLive, finishThinkingView } from './thinking-view.js';
|
|
6
|
+
|
|
7
|
+
function getThinkingState() {
|
|
8
|
+
return store.get('thinkingState') || null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function setThinkingState(state) {
|
|
12
|
+
store.set({ thinkingState: state });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function updateThinkingState(state, patch) {
|
|
16
|
+
var next = Object.assign({}, state, patch);
|
|
17
|
+
setThinkingState(next);
|
|
18
|
+
return next;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isMateThinking() {
|
|
22
|
+
var target = store.get('dmTargetUser');
|
|
23
|
+
return !!(store.get('dmMode') && target && target.isMate);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function maybeScrollToBottom() {
|
|
27
|
+
if (!store.get('replayingHistory')) scrollToBottom();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function createThinkingElement() {
|
|
31
|
+
var el = document.createElement("div");
|
|
32
|
+
el.className = "thinking-item";
|
|
33
|
+
if (isMateThinking()) {
|
|
34
|
+
var target = store.get('dmTargetUser');
|
|
35
|
+
var mateName = (target && target.displayName) || "Mate";
|
|
36
|
+
var mateAvatar = document.body.dataset.mateAvatarUrl || "";
|
|
37
|
+
el.classList.add("mate-thinking");
|
|
38
|
+
el.innerHTML =
|
|
39
|
+
'<img class="dm-bubble-avatar dm-bubble-avatar-mate" src="' + escapeHtml(mateAvatar) + '" alt="">' +
|
|
40
|
+
'<div class="dm-bubble-content">' +
|
|
41
|
+
'<div class="dm-bubble-header"><span class="dm-bubble-name">' + escapeHtml(mateName) + '</span></div>' +
|
|
42
|
+
'<div class="mate-thinking-dots mate-thinking-activity"><span></span><span></span><span></span></div>' +
|
|
43
|
+
'<div class="thinking-header" style="display:none">' +
|
|
44
|
+
'<span class="thinking-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
45
|
+
'<span class="thinking-label">Thinking</span>' +
|
|
46
|
+
'<span class="thinking-duration"></span>' +
|
|
47
|
+
'<span class="thinking-spinner">' + iconHtml("loader", "icon-spin") + '</span>' +
|
|
48
|
+
'</div>' +
|
|
49
|
+
'<div class="thinking-content"></div>' +
|
|
50
|
+
'</div>';
|
|
51
|
+
} else {
|
|
52
|
+
el.innerHTML =
|
|
53
|
+
'<div class="thinking-header">' +
|
|
54
|
+
'<span class="thinking-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
55
|
+
'<span class="thinking-label">Thinking</span>' +
|
|
56
|
+
'<span class="thinking-duration"></span>' +
|
|
57
|
+
'<span class="thinking-spinner">' + iconHtml("loader", "icon-spin") + '</span>' +
|
|
58
|
+
'</div>' +
|
|
59
|
+
'<div class="thinking-content"></div>';
|
|
60
|
+
}
|
|
61
|
+
prepareThinkingView(el);
|
|
62
|
+
bindThinkingView(el);
|
|
63
|
+
addToMessages(el);
|
|
64
|
+
return el;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function showMateActivity(el, active, hasContent) {
|
|
68
|
+
if (!el.classList.contains("mate-thinking")) return;
|
|
69
|
+
var activity = el.querySelector(".mate-thinking-activity");
|
|
70
|
+
var header = el.querySelector(".thinking-header");
|
|
71
|
+
if (activity) activity.style.display = active ? "" : "none";
|
|
72
|
+
if (header) header.style.display = active || !hasContent ? "none" : "";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function startThinkingSegment() {
|
|
76
|
+
var thinkingState = getThinkingState();
|
|
77
|
+
if (thinkingState && thinkingState.active) return thinkingState.el;
|
|
78
|
+
if (!thinkingState || thinkingState.ended) {
|
|
79
|
+
thinkingState = {
|
|
80
|
+
el: createThinkingElement(),
|
|
81
|
+
detailsText: "",
|
|
82
|
+
segmentText: "",
|
|
83
|
+
latestText: "",
|
|
84
|
+
active: false,
|
|
85
|
+
ended: false,
|
|
86
|
+
startTime: 0,
|
|
87
|
+
totalDuration: 0,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
thinkingState = updateThinkingState(thinkingState, {
|
|
91
|
+
active: true,
|
|
92
|
+
segmentText: "",
|
|
93
|
+
startTime: Date.now(),
|
|
94
|
+
});
|
|
95
|
+
beginThinkingView(thinkingState.el, !store.get('replayingHistory') && !isMateThinking());
|
|
96
|
+
showMateActivity(thinkingState.el, true, !!thinkingState.detailsText.trim());
|
|
97
|
+
refreshIcons();
|
|
98
|
+
maybeScrollToBottom();
|
|
99
|
+
return thinkingState.el;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function appendThinkingText(text) {
|
|
103
|
+
var thinkingState = getThinkingState();
|
|
104
|
+
if (!thinkingState || !thinkingState.active || typeof text !== "string" || !text) return;
|
|
105
|
+
var separator = !thinkingState.segmentText && thinkingState.detailsText ? "\n\n" : "";
|
|
106
|
+
var segmentText = thinkingState.segmentText + text;
|
|
107
|
+
thinkingState = updateThinkingState(thinkingState, {
|
|
108
|
+
segmentText: segmentText,
|
|
109
|
+
detailsText: thinkingState.detailsText + separator + text,
|
|
110
|
+
latestText: segmentText.trim() ? segmentText : thinkingState.latestText,
|
|
111
|
+
});
|
|
112
|
+
updateThinkingView(thinkingState.el, thinkingState.segmentText || thinkingState.latestText, thinkingState.detailsText);
|
|
113
|
+
maybeScrollToBottom();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function stopThinkingSegment(duration) {
|
|
117
|
+
var thinkingState = getThinkingState();
|
|
118
|
+
if (!thinkingState || !thinkingState.active) return;
|
|
119
|
+
var seconds = typeof duration === "number" ? duration : (Date.now() - thinkingState.startTime) / 1000;
|
|
120
|
+
thinkingState = updateThinkingState(thinkingState, {
|
|
121
|
+
totalDuration: thinkingState.totalDuration + seconds,
|
|
122
|
+
active: false,
|
|
123
|
+
});
|
|
124
|
+
finishThinkingView(thinkingState.el, thinkingState.latestText, thinkingState.detailsText);
|
|
125
|
+
thinkingState.el.querySelector(".thinking-duration").textContent = " " + thinkingState.totalDuration.toFixed(1) + "s";
|
|
126
|
+
showMateActivity(thinkingState.el, false, !!thinkingState.detailsText.trim());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function finishThinkingTurn() {
|
|
130
|
+
var thinkingState = getThinkingState();
|
|
131
|
+
if (!thinkingState) return;
|
|
132
|
+
stopThinkingSegment();
|
|
133
|
+
thinkingState = getThinkingState();
|
|
134
|
+
thinkingState = updateThinkingState(thinkingState, { ended: true });
|
|
135
|
+
finishThinkingView(thinkingState.el, thinkingState.latestText, thinkingState.detailsText);
|
|
136
|
+
showMateActivity(thinkingState.el, false, !!thinkingState.detailsText.trim());
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function resetThinkingTurn() {
|
|
140
|
+
finishThinkingTurn();
|
|
141
|
+
setThinkingState(null);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function clearThinkingState() {
|
|
145
|
+
setThinkingState(null);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function saveThinkingState() {
|
|
149
|
+
return getThinkingState();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function restoreThinkingState(saved) {
|
|
153
|
+
setThinkingState(saved || null);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function resumeThinkingAfterReplay(processing) {
|
|
157
|
+
var thinkingState = getThinkingState();
|
|
158
|
+
if (!thinkingState || !thinkingState.active || !processing) return;
|
|
159
|
+
setThinkingViewLive(thinkingState.el, !isMateThinking());
|
|
160
|
+
showMateActivity(thinkingState.el, true, !!thinkingState.detailsText.trim());
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function updateThinkingTokenEstimate(estimatedTokens) {
|
|
164
|
+
var thinkingState = getThinkingState();
|
|
165
|
+
if (!thinkingState || !thinkingState.active || !thinkingState.el) return;
|
|
166
|
+
var label = thinkingState.el.querySelector(".thinking-label");
|
|
167
|
+
if (!label) return;
|
|
168
|
+
var count = estimatedTokens || 0;
|
|
169
|
+
var display = count >= 1000 ? ("~" + (Math.round(count / 100) / 10) + "k") : ("~" + count);
|
|
170
|
+
if (thinkingState.latestText.trim()) {
|
|
171
|
+
label.title = label.textContent + " · " + display + " tokens";
|
|
172
|
+
} else {
|
|
173
|
+
label.textContent = "Thinking " + display + " tokens";
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Summarize only supplied text; a heading is preferable to a prose excerpt.
|
|
2
|
+
function plainText(text) {
|
|
3
|
+
return text.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1")
|
|
4
|
+
.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
|
|
5
|
+
.replace(/^\s*(?:#{1,6}\s+|>\s*)/gm, "")
|
|
6
|
+
.replace(/[*_`~]/g, "")
|
|
7
|
+
.replace(/\s+/g, " ").trim();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function thinkingSummary(text) {
|
|
11
|
+
if (typeof text !== "string" || !text.trim()) return "Thinking";
|
|
12
|
+
var lines = text.split("\n");
|
|
13
|
+
var heading = "";
|
|
14
|
+
for (var i = 0; i < lines.length; i++) {
|
|
15
|
+
if (/^\s*(?:#{1,6}\s+|\*\*|__)/.test(lines[i])) {
|
|
16
|
+
var candidate = plainText(lines[i]);
|
|
17
|
+
if (candidate) heading = candidate;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
var paragraphs = text.trim().split(/\n\s*\n/);
|
|
21
|
+
var summary = heading || plainText(paragraphs[paragraphs.length - 1]);
|
|
22
|
+
if (!heading) {
|
|
23
|
+
var sentence = summary.match(/^.*?[.!?。!?](?:\s|$)/);
|
|
24
|
+
if (sentence) summary = sentence[0].trim();
|
|
25
|
+
}
|
|
26
|
+
if (!summary) return "Thinking";
|
|
27
|
+
return summary.length > 140 ? summary.slice(0, 139).trimEnd() + "…" : summary;
|
|
28
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { thinkingSummary } from './thinking-summary.js';
|
|
2
|
+
import { renderMarkdown } from './markdown.js';
|
|
3
|
+
|
|
4
|
+
export function prepareThinkingView(el) {
|
|
5
|
+
el.hidden = false;
|
|
6
|
+
el.classList.remove("empty", "done", "expanded", "thinking-live");
|
|
7
|
+
var header = el.querySelector(".thinking-header");
|
|
8
|
+
header.setAttribute("role", "button");
|
|
9
|
+
header.setAttribute("aria-expanded", "false");
|
|
10
|
+
header.setAttribute("aria-disabled", "true");
|
|
11
|
+
header.tabIndex = -1;
|
|
12
|
+
header.style.cursor = "";
|
|
13
|
+
el.querySelector(".thinking-chevron").style.display = "";
|
|
14
|
+
el.querySelector(".thinking-label").textContent = "Thinking";
|
|
15
|
+
el.querySelector(".thinking-label").removeAttribute("title");
|
|
16
|
+
el.querySelector(".thinking-duration").textContent = "";
|
|
17
|
+
el.querySelector(".thinking-content").textContent = "";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function bindThinkingView(el) {
|
|
21
|
+
var header = el.querySelector(".thinking-header");
|
|
22
|
+
function toggle() {
|
|
23
|
+
if (header.getAttribute("aria-disabled") === "true") return;
|
|
24
|
+
el.classList.toggle("expanded");
|
|
25
|
+
header.setAttribute("aria-expanded", el.classList.contains("expanded") ? "true" : "false");
|
|
26
|
+
}
|
|
27
|
+
header.addEventListener("click", toggle);
|
|
28
|
+
header.addEventListener("keydown", function (event) {
|
|
29
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
30
|
+
event.preventDefault();
|
|
31
|
+
toggle();
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function updateThinkingView(el, summaryText, detailsText) {
|
|
36
|
+
var summary = thinkingSummary(summaryText);
|
|
37
|
+
var label = el.querySelector(".thinking-label");
|
|
38
|
+
label.textContent = summary;
|
|
39
|
+
label.title = summary;
|
|
40
|
+
el.querySelector(".thinking-content").innerHTML = detailsText.trim() ? renderMarkdown(detailsText) : "";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function setThinkingViewLive(el, live) {
|
|
44
|
+
el.classList.toggle("thinking-live", !!live);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function beginThinkingView(el, shimmer) {
|
|
48
|
+
el.hidden = false;
|
|
49
|
+
el.classList.remove("done", "empty", "expanded");
|
|
50
|
+
setThinkingViewLive(el, shimmer);
|
|
51
|
+
var header = el.querySelector(".thinking-header");
|
|
52
|
+
header.setAttribute("aria-expanded", "false");
|
|
53
|
+
header.setAttribute("aria-disabled", "true");
|
|
54
|
+
header.tabIndex = -1;
|
|
55
|
+
header.style.cursor = "";
|
|
56
|
+
el.querySelector(".thinking-chevron").style.display = "";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function finishThinkingView(el, summaryText, detailsText) {
|
|
60
|
+
el.classList.remove("thinking-live");
|
|
61
|
+
el.classList.add("done");
|
|
62
|
+
el.classList.remove("expanded");
|
|
63
|
+
updateThinkingView(el, summaryText, detailsText);
|
|
64
|
+
var hasContent = !!detailsText.trim();
|
|
65
|
+
var header = el.querySelector(".thinking-header");
|
|
66
|
+
header.setAttribute("aria-expanded", "false");
|
|
67
|
+
header.setAttribute("aria-disabled", hasContent ? "false" : "true");
|
|
68
|
+
header.tabIndex = hasContent ? 0 : -1;
|
|
69
|
+
header.style.cursor = hasContent ? "pointer" : "default";
|
|
70
|
+
el.hidden = !hasContent;
|
|
71
|
+
if (!hasContent) {
|
|
72
|
+
el.classList.add("empty");
|
|
73
|
+
el.querySelector(".thinking-chevron").style.display = "none";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -7,6 +7,7 @@ import { mateAvatarUrl } from './avatar.js';
|
|
|
7
7
|
import { getChatLayout } from './theme.js';
|
|
8
8
|
import { store } from './store.js';
|
|
9
9
|
import { VENDOR_NAMES } from './app-rendering.js';
|
|
10
|
+
import { startThinkingSegment, appendThinkingText, stopThinkingSegment, resetThinkingTurn, clearThinkingState, saveThinkingState, restoreThinkingState, updateThinkingTokenEstimate } from './thinking-lifecycle.js';
|
|
10
11
|
|
|
11
12
|
var ctx;
|
|
12
13
|
|
|
@@ -46,8 +47,6 @@ var todoMeta = {
|
|
|
46
47
|
|
|
47
48
|
// --- Tool tracking ---
|
|
48
49
|
var tools = {};
|
|
49
|
-
var currentThinking = null;
|
|
50
|
-
var thinkingGroup = null; // { el, count, totalDuration }
|
|
51
50
|
var pendingPermissions = {};
|
|
52
51
|
|
|
53
52
|
// --- Tool group tracking ---
|
|
@@ -1354,12 +1353,7 @@ export function markUserDialogResolved(requestId, behavior) {
|
|
|
1354
1353
|
// Annotates the active thinking block's label with a running token estimate.
|
|
1355
1354
|
// No-op when no thinking block is active.
|
|
1356
1355
|
export function updateThinkingTokens(estimatedTokens) {
|
|
1357
|
-
|
|
1358
|
-
var label = currentThinking.el.querySelector(".thinking-label");
|
|
1359
|
-
if (!label) return;
|
|
1360
|
-
var n = estimatedTokens || 0;
|
|
1361
|
-
var disp = n >= 1000 ? ("~" + (Math.round(n / 100) / 10) + "k") : ("~" + n);
|
|
1362
|
-
label.textContent = "Thinking " + disp + " tokens";
|
|
1356
|
+
updateThinkingTokenEstimate(estimatedTokens);
|
|
1363
1357
|
}
|
|
1364
1358
|
|
|
1365
1359
|
// --- Plan mode rendering ---
|
|
@@ -1729,128 +1723,23 @@ function updateTodoSticky() {
|
|
|
1729
1723
|
// --- Thinking ---
|
|
1730
1724
|
export function startThinking() {
|
|
1731
1725
|
ctx.finalizeAssistantBlock();
|
|
1732
|
-
|
|
1733
|
-
// Reuse existing thinking group if consecutive
|
|
1734
|
-
if (thinkingGroup && thinkingGroup.el.classList.contains("done")) {
|
|
1735
|
-
var el = thinkingGroup.el;
|
|
1736
|
-
el.classList.remove("done");
|
|
1737
|
-
el.querySelector(".thinking-content").textContent = "";
|
|
1738
|
-
var reuseLabel = el.querySelector(".thinking-label");
|
|
1739
|
-
if (reuseLabel) reuseLabel.textContent = "Thinking";
|
|
1740
|
-
// Mate mode: restore dots activity row, hide thinking header
|
|
1741
|
-
if (el.classList.contains("mate-thinking")) {
|
|
1742
|
-
var actRow = el.querySelector(".mate-thinking-activity");
|
|
1743
|
-
if (actRow) {
|
|
1744
|
-
actRow.style.display = "";
|
|
1745
|
-
}
|
|
1746
|
-
var header = el.querySelector(".thinking-header");
|
|
1747
|
-
if (header) header.style.display = "none";
|
|
1748
|
-
}
|
|
1749
|
-
currentThinking = { el: el, fullText: "", startTime: Date.now() };
|
|
1750
|
-
refreshIcons();
|
|
1751
|
-
maybeScrollToBottom();
|
|
1752
|
-
if (!el.classList.contains("mate-thinking")) {
|
|
1753
|
-
ctx.setActivity("thinking");
|
|
1754
|
-
}
|
|
1755
|
-
return;
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
|
-
var el = document.createElement("div");
|
|
1759
|
-
el.className = "thinking-item";
|
|
1760
|
-
|
|
1761
|
-
if (ctx.isMateDm()) {
|
|
1762
|
-
var mateName = ctx.getMateName();
|
|
1763
|
-
var mateAvatar = ctx.getMateAvatarUrl();
|
|
1764
|
-
el.classList.add("mate-thinking");
|
|
1765
|
-
el.innerHTML =
|
|
1766
|
-
'<img class="dm-bubble-avatar dm-bubble-avatar-mate" src="' + escapeHtml(mateAvatar) + '" alt="">' +
|
|
1767
|
-
'<div class="dm-bubble-content">' +
|
|
1768
|
-
'<div class="dm-bubble-header"><span class="dm-bubble-name">' + escapeHtml(mateName) + '</span></div>' +
|
|
1769
|
-
'<div class="mate-thinking-dots mate-thinking-activity"><span></span><span></span><span></span></div>' +
|
|
1770
|
-
'<div class="thinking-header" style="display:none">' +
|
|
1771
|
-
'<span class="thinking-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
1772
|
-
'<span class="thinking-label">Thinking</span>' +
|
|
1773
|
-
'<span class="thinking-duration"></span>' +
|
|
1774
|
-
'<span class="thinking-spinner">' + iconHtml("loader", "icon-spin") + '</span>' +
|
|
1775
|
-
'</div>' +
|
|
1776
|
-
'<div class="thinking-content"></div>' +
|
|
1777
|
-
'</div>';
|
|
1778
|
-
} else {
|
|
1779
|
-
el.innerHTML =
|
|
1780
|
-
'<div class="thinking-header">' +
|
|
1781
|
-
'<span class="thinking-chevron">' + iconHtml("chevron-right") + '</span>' +
|
|
1782
|
-
'<span class="thinking-label">Thinking</span>' +
|
|
1783
|
-
'<span class="thinking-duration"></span>' +
|
|
1784
|
-
'<span class="thinking-spinner">' + iconHtml("loader", "icon-spin") + '</span>' +
|
|
1785
|
-
'</div>' +
|
|
1786
|
-
'<div class="thinking-content"></div>';
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
el.querySelector(".thinking-header").addEventListener("click", function () {
|
|
1790
|
-
el.classList.toggle("expanded");
|
|
1791
|
-
});
|
|
1792
|
-
|
|
1793
|
-
ctx.addToMessages(el);
|
|
1794
|
-
refreshIcons();
|
|
1795
|
-
maybeScrollToBottom();
|
|
1796
|
-
thinkingGroup = { el: el, count: 0, totalDuration: 0 };
|
|
1797
|
-
currentThinking = { el: el, fullText: "", startTime: Date.now() };
|
|
1726
|
+
var el = startThinkingSegment();
|
|
1798
1727
|
if (!ctx.isMateDm()) {
|
|
1799
1728
|
ctx.setActivity("thinking");
|
|
1800
1729
|
}
|
|
1730
|
+
return el;
|
|
1801
1731
|
}
|
|
1802
1732
|
|
|
1803
1733
|
export function appendThinking(text) {
|
|
1804
|
-
|
|
1805
|
-
currentThinking.fullText += text;
|
|
1806
|
-
currentThinking.el.querySelector(".thinking-content").textContent = currentThinking.fullText;
|
|
1807
|
-
maybeScrollToBottom();
|
|
1734
|
+
appendThinkingText(text);
|
|
1808
1735
|
}
|
|
1809
1736
|
|
|
1810
1737
|
export function stopThinking(duration) {
|
|
1811
|
-
|
|
1812
|
-
var secs = typeof duration === "number" ? duration : (Date.now() - currentThinking.startTime) / 1000;
|
|
1813
|
-
currentThinking.el.classList.add("done");
|
|
1814
|
-
if (thinkingGroup && thinkingGroup.el === currentThinking.el) {
|
|
1815
|
-
thinkingGroup.count++;
|
|
1816
|
-
thinkingGroup.totalDuration += secs;
|
|
1817
|
-
currentThinking.el.querySelector(".thinking-duration").textContent = " " + thinkingGroup.totalDuration.toFixed(1) + "s";
|
|
1818
|
-
} else {
|
|
1819
|
-
currentThinking.el.querySelector(".thinking-duration").textContent = " " + secs.toFixed(1) + "s";
|
|
1820
|
-
}
|
|
1821
|
-
// If no thinking text was streamed (e.g. Codex reasoning items arrive
|
|
1822
|
-
// with encrypted/hidden content, or Claude without extended-thinking),
|
|
1823
|
-
// the expand affordance is misleading because there's nothing inside.
|
|
1824
|
-
// Strip the chevron and the click handler so the header reads as a
|
|
1825
|
-
// plain label.
|
|
1826
|
-
var hasContent = !!(currentThinking.fullText && currentThinking.fullText.length > 0);
|
|
1827
|
-
if (!hasContent) {
|
|
1828
|
-
currentThinking.el.classList.add("empty");
|
|
1829
|
-
var chev = currentThinking.el.querySelector(".thinking-chevron");
|
|
1830
|
-
if (chev) chev.style.display = "none";
|
|
1831
|
-
var hdr = currentThinking.el.querySelector(".thinking-header");
|
|
1832
|
-
if (hdr) {
|
|
1833
|
-
hdr.style.cursor = "default";
|
|
1834
|
-
// Replace click listener by cloning the node (cheapest way to strip listeners).
|
|
1835
|
-
var clone = hdr.cloneNode(true);
|
|
1836
|
-
hdr.parentNode.replaceChild(clone, hdr);
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
|
-
// In mate mode: hide sparkle activity, show compact thinking header.
|
|
1840
|
-
if (currentThinking.el.classList.contains("mate-thinking")) {
|
|
1841
|
-
var actRow = currentThinking.el.querySelector(".mate-thinking-activity");
|
|
1842
|
-
if (actRow) actRow.style.display = "none";
|
|
1843
|
-
var header = currentThinking.el.querySelector(".thinking-header");
|
|
1844
|
-
if (header) {
|
|
1845
|
-
header.style.display = "";
|
|
1846
|
-
header.style.cursor = hasContent ? "pointer" : "default";
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
currentThinking = null;
|
|
1738
|
+
stopThinkingSegment(duration);
|
|
1850
1739
|
}
|
|
1851
1740
|
|
|
1852
1741
|
export function resetThinkingGroup() {
|
|
1853
|
-
|
|
1742
|
+
resetThinkingTurn();
|
|
1854
1743
|
}
|
|
1855
1744
|
|
|
1856
1745
|
// --- Tool items ---
|
|
@@ -2521,7 +2410,7 @@ export function getHiddenResultTools() { return HIDDEN_RESULT_TOOLS; }
|
|
|
2521
2410
|
export function saveToolState() {
|
|
2522
2411
|
return {
|
|
2523
2412
|
tools: tools,
|
|
2524
|
-
|
|
2413
|
+
thinkingState: saveThinkingState(),
|
|
2525
2414
|
todoWidgetEl: todoWidgetEl,
|
|
2526
2415
|
todoMeta: todoMeta,
|
|
2527
2416
|
inPlanMode: inPlanMode,
|
|
@@ -2536,7 +2425,7 @@ export function saveToolState() {
|
|
|
2536
2425
|
|
|
2537
2426
|
export function restoreToolState(saved) {
|
|
2538
2427
|
tools = saved.tools;
|
|
2539
|
-
|
|
2428
|
+
restoreThinkingState(saved.thinkingState);
|
|
2540
2429
|
todoWidgetEl = saved.todoWidgetEl;
|
|
2541
2430
|
todoMeta = saved.todoMeta || normalizeTodoMeta();
|
|
2542
2431
|
inPlanMode = saved.inPlanMode;
|
|
@@ -2553,8 +2442,7 @@ export function restoreToolState(saved) {
|
|
|
2553
2442
|
|
|
2554
2443
|
export function resetToolState() {
|
|
2555
2444
|
tools = {};
|
|
2556
|
-
|
|
2557
|
-
thinkingGroup = null;
|
|
2445
|
+
clearThinkingState();
|
|
2558
2446
|
inPlanMode = false;
|
|
2559
2447
|
planContent = null;
|
|
2560
2448
|
currentPlanCardEl = null;
|
|
@@ -128,11 +128,6 @@ function applyState(card, msg) {
|
|
|
128
128
|
error.textContent = msg.error || "";
|
|
129
129
|
error.classList.toggle("hidden", !msg.error);
|
|
130
130
|
}
|
|
131
|
-
var preview = card.querySelector(".worker-proposal-result");
|
|
132
|
-
if (preview && msg.resultPreview) {
|
|
133
|
-
preview.textContent = msg.resultPreview;
|
|
134
|
-
preview.classList.remove("hidden");
|
|
135
|
-
}
|
|
136
131
|
setControlsDisabled(card, status !== "pending");
|
|
137
132
|
var actions = card.querySelector(".worker-proposal-actions");
|
|
138
133
|
if (actions) actions.classList.toggle("hidden", autoAccepted);
|
|
@@ -248,9 +243,6 @@ export function renderWorkerProposal(msg) {
|
|
|
248
243
|
var error = document.createElement("div");
|
|
249
244
|
error.className = "worker-proposal-error hidden";
|
|
250
245
|
card.appendChild(error);
|
|
251
|
-
var result = document.createElement("div");
|
|
252
|
-
result.className = "worker-proposal-result hidden";
|
|
253
|
-
card.appendChild(result);
|
|
254
246
|
|
|
255
247
|
var actions = document.createElement("div");
|
|
256
248
|
actions.className = "worker-proposal-actions";
|
package/lib/sdk-bridge.js
CHANGED
|
@@ -2121,6 +2121,7 @@ function createSDKBridge(opts) {
|
|
|
2121
2121
|
// No active query — just store the model for next startQuery
|
|
2122
2122
|
session.model = model;
|
|
2123
2123
|
sm.saveSessionFile(session);
|
|
2124
|
+
sm.broadcastSessionList();
|
|
2124
2125
|
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
2125
2126
|
// Confirm against the session vendor so a non-default vendor does not
|
|
2126
2127
|
// receive model metadata labeled as the project's default adapter.
|
|
@@ -2134,6 +2135,7 @@ function createSDKBridge(opts) {
|
|
|
2134
2135
|
await session.queryInstance.setModel(model);
|
|
2135
2136
|
session.model = model;
|
|
2136
2137
|
sm.saveSessionFile(session);
|
|
2138
|
+
sm.broadcastSessionList();
|
|
2137
2139
|
sm.defaultModelByVendor = sm.defaultModelByVendor || {};
|
|
2138
2140
|
var sessionVendor = session.vendor || (adapter && adapter.vendor) || "claude";
|
|
2139
2141
|
sm.defaultModelByVendor[sessionVendor] = model;
|
|
@@ -2156,6 +2158,7 @@ function createSDKBridge(opts) {
|
|
|
2156
2158
|
sm.currentEffortByVendor[effortVendor] = clamped;
|
|
2157
2159
|
session.effort = clamped;
|
|
2158
2160
|
sm.saveSessionFile(session);
|
|
2161
|
+
sm.broadcastSessionList();
|
|
2159
2162
|
sm.currentEffort = clamped;
|
|
2160
2163
|
if (!session.queryInstance) {
|
|
2161
2164
|
return clamped;
|
package/package.json
CHANGED