newmark-agent 0.6.4 → 0.6.7
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/config.example.json +13 -3
- package/dist/cli-commands.d.ts +1 -1
- package/dist/cli-commands.js +53 -2
- package/dist/cli-discovery.js +3 -0
- package/dist/conversation-utility-host.bundle.cjs +2880 -1069
- package/dist/conversation-utility-host.js +94 -7
- package/dist/core/agent.d.ts +126 -12
- package/dist/core/agent.js +783 -172
- package/dist/core/agentKernelRunner.d.ts +7 -1
- package/dist/core/agentKernelRunner.js +156 -20
- package/dist/core/autoRouter.d.ts +49 -44
- package/dist/core/autoRouter.js +117 -302
- package/dist/core/config.js +27 -11
- package/dist/core/continuation/contracts.d.ts +1 -1
- package/dist/core/continuation/store.d.ts +91 -1
- package/dist/core/continuation/store.js +291 -61
- package/dist/core/conversationKernel.d.ts +49 -7
- package/dist/core/conversationKernel.js +303 -51
- package/dist/core/conversationStateDocument.d.ts +18 -0
- package/dist/core/conversationStateDocument.js +88 -0
- package/dist/core/conversationVisualBudget.d.ts +74 -0
- package/dist/core/conversationVisualBudget.js +153 -0
- package/dist/core/electronUtilityAgentClient.d.ts +3 -2
- package/dist/core/electronUtilityAgentClient.js +32 -8
- package/dist/core/electronUtilityRuntimePool.d.ts +28 -3
- package/dist/core/electronUtilityRuntimePool.js +93 -24
- package/dist/core/hostRuntimeHooks.d.ts +23 -0
- package/dist/core/hostRuntimeHooks.js +17 -0
- package/dist/core/jevDecision.d.ts +144 -0
- package/dist/core/jevDecision.js +226 -0
- package/dist/core/memoryProbe.d.ts +24 -0
- package/dist/core/memoryProbe.js +104 -0
- package/dist/core/mobilePairing.d.ts +7 -1
- package/dist/core/mobilePairing.js +9 -1
- package/dist/core/performanceDiagnostics.d.ts +1 -1
- package/dist/core/routeDecisionValidator.d.ts +86 -0
- package/dist/core/routeDecisionValidator.js +249 -0
- package/dist/core/routeEligibility.d.ts +52 -0
- package/dist/core/routeEligibility.js +108 -0
- package/dist/core/runtimeMemoryBudget.d.ts +6 -0
- package/dist/core/runtimeMemoryBudget.js +12 -0
- package/dist/core/utilityAgentProtocol.d.ts +8 -10
- package/dist/core/utilityHostToolRouter.js +2 -0
- package/dist/core/visualDownscale.d.ts +6 -0
- package/dist/core/visualDownscale.js +137 -0
- package/dist/core/wslAgentClient.d.ts +1 -2
- package/dist/core/wslAgentClient.js +0 -8
- package/dist/core/wslAgentProtocol.d.ts +1 -10
- package/dist/core/wslAgentRuntimePool.d.ts +1 -3
- package/dist/core/wslAgentRuntimePool.js +17 -22
- package/dist/llm/provider.d.ts +3 -1
- package/dist/llm/provider.js +53 -9
- package/dist/main.js +107 -30
- package/dist/preload.js +3 -1
- package/dist/server.js +30 -18
- package/dist/tools/computerUse.d.ts +4 -0
- package/dist/tools/computerUse.js +202 -4
- package/dist/tools/computerUsePowerShellHost.d.ts +6 -1
- package/dist/tools/computerUsePowerShellHost.js +105 -31
- package/dist/tools/index.js +4 -1
- package/dist/tui/src/adapters/core-runtime-adapter.js +13 -1
- package/dist/tui/src/i18n.js +12 -1
- package/dist/tui/src/render.js +9 -2
- package/dist/tui/src/settings-schema.js +19 -0
- package/dist/tui/src/state.js +69 -1
- package/dist/ui/index.html +651 -233
- package/dist/ui/lucide-sprite.svg +0 -8
- package/dist/wsl-agent-host.bundle.cjs +2685 -1044
- package/dist/wsl-agent-host.js +0 -3
- package/package.json +36 -9
package/dist/ui/index.html
CHANGED
|
@@ -185,7 +185,7 @@ try {
|
|
|
185
185
|
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
|
|
186
186
|
--duration-fast: 150ms;
|
|
187
187
|
--duration-normal: 250ms;
|
|
188
|
-
--duration-slow:
|
|
188
|
+
--duration-slow: 300ms;
|
|
189
189
|
|
|
190
190
|
/* Stable Newmark semantic layers. Components depend on meaning, not on an
|
|
191
191
|
upstream developer-preview framework's private tokens or brand palette. */
|
|
@@ -208,6 +208,17 @@ try {
|
|
|
208
208
|
--right-width: 380px;
|
|
209
209
|
--bottom-height: 200px;
|
|
210
210
|
--topbar-height: 38px;
|
|
211
|
+
|
|
212
|
+
/* Component aliases. These names are referenced by existing component rules
|
|
213
|
+
but were never defined, which silently dropped fills, borders and danger
|
|
214
|
+
colors. Map them onto the theme tokens so both themes resolve them. */
|
|
215
|
+
--control-bg: var(--glass-bg-1);
|
|
216
|
+
--panel: var(--glass-bg-1);
|
|
217
|
+
--panel-2: var(--modal-surface);
|
|
218
|
+
--bg-panel: rgb(var(--glass-rgb-2));
|
|
219
|
+
--text-primary: var(--text);
|
|
220
|
+
--accent-border: color-mix(in srgb, var(--accent) 30%, transparent);
|
|
221
|
+
--danger: var(--nm-state-danger);
|
|
211
222
|
}
|
|
212
223
|
|
|
213
224
|
/* Light theme */
|
|
@@ -371,7 +382,7 @@ html, body {
|
|
|
371
382
|
50% { transform: scale(1.04); filter: saturate(1.25); }
|
|
372
383
|
}
|
|
373
384
|
|
|
374
|
-
::selection { background:
|
|
385
|
+
::selection { background: var(--editor-selection); color: var(--text-bright); }
|
|
375
386
|
|
|
376
387
|
/* Open-source icon system: lucide-static sprite is copied into dist/ui at build time. */
|
|
377
388
|
.nm-icon {
|
|
@@ -413,7 +424,10 @@ html, body {
|
|
|
413
424
|
white-space: nowrap;
|
|
414
425
|
border: 0;
|
|
415
426
|
}
|
|
416
|
-
|
|
427
|
+
/* Pointer focus stays quiet. The button reset goes through :where() so it keeps
|
|
428
|
+
(0,1,0) specificity and the keyboard :focus-visible ring below can win. */
|
|
429
|
+
:where(button):focus { outline: none; }
|
|
430
|
+
select:focus, input:focus, textarea:focus, [contenteditable]:focus { outline: none; }
|
|
417
431
|
/* Liquid-glass controls use one continuous capsule edge. Equal-width icon
|
|
418
432
|
buttons naturally become circles; wider controls become end-rounded pills. */
|
|
419
433
|
button { border-radius: var(--radius-full) !important; }
|
|
@@ -435,6 +449,7 @@ button { border-radius: var(--radius-full) !important; }
|
|
|
435
449
|
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.10); border-radius: var(--radius-full); }
|
|
436
450
|
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.18); }
|
|
437
451
|
[data-theme="light"] ::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.12); }
|
|
452
|
+
[data-theme="light"] ::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.2); }
|
|
438
453
|
|
|
439
454
|
/* ============================================================
|
|
440
455
|
LAYER 1 - App Shell
|
|
@@ -444,7 +459,7 @@ button { border-radius: var(--radius-full) !important; }
|
|
|
444
459
|
flex-direction: column;
|
|
445
460
|
height: 100vh;
|
|
446
461
|
background:
|
|
447
|
-
radial-gradient(ellipse 80% 60% at 50% 0%,
|
|
462
|
+
radial-gradient(ellipse 80% 60% at 50% 0%, color-mix(in srgb, var(--accent) 6%, transparent) 0%, transparent 60%),
|
|
448
463
|
radial-gradient(ellipse 60% 50% at 80% 80%, rgba(56,212,160,0.04) 0%, transparent 50%),
|
|
449
464
|
var(--app-bg);
|
|
450
465
|
}
|
|
@@ -516,11 +531,11 @@ button { border-radius: var(--radius-full) !important; }
|
|
|
516
531
|
display: flex;
|
|
517
532
|
align-items: center;
|
|
518
533
|
justify-content: center;
|
|
519
|
-
transition: background var(--duration-fast) var(--ease-out-expo), color var(--duration-fast) var(--ease-out-expo);
|
|
534
|
+
transition: background var(--duration-fast) var(--ease-out-expo), color var(--duration-fast) var(--ease-out-expo), transform 90ms var(--ease-out-expo);
|
|
520
535
|
}
|
|
521
536
|
|
|
522
537
|
.top-btn:hover { background: var(--control-hover-bg); color: var(--text-bright); }
|
|
523
|
-
.top-btn:active { transform: scale(0.92)
|
|
538
|
+
.top-btn:active { transform: scale(0.92); }
|
|
524
539
|
.top-btn.close-btn:hover { background: rgba(255,68,68,0.2); color: var(--nm-state-danger); }
|
|
525
540
|
|
|
526
541
|
/* ============================================================
|
|
@@ -598,7 +613,7 @@ button { border-radius: var(--radius-full) !important; }
|
|
|
598
613
|
font-size: 0;
|
|
599
614
|
cursor: pointer;
|
|
600
615
|
color: var(--text-dim);
|
|
601
|
-
transition: background var(--duration-fast) var(--ease-out-expo), color var(--duration-fast) var(--ease-out-expo), transform var(--duration-fast) var(--ease-out-
|
|
616
|
+
transition: background var(--duration-fast) var(--ease-out-expo), color var(--duration-fast) var(--ease-out-expo), transform var(--duration-fast) var(--ease-out-expo);
|
|
602
617
|
flex-shrink: 0;
|
|
603
618
|
}
|
|
604
619
|
|
|
@@ -729,7 +744,7 @@ button.left-nav-icon { width: calc(100% - 12px); }
|
|
|
729
744
|
}
|
|
730
745
|
|
|
731
746
|
.left-nav-icon:hover { background: var(--control-hover-bg); color: var(--text-bright); }
|
|
732
|
-
.left-nav-icon:active { transform: scale(0.96)
|
|
747
|
+
.left-nav-icon:active { transform: scale(0.96); }
|
|
733
748
|
.left-nav-icon .icon { width: 24px; height: 20px; text-align: center; flex-shrink: 0; font-size: 0; }
|
|
734
749
|
.left-nav-icon span:not(.icon) {
|
|
735
750
|
display: inline-block;
|
|
@@ -939,7 +954,9 @@ button.left-ws-item {
|
|
|
939
954
|
color: var(--text-dim);
|
|
940
955
|
font-size: 11px;
|
|
941
956
|
cursor: pointer;
|
|
942
|
-
transition:
|
|
957
|
+
transition-property: color, background-color, border-color, filter, opacity, visibility, max-height, margin-top, margin-bottom, padding-top, padding-bottom;
|
|
958
|
+
transition-duration: var(--duration-fast);
|
|
959
|
+
transition-timing-function: var(--ease-out-expo);
|
|
943
960
|
flex-shrink: 0;
|
|
944
961
|
max-height: 30px;
|
|
945
962
|
opacity: 1;
|
|
@@ -949,7 +966,7 @@ button.left-ws-item {
|
|
|
949
966
|
#left-ws-add:hover {
|
|
950
967
|
border-color: var(--accent);
|
|
951
968
|
color: var(--accent);
|
|
952
|
-
background:
|
|
969
|
+
background: color-mix(in srgb, var(--accent) 6%, transparent);
|
|
953
970
|
}
|
|
954
971
|
|
|
955
972
|
#left.collapsed #left-ws-add {
|
|
@@ -990,7 +1007,7 @@ button.left-ws-item {
|
|
|
990
1007
|
}
|
|
991
1008
|
|
|
992
1009
|
.left-collapse-btn:hover { background: var(--control-hover-bg); color: var(--text-bright); }
|
|
993
|
-
.left-collapse-btn:active { transform: scale(0.92)
|
|
1010
|
+
.left-collapse-btn:active { transform: scale(0.92); }
|
|
994
1011
|
|
|
995
1012
|
/* ============================================================
|
|
996
1013
|
LEFT SECONDARY SIDEBAR
|
|
@@ -1041,10 +1058,11 @@ button.left-ws-item {
|
|
|
1041
1058
|
}
|
|
1042
1059
|
|
|
1043
1060
|
.sec-btn:hover { background: var(--control-hover-bg); border-color: var(--border-hover); }
|
|
1044
|
-
.sec-btn:active { transform: scale(0.96)
|
|
1061
|
+
.sec-btn:active { transform: scale(0.96); }
|
|
1045
1062
|
.sec-btn.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
|
|
1046
1063
|
.sec-btn.primary:hover { filter: brightness(1.1); }
|
|
1047
1064
|
.sec-btn:focus-visible {
|
|
1065
|
+
outline: none;
|
|
1048
1066
|
border-color: var(--accent);
|
|
1049
1067
|
box-shadow: 0 0 0 3px var(--accent-glow);
|
|
1050
1068
|
}
|
|
@@ -1075,7 +1093,7 @@ button.left-ws-item {
|
|
|
1075
1093
|
}
|
|
1076
1094
|
|
|
1077
1095
|
.conv-item:hover { background: var(--glass-bg-2); border-color: var(--border-hover); }
|
|
1078
|
-
.conv-item.active { border-color: var(--accent); background:
|
|
1096
|
+
.conv-item.active { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
|
|
1079
1097
|
.conv-item.marquee-border::before {
|
|
1080
1098
|
border-radius: var(--radius-full);
|
|
1081
1099
|
}
|
|
@@ -1378,13 +1396,13 @@ button.left-ws-item {
|
|
|
1378
1396
|
display: flex;
|
|
1379
1397
|
align-items: center;
|
|
1380
1398
|
justify-content: center;
|
|
1381
|
-
transition: background var(--duration-fast) var(--ease-out-expo), color var(--duration-fast) var(--ease-out-expo), border-color var(--duration-fast) var(--ease-out-expo), transform var(--duration-fast) var(--ease-out-
|
|
1399
|
+
transition: background var(--duration-fast) var(--ease-out-expo), color var(--duration-fast) var(--ease-out-expo), border-color var(--duration-fast) var(--ease-out-expo), transform var(--duration-fast) var(--ease-out-expo);
|
|
1382
1400
|
position: relative;
|
|
1383
1401
|
}
|
|
1384
1402
|
|
|
1385
1403
|
.tab-btn:hover { background: var(--control-hover-bg); color: var(--text-bright); }
|
|
1386
|
-
.tab-btn.active { background:
|
|
1387
|
-
.tab-btn:active { transform: scale(0.92)
|
|
1404
|
+
.tab-btn.active { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent); border-color: color-mix(in srgb, var(--accent) 20%, transparent); }
|
|
1405
|
+
.tab-btn:active { transform: scale(0.92); }
|
|
1388
1406
|
.tab-btn .label { display: none; }
|
|
1389
1407
|
|
|
1390
1408
|
.tab-divider {
|
|
@@ -1438,8 +1456,8 @@ button.left-ws-item {
|
|
|
1438
1456
|
white-space: nowrap;
|
|
1439
1457
|
}
|
|
1440
1458
|
|
|
1441
|
-
.ft-item:hover { background:
|
|
1442
|
-
.ft-item.selected { background:
|
|
1459
|
+
.ft-item:hover { background: var(--control-hover-bg); color: var(--text); }
|
|
1460
|
+
.ft-item.selected { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--text-bright); }
|
|
1443
1461
|
.ft-item .ft-icon { width: 16px; height: 16px; display: inline-flex; align-items: center; justify-content: center; text-align: center; flex-shrink: 0; font-size: 0; }
|
|
1444
1462
|
.ft-item .ft-name { overflow: hidden; text-overflow: ellipsis; }
|
|
1445
1463
|
.ft-item .ft-toggle { width: 14px; height: 16px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; font-size: 0; color: var(--text-dim); transform: rotate(90deg); transition: transform var(--duration-fast) var(--ease-out-expo); }
|
|
@@ -1479,7 +1497,7 @@ button.left-ws-item {
|
|
|
1479
1497
|
}
|
|
1480
1498
|
|
|
1481
1499
|
.et-btn:hover { background: var(--control-hover-bg); color: var(--text-bright); }
|
|
1482
|
-
.et-btn.active { background:
|
|
1500
|
+
.et-btn.active { background:color-mix(in srgb, var(--accent) 16%, transparent); color:var(--accent2); border-color:color-mix(in srgb, var(--accent) 55%, transparent); }
|
|
1483
1501
|
.et-btn .nm-icon { width:15px; height:15px; }
|
|
1484
1502
|
.et-btn.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
|
|
1485
1503
|
.et-btn.primary:hover { filter: brightness(1.1); }
|
|
@@ -1712,7 +1730,6 @@ button.left-ws-item {
|
|
|
1712
1730
|
line-height: 0;
|
|
1713
1731
|
}
|
|
1714
1732
|
.math-symbol { display: inline-block; }
|
|
1715
|
-
}
|
|
1716
1733
|
|
|
1717
1734
|
/* Subagent panel */
|
|
1718
1735
|
.subagent-item {
|
|
@@ -2117,7 +2134,7 @@ button.left-ws-item {
|
|
|
2117
2134
|
}
|
|
2118
2135
|
|
|
2119
2136
|
.shell-btn:hover { background: var(--control-hover-bg); color: var(--text-bright); }
|
|
2120
|
-
.shell-btn:active { transform: scale(0.95)
|
|
2137
|
+
.shell-btn:active { transform: scale(0.95); }
|
|
2121
2138
|
|
|
2122
2139
|
.shell-toggle-btn {
|
|
2123
2140
|
margin-left: auto;
|
|
@@ -2220,7 +2237,6 @@ button.left-ws-item {
|
|
|
2220
2237
|
.conversation-load-earlier:hover,
|
|
2221
2238
|
.conversation-load-earlier:focus-visible { background: var(--glass-bg-2); border-color: var(--border-hover); outline: none; }
|
|
2222
2239
|
.conversation-load-earlier:disabled { opacity: 0.6; cursor: default; }
|
|
2223
|
-
}
|
|
2224
2240
|
|
|
2225
2241
|
#chat-area:has(> .chat-msg) {
|
|
2226
2242
|
background-image:
|
|
@@ -2331,11 +2347,18 @@ button.left-ws-item {
|
|
|
2331
2347
|
line-height: 1.6;
|
|
2332
2348
|
word-wrap: break-word;
|
|
2333
2349
|
overflow-wrap: break-word;
|
|
2334
|
-
transform: translateZ(0);
|
|
2335
2350
|
animation: msg-in 250ms var(--ease-out-expo);
|
|
2336
2351
|
position: relative;
|
|
2337
2352
|
}
|
|
2338
2353
|
|
|
2354
|
+
/* Restored transcript rows are already present in the conversation. Avoid
|
|
2355
|
+
animating a large history batch or pinning every row to its own compositor
|
|
2356
|
+
layer; only newly arriving live messages use the entrance motion. */
|
|
2357
|
+
.chat-msg.history-static {
|
|
2358
|
+
animation: none;
|
|
2359
|
+
transform: none;
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2339
2362
|
.chat-msg::after {
|
|
2340
2363
|
content: "";
|
|
2341
2364
|
position: absolute;
|
|
@@ -2350,8 +2373,8 @@ button.left-ws-item {
|
|
|
2350
2373
|
}
|
|
2351
2374
|
|
|
2352
2375
|
@keyframes msg-in {
|
|
2353
|
-
from { opacity: 0; transform: translateY(8px)
|
|
2354
|
-
to { opacity: 1; transform: translateY(0)
|
|
2376
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
2377
|
+
to { opacity: 1; transform: translateY(0); }
|
|
2355
2378
|
}
|
|
2356
2379
|
|
|
2357
2380
|
.chat-msg.user {
|
|
@@ -2467,7 +2490,6 @@ button.left-ws-item {
|
|
|
2467
2490
|
}
|
|
2468
2491
|
|
|
2469
2492
|
.msg-action-btn:hover { color: var(--text); background: var(--glass-bg-2); }
|
|
2470
|
-
.msg-action-btn.route-rating-selected { color: var(--accent); background: var(--glass-bg-2); }
|
|
2471
2493
|
.msg-action-btn:disabled { cursor: default; opacity: 0.62; }
|
|
2472
2494
|
|
|
2473
2495
|
.message-inline-editor { display: grid; gap: 8px; margin-top: 4px; }
|
|
@@ -2487,7 +2509,7 @@ button.left-ws-item {
|
|
|
2487
2509
|
.conversation-branch-pager { display:flex; justify-content:flex-end; align-items:center; gap:0; margin-top:7px; color:var(--text-dim); font-size:11px; }
|
|
2488
2510
|
.branch-page-arrow { cursor:pointer; color:var(--text-dim); padding:1px 3px; transition:color var(--duration-fast) var(--ease-out-expo), background var(--duration-fast) var(--ease-out-expo); border-radius:var(--radius-sm); }
|
|
2489
2511
|
.branch-page-arrow:hover { color:var(--text-bright); background:var(--control-hover-bg); }
|
|
2490
|
-
.branch-page-arrow:active { transform:scale(0.92)
|
|
2512
|
+
.branch-page-arrow:active { transform:scale(0.92); }
|
|
2491
2513
|
|
|
2492
2514
|
.chat-msg .msg-body .md-rendered {
|
|
2493
2515
|
padding: 0;
|
|
@@ -2919,8 +2941,8 @@ button.todo-item { background: transparent; }
|
|
|
2919
2941
|
margin: 8px 0;
|
|
2920
2942
|
padding: 10px 14px;
|
|
2921
2943
|
border-radius: var(--radius-md);
|
|
2922
|
-
background:
|
|
2923
|
-
border: 1px solid
|
|
2944
|
+
background: color-mix(in srgb, var(--accent) 8%, transparent);
|
|
2945
|
+
border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
|
|
2924
2946
|
}
|
|
2925
2947
|
|
|
2926
2948
|
.option-question {
|
|
@@ -2945,9 +2967,9 @@ button.todo-item { background: transparent; }
|
|
|
2945
2967
|
transition: background var(--duration-fast) var(--ease-out-expo), border-color var(--duration-fast) var(--ease-out-expo);
|
|
2946
2968
|
}
|
|
2947
2969
|
|
|
2948
|
-
.option-btn:hover { background:
|
|
2949
|
-
.option-btn:active { transform: scale(0.96)
|
|
2950
|
-
.option-btn.selected { background:
|
|
2970
|
+
.option-btn:hover { background: color-mix(in srgb, var(--accent) 15%, transparent); border-color: var(--accent); color: var(--text-bright); }
|
|
2971
|
+
.option-btn:active { transform: scale(0.96); }
|
|
2972
|
+
.option-btn.selected { background:color-mix(in srgb, var(--accent) 24%, transparent); border-color:var(--accent); color:var(--text-bright); }
|
|
2951
2973
|
.option-btn:disabled { cursor:default; opacity:.55; }
|
|
2952
2974
|
.option-btn.selected:disabled { opacity:1; }
|
|
2953
2975
|
.option-desc {
|
|
@@ -2986,14 +3008,14 @@ button.todo-item { background: transparent; }
|
|
|
2986
3008
|
padding: 0 12px;
|
|
2987
3009
|
border: 1px solid var(--accent);
|
|
2988
3010
|
border-radius: var(--radius-md);
|
|
2989
|
-
background:
|
|
3011
|
+
background: color-mix(in srgb, var(--accent) 14%, transparent);
|
|
2990
3012
|
color: var(--text-bright);
|
|
2991
3013
|
font: 12px var(--font-ui);
|
|
2992
3014
|
cursor: pointer;
|
|
2993
3015
|
transition: background var(--duration-fast) var(--ease-out-expo);
|
|
2994
3016
|
}
|
|
2995
3017
|
|
|
2996
|
-
.option-manual-submit:hover { background:
|
|
3018
|
+
.option-manual-submit:hover { background: color-mix(in srgb, var(--accent) 24%, transparent); }
|
|
2997
3019
|
.option-manual-submit:disabled { opacity: .55; cursor: default; }
|
|
2998
3020
|
|
|
2999
3021
|
/* ============================================================
|
|
@@ -3043,7 +3065,7 @@ button.todo-item { background: transparent; }
|
|
|
3043
3065
|
border-radius: 8px;
|
|
3044
3066
|
box-shadow: 0 6px 18px rgba(0,0,0,0.12);
|
|
3045
3067
|
overflow: hidden;
|
|
3046
|
-
transform: translateY(0)
|
|
3068
|
+
transform: translateY(0);
|
|
3047
3069
|
animation: fade-in-up 180ms var(--ease-out-expo);
|
|
3048
3070
|
}
|
|
3049
3071
|
[data-theme="light"] .stack-card {
|
|
@@ -3115,6 +3137,7 @@ button.todo-item { background: transparent; }
|
|
|
3115
3137
|
.stack-icon-btn.warn { color: var(--nm-state-warning); }
|
|
3116
3138
|
.stack-icon-btn.danger { color: var(--nm-state-danger); }
|
|
3117
3139
|
.stack-icon-btn:focus-visible {
|
|
3140
|
+
outline: none;
|
|
3118
3141
|
border-color: var(--accent);
|
|
3119
3142
|
box-shadow: 0 0 0 3px var(--accent-glow);
|
|
3120
3143
|
}
|
|
@@ -3127,12 +3150,12 @@ button.todo-item { background: transparent; }
|
|
|
3127
3150
|
#goal-bar {
|
|
3128
3151
|
border-bottom: none;
|
|
3129
3152
|
border-radius: 8px 8px 0 0;
|
|
3130
|
-
border-color:
|
|
3131
|
-
background: linear-gradient(90deg,
|
|
3153
|
+
border-color: color-mix(in srgb, var(--accent) 24%, transparent);
|
|
3154
|
+
background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 10%, transparent), rgba(24,24,24,0.94) 34%);
|
|
3132
3155
|
box-shadow: none;
|
|
3133
3156
|
}
|
|
3134
3157
|
[data-theme="light"] #goal-bar {
|
|
3135
|
-
background: linear-gradient(90deg,
|
|
3158
|
+
background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 13%, transparent), rgba(255,255,255,0.82) 36%, rgba(255,255,255,0.72) 100%);
|
|
3136
3159
|
border-color: rgba(74,104,223,0.2);
|
|
3137
3160
|
}
|
|
3138
3161
|
[data-theme="light"] #goal-bar.goal-paused {
|
|
@@ -3140,7 +3163,7 @@ button.todo-item { background: transparent; }
|
|
|
3140
3163
|
}
|
|
3141
3164
|
|
|
3142
3165
|
#goal-bar .stack-row { grid-template-columns: auto minmax(0,1fr) auto; }
|
|
3143
|
-
#goal-bar .stack-row::before { content:""; width:6px; height:6px; border-radius:50%; background:var(--accent); box-shadow:0 0 0 3px
|
|
3166
|
+
#goal-bar .stack-row::before { content:""; width:6px; height:6px; border-radius:50%; background:var(--accent); box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent); }
|
|
3144
3167
|
#goal-bar.goal-paused .stack-row::before { background:#f4c95d; box-shadow:0 0 0 3px rgba(244,201,93,.12); }
|
|
3145
3168
|
|
|
3146
3169
|
#goal-bar .goal-text {
|
|
@@ -3222,7 +3245,7 @@ button.todo-item { background: transparent; }
|
|
|
3222
3245
|
transition: background var(--duration-fast) var(--ease-out-expo), opacity var(--duration-fast) var(--ease-out-expo);
|
|
3223
3246
|
}
|
|
3224
3247
|
|
|
3225
|
-
.todo-item:hover { background:
|
|
3248
|
+
.todo-item:hover { background: var(--control-hover-bg); }
|
|
3226
3249
|
|
|
3227
3250
|
.todo-check {
|
|
3228
3251
|
width: 14px;
|
|
@@ -3809,6 +3832,18 @@ button.todo-item { background: transparent; }
|
|
|
3809
3832
|
position: relative;
|
|
3810
3833
|
z-index: 1;
|
|
3811
3834
|
}
|
|
3835
|
+
/* Carried content wrappers are the destination label itself, not a bare
|
|
3836
|
+
stacking child. They author their own inset box, and the fallback rule above
|
|
3837
|
+
outranks them (.liquid-selection-float > :not(...) is two class-level
|
|
3838
|
+
selectors against one), so their authored position has to be restated with
|
|
3839
|
+
the same selector weight and a later source order. Without it a pressed
|
|
3840
|
+
fixed control (left rail New row, rail collapse button) and a carried
|
|
3841
|
+
conversation row lose inset:0 / inset:9px, become shrink-to-fit flex items
|
|
3842
|
+
and paint left-flush inside the glass instead of staying centered. */
|
|
3843
|
+
.liquid-selection-float > .liquid-fixed-control-content,
|
|
3844
|
+
.liquid-selection-float > .conversation-drag-content {
|
|
3845
|
+
position: absolute;
|
|
3846
|
+
}
|
|
3812
3847
|
@keyframes model-menu-in { from { opacity: 0; transform: translateY(5px) scale(.985); } to { opacity: 1; transform: none; } }
|
|
3813
3848
|
|
|
3814
3849
|
.newmark-select-shell {
|
|
@@ -3917,6 +3952,9 @@ button.todo-item { background: transparent; }
|
|
|
3917
3952
|
|
|
3918
3953
|
.tool-select:hover { border-color: var(--border-hover); }
|
|
3919
3954
|
.tool-select:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-glow); }
|
|
3955
|
+
/* The enhanced native <select> is still a Tab stop but is transparent; mirror its
|
|
3956
|
+
keyboard focus on the proxy button that immediately follows it. */
|
|
3957
|
+
.newmark-select-native:focus-visible + .newmark-select-button { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-glow); }
|
|
3920
3958
|
|
|
3921
3959
|
.context-token-wrap {
|
|
3922
3960
|
position: relative;
|
|
@@ -3980,6 +4018,20 @@ button.todo-item { background: transparent; }
|
|
|
3980
4018
|
opacity: 1;
|
|
3981
4019
|
transform: scale(1.08);
|
|
3982
4020
|
}
|
|
4021
|
+
/* The ring's donut mask clips its own outline/box-shadow, so the keyboard ring is
|
|
4022
|
+
painted by the wrapper: 2px wide, 2px outside the 16px ring (global indicator). */
|
|
4023
|
+
.context-token-wrap:has(> #context-token-ring:focus-visible)::before {
|
|
4024
|
+
content: "";
|
|
4025
|
+
position: absolute;
|
|
4026
|
+
left: 50%;
|
|
4027
|
+
top: 50%;
|
|
4028
|
+
width: 20px;
|
|
4029
|
+
height: 20px;
|
|
4030
|
+
margin: -10px 0 0 -10px;
|
|
4031
|
+
border-radius: 50%;
|
|
4032
|
+
box-shadow: 0 0 0 2px var(--accent);
|
|
4033
|
+
pointer-events: none;
|
|
4034
|
+
}
|
|
3983
4035
|
|
|
3984
4036
|
#context-token-tooltip {
|
|
3985
4037
|
display: none;
|
|
@@ -4026,9 +4078,9 @@ button.todo-item { background: transparent; }
|
|
|
4026
4078
|
transition: background var(--duration-fast) var(--ease-out-expo), color var(--duration-fast) var(--ease-out-expo);
|
|
4027
4079
|
}
|
|
4028
4080
|
|
|
4029
|
-
.mode-toggle-btn:hover { background:
|
|
4030
|
-
.mode-toggle-btn.active { background:
|
|
4031
|
-
.mode-toggle-btn:active { transform: scale(0.95)
|
|
4081
|
+
.mode-toggle-btn:hover { background: var(--control-hover-bg); color: var(--text); }
|
|
4082
|
+
.mode-toggle-btn.active { background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent); border-radius: var(--radius-full); }
|
|
4083
|
+
.mode-toggle-btn:active { transform: scale(0.95); }
|
|
4032
4084
|
|
|
4033
4085
|
.workspace-type-pager {
|
|
4034
4086
|
display: grid;
|
|
@@ -4059,11 +4111,11 @@ button.todo-item { background: transparent; }
|
|
|
4059
4111
|
display: flex;
|
|
4060
4112
|
align-items: center;
|
|
4061
4113
|
justify-content: center;
|
|
4062
|
-
transition: transform var(--duration-fast) var(--ease-out-
|
|
4114
|
+
transition: transform var(--duration-fast) var(--ease-out-expo), box-shadow var(--duration-fast) var(--ease-out-expo);
|
|
4063
4115
|
box-shadow: 0 2px 12px var(--accent-glow);
|
|
4064
4116
|
}
|
|
4065
4117
|
|
|
4066
|
-
#submit-btn:hover { transform: scale(1.08); box-shadow: 0 4px 20px
|
|
4118
|
+
#submit-btn:hover { transform: scale(1.08); box-shadow: 0 4px 20px color-mix(in srgb, var(--accent) 50%, transparent); }
|
|
4067
4119
|
#submit-btn:active { transform: scale(0.92); transition: transform 80ms var(--ease-out-expo); }
|
|
4068
4120
|
#submit-btn::after { display: none !important; }
|
|
4069
4121
|
#submit-btn:active { transform: none; scale: 1; }
|
|
@@ -4189,13 +4241,13 @@ button.todo-item { background: transparent; }
|
|
|
4189
4241
|
|
|
4190
4242
|
.working-glow {
|
|
4191
4243
|
animation: none;
|
|
4192
|
-
box-shadow: 0 0 16px var(--accent-glow), 0 0 40px
|
|
4193
|
-
border-color:
|
|
4244
|
+
box-shadow: 0 0 16px var(--accent-glow), 0 0 40px color-mix(in srgb, var(--accent) 10%, transparent);
|
|
4245
|
+
border-color: color-mix(in srgb, var(--accent) 40%, transparent) !important;
|
|
4194
4246
|
}
|
|
4195
4247
|
|
|
4196
4248
|
@keyframes working-border-glow {
|
|
4197
|
-
0%, 100% { box-shadow: 0 0 12px var(--accent-glow), 0 0 30px
|
|
4198
|
-
50% { box-shadow: 0 0 24px
|
|
4249
|
+
0%, 100% { box-shadow: 0 0 12px var(--accent-glow), 0 0 30px color-mix(in srgb, var(--accent) 8%, transparent); }
|
|
4250
|
+
50% { box-shadow: 0 0 24px color-mix(in srgb, var(--accent) 50%, transparent), 0 0 60px color-mix(in srgb, var(--accent) 15%, transparent); }
|
|
4199
4251
|
}
|
|
4200
4252
|
|
|
4201
4253
|
/* ============================================================
|
|
@@ -4456,8 +4508,8 @@ button.todo-item { background: transparent; }
|
|
|
4456
4508
|
transition: background var(--duration-fast) var(--ease-out-expo), color var(--duration-fast) var(--ease-out-expo), border-color var(--duration-fast) var(--ease-out-expo);
|
|
4457
4509
|
}
|
|
4458
4510
|
|
|
4459
|
-
.stab-btn:hover { background:
|
|
4460
|
-
.stab-btn.active { background:
|
|
4511
|
+
.stab-btn:hover { background: var(--control-hover-bg); color: var(--text); }
|
|
4512
|
+
.stab-btn.active { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent); border-color: color-mix(in srgb, var(--accent) 25%, transparent); }
|
|
4461
4513
|
.stab-btn:focus-visible { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-glow); color:var(--text-bright); }
|
|
4462
4514
|
|
|
4463
4515
|
.plugin-tabs { overflow-x:auto; scrollbar-width:thin; }
|
|
@@ -4773,6 +4825,10 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
4773
4825
|
box-shadow: 0 2px 6px rgba(0,0,0,.3);
|
|
4774
4826
|
transition: width 120ms var(--ease-out-expo), height 120ms var(--ease-out-expo), transform 120ms var(--ease-out-expo), box-shadow 120ms var(--ease-out-expo);
|
|
4775
4827
|
}
|
|
4828
|
+
.setting-control input[type="range"]:focus-visible::-webkit-slider-thumb,
|
|
4829
|
+
#settings-glass-opacity:focus-visible::-webkit-slider-thumb {
|
|
4830
|
+
box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent), 0 2px 6px rgba(0,0,0,.3);
|
|
4831
|
+
}
|
|
4776
4832
|
#settings-glass-opacity.liquid-range-dragging::-webkit-slider-thumb {
|
|
4777
4833
|
width: 26px;
|
|
4778
4834
|
height: 20px;
|
|
@@ -5110,9 +5166,9 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
5110
5166
|
.memory-lab-node.selected {
|
|
5111
5167
|
min-height: 52px;
|
|
5112
5168
|
border-color: var(--accent);
|
|
5113
|
-
background:
|
|
5169
|
+
background: color-mix(in srgb, var(--accent) 16%, transparent);
|
|
5114
5170
|
color: var(--text-bright);
|
|
5115
|
-
box-shadow: 0 0 0 1px
|
|
5171
|
+
box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 8%, transparent);
|
|
5116
5172
|
}
|
|
5117
5173
|
.memory-lab-node-label {
|
|
5118
5174
|
min-width: 0;
|
|
@@ -5316,8 +5372,8 @@ textarea.mcp-input { min-height:76px; resize:vertical; font-family:var(--font-mo
|
|
|
5316
5372
|
}
|
|
5317
5373
|
.memory-lab-overview-node.focus {
|
|
5318
5374
|
border-color: var(--accent);
|
|
5319
|
-
background:
|
|
5320
|
-
box-shadow: 0 0 0 5px
|
|
5375
|
+
background: color-mix(in srgb, var(--accent) 20%, transparent);
|
|
5376
|
+
box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 11%, transparent), 0 12px 28px rgba(0,0,0,0.24);
|
|
5321
5377
|
}
|
|
5322
5378
|
.memory-lab-overview-node.dim { opacity: 0.18; }
|
|
5323
5379
|
.memory-lab-overview-stage.zoom-dots .memory-lab-overview-node {
|
|
@@ -5422,9 +5478,9 @@ html.newmark-memory-overview-viewer body {
|
|
|
5422
5478
|
}
|
|
5423
5479
|
[data-theme="light"] .memory-lab-node.selected,
|
|
5424
5480
|
[data-theme="light"] .memory-lab-overview-node.focus {
|
|
5425
|
-
background:
|
|
5481
|
+
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
|
5426
5482
|
color: var(--text-bright);
|
|
5427
|
-
box-shadow: 0 0 0 3px
|
|
5483
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 8%, transparent);
|
|
5428
5484
|
}
|
|
5429
5485
|
[data-theme="light"] .memory-lab-overview-grid {
|
|
5430
5486
|
opacity: 0.34;
|
|
@@ -5442,15 +5498,15 @@ html.newmark-memory-overview-viewer body {
|
|
|
5442
5498
|
.model-chip {
|
|
5443
5499
|
display:flex; align-items:center; gap:12px; box-sizing:border-box;
|
|
5444
5500
|
width:100%; min-width:0; margin:6px 0; padding:10px 12px;
|
|
5445
|
-
border-radius:var(--radius-sm); background:
|
|
5446
|
-
border:1px solid
|
|
5501
|
+
border-radius:var(--radius-sm); background:color-mix(in srgb, var(--accent) 10%, transparent);
|
|
5502
|
+
border:1px solid color-mix(in srgb, var(--accent) 20%, transparent); font-size:11px; color:var(--text-accent);
|
|
5447
5503
|
}
|
|
5448
5504
|
.model-chip-info { flex:1; min-width:0; text-align:left; overflow-wrap:anywhere; }
|
|
5449
5505
|
.model-chip-actions { display:flex; flex:0 0 auto; align-items:center; gap:6px; margin-left:auto; }
|
|
5450
5506
|
.model-response-warning { color:var(--text-dim); font-size:inherit; margin-left:4px; font-weight:400; }
|
|
5451
5507
|
.model-enable-switch { width:38px; height:22px; padding:2px; border-radius:999px; border:1px solid var(--glass-border-1); background:var(--glass-bg-1); cursor:pointer; flex:none; }
|
|
5452
5508
|
.model-enable-thumb { display:block; width:16px; height:16px; border-radius:50%; background:rgba(255,255,255,.55); transform:translateX(0); transition:transform 180ms var(--ease-out-expo); }
|
|
5453
|
-
.model-enable-switch[aria-checked="true"] { background:
|
|
5509
|
+
.model-enable-switch[aria-checked="true"] { background:color-mix(in srgb, var(--accent) 35%, transparent); }
|
|
5454
5510
|
.model-enable-switch[aria-checked="true"] .model-enable-thumb { transform:translateX(16px); }
|
|
5455
5511
|
.model-enable-switch:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
|
|
5456
5512
|
.model-enable-switch:disabled { opacity:.6; }
|
|
@@ -5524,7 +5580,7 @@ html.newmark-memory-overview-viewer body {
|
|
|
5524
5580
|
padding: 8px 20px;
|
|
5525
5581
|
border: 1px solid var(--accent);
|
|
5526
5582
|
border-radius: var(--radius-md);
|
|
5527
|
-
background:
|
|
5583
|
+
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
|
5528
5584
|
color: var(--accent);
|
|
5529
5585
|
font-size: 12px;
|
|
5530
5586
|
font-family: var(--font-ui);
|
|
@@ -5532,7 +5588,7 @@ html.newmark-memory-overview-viewer body {
|
|
|
5532
5588
|
transition: background var(--duration-fast) var(--ease-out-expo);
|
|
5533
5589
|
}
|
|
5534
5590
|
|
|
5535
|
-
.fuzzy-inject-btn:hover { background:
|
|
5591
|
+
.fuzzy-inject-btn:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
|
|
5536
5592
|
|
|
5537
5593
|
/* Archive list */
|
|
5538
5594
|
.archive-item {
|
|
@@ -5729,14 +5785,14 @@ html.newmark-memory-overview-viewer body {
|
|
|
5729
5785
|
transition: background var(--duration-fast) var(--ease-out-expo), color var(--duration-fast) var(--ease-out-expo);
|
|
5730
5786
|
}
|
|
5731
5787
|
|
|
5732
|
-
.flow-new-btn:hover { background:
|
|
5788
|
+
.flow-new-btn:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); color: var(--accent); border-color: var(--accent); }
|
|
5733
5789
|
|
|
5734
5790
|
/* ============================================================
|
|
5735
5791
|
ANIMATIONS & UTILITY
|
|
5736
5792
|
============================================================ */
|
|
5737
5793
|
@keyframes fade-in-up {
|
|
5738
|
-
from { opacity: 0; transform: translateY(12px)
|
|
5739
|
-
to { opacity: 1; transform: translateY(0)
|
|
5794
|
+
from { opacity: 0; transform: translateY(12px); }
|
|
5795
|
+
to { opacity: 1; transform: translateY(0); }
|
|
5740
5796
|
}
|
|
5741
5797
|
|
|
5742
5798
|
.hidden { display: none !important; }
|
|
@@ -5962,8 +6018,12 @@ html.newmark-memory-overview-viewer body {
|
|
|
5962
6018
|
.liquid-glass-popup,
|
|
5963
6019
|
.liquid-glass-popup.liquid-popup-exit,
|
|
5964
6020
|
.liquid-menu-color-block,
|
|
5965
|
-
.liquid-selection-float { transition-duration:
|
|
6021
|
+
.liquid-selection-float { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
|
|
5966
6022
|
.liquid-glass-popup::before { opacity: 0; }
|
|
6023
|
+
/* These two-class / id !important transitions outrank the rules above. */
|
|
6024
|
+
.liquid-selection-float.liquid-selection-landing,
|
|
6025
|
+
.conversation-selection-float.conversation-drag-following { transition-duration: 0.01ms !important; }
|
|
6026
|
+
#left.width-animating { transition-duration: 0.01ms !important; }
|
|
5967
6027
|
}
|
|
5968
6028
|
@media (max-width: 900px), (max-height: 680px) {
|
|
5969
6029
|
.liquid-glass-popup { --liquid-popup-blur: calc(var(--glass-blur-3) * 3); }
|
|
@@ -6058,7 +6118,7 @@ html.newmark-memory-overview-viewer body {
|
|
|
6058
6118
|
}
|
|
6059
6119
|
.liquid-glass-popup[data-liquid-popup-feedback='outward']::after { opacity: 0; }
|
|
6060
6120
|
@media (prefers-reduced-motion: reduce) {
|
|
6061
|
-
.liquid-popup-feedback-surface { transition-duration:
|
|
6121
|
+
.liquid-popup-feedback-surface { transition-duration: 0.01ms !important; }
|
|
6062
6122
|
}
|
|
6063
6123
|
.model-select-menu-option,
|
|
6064
6124
|
.newmark-select-option,
|
|
@@ -6168,10 +6228,12 @@ button::after {
|
|
|
6168
6228
|
border-radius: inherit;
|
|
6169
6229
|
opacity: 0;
|
|
6170
6230
|
background: var(--liquid-refraction);
|
|
6171
|
-
backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%) brightness(1.04);
|
|
6172
|
-
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%) brightness(1.04);
|
|
6173
6231
|
box-shadow: var(--liquid-top-highlight), var(--liquid-inner-shadow), 0 4px 14px rgba(0,0,0,0.18);
|
|
6174
|
-
|
|
6232
|
+
/* The backdrop blur is attached only while the plate can be seen. Chromium
|
|
6233
|
+
still paints and composites an opacity-0 layer that has a backdrop-filter,
|
|
6234
|
+
so a resting blur cost one backdrop layer per visible button. The delayed
|
|
6235
|
+
step keeps the blur through the 90ms release fade. */
|
|
6236
|
+
transition: opacity 90ms var(--ease-out-expo), backdrop-filter 0s linear 90ms;
|
|
6175
6237
|
}
|
|
6176
6238
|
button:hover:not(:disabled) {
|
|
6177
6239
|
filter: brightness(1.06);
|
|
@@ -6182,7 +6244,12 @@ button:active:not(:disabled) {
|
|
|
6182
6244
|
background-color: transparent !important;
|
|
6183
6245
|
box-shadow: none;
|
|
6184
6246
|
}
|
|
6185
|
-
button:active:not(:disabled)::after {
|
|
6247
|
+
button:active:not(:disabled)::after {
|
|
6248
|
+
opacity: 1;
|
|
6249
|
+
backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%) brightness(1.04);
|
|
6250
|
+
-webkit-backdrop-filter: blur(calc(var(--glass-blur-3) * 4)) saturate(145%) brightness(1.04);
|
|
6251
|
+
transition: opacity 90ms var(--ease-out-expo);
|
|
6252
|
+
}
|
|
6186
6253
|
button:disabled { opacity: 0.5; }
|
|
6187
6254
|
|
|
6188
6255
|
/* Unified glass controls are rendered by a real top-layer float. Their local
|
|
@@ -6220,6 +6287,21 @@ button.conversation-work-run-head::after,
|
|
|
6220
6287
|
button.shell-block-header::after,
|
|
6221
6288
|
button.diff-block-header::after,
|
|
6222
6289
|
button.work-review-head::after { display: none !important; }
|
|
6290
|
+
/* Without a press plate, a held utility action keeps its own hover wash
|
|
6291
|
+
instead of dropping back to the bare row, and it does not take the
|
|
6292
|
+
plate's outward lift. */
|
|
6293
|
+
.top-btn:active:not(:disabled),
|
|
6294
|
+
.conv-more-btn:active:not(:disabled),
|
|
6295
|
+
.provider-icon-btn:active:not(:disabled),
|
|
6296
|
+
.work-review-toggle:active:not(:disabled) {
|
|
6297
|
+
scale: 1;
|
|
6298
|
+
background-color: var(--control-hover-bg) !important;
|
|
6299
|
+
}
|
|
6300
|
+
.msg-action-btn:active:not(:disabled) {
|
|
6301
|
+
scale: 1;
|
|
6302
|
+
background-color: var(--glass-bg-2) !important;
|
|
6303
|
+
}
|
|
6304
|
+
.top-btn.close-btn:active:not(:disabled) { background-color: rgba(255,68,68,0.2) !important; }
|
|
6223
6305
|
button.conversation-work-run-head:active:not(:disabled),
|
|
6224
6306
|
button.shell-block-header:active:not(:disabled),
|
|
6225
6307
|
button.diff-block-header:active:not(:disabled),
|
|
@@ -6720,14 +6802,6 @@ input[type="checkbox"].liquid-switch-dragging::before {
|
|
|
6720
6802
|
<path d="M6 4h1a2 2 0 0 1 2 2 2 2 0 0 1 2-2h1" />
|
|
6721
6803
|
<path d="M9 6v12" />
|
|
6722
6804
|
</symbol>
|
|
6723
|
-
<symbol id="thumbs-down" viewBox="0 0 24 24">
|
|
6724
|
-
<path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z" />
|
|
6725
|
-
<path d="M17 14V2" />
|
|
6726
|
-
</symbol>
|
|
6727
|
-
<symbol id="thumbs-up" viewBox="0 0 24 24">
|
|
6728
|
-
<path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z" />
|
|
6729
|
-
<path d="M7 10v12" />
|
|
6730
|
-
</symbol>
|
|
6731
6805
|
<symbol id="trash-2" viewBox="0 0 24 24">
|
|
6732
6806
|
<path d="M10 11v6" />
|
|
6733
6807
|
<path d="M14 11v6" />
|
|
@@ -7208,12 +7282,15 @@ var state = {
|
|
|
7208
7282
|
feedbackLevel: 'default',
|
|
7209
7283
|
autoSwitch: 'off',
|
|
7210
7284
|
autoSwitchScope: 'all',
|
|
7285
|
+
/**
|
|
7286
|
+
* Auto decision model ("JEV") slot as reported by the backend. `locked`
|
|
7287
|
+
* means Auto is off, so the decision model can neither be edited nor move a
|
|
7288
|
+
* model; every control in the JEV card is disabled while it is true.
|
|
7289
|
+
*/
|
|
7290
|
+
jevDecisionModel: null,
|
|
7211
7291
|
openAIApiMode: 'chat_stream',
|
|
7212
7292
|
fallbackOnUnavailable: 'off',
|
|
7213
7293
|
switchTendency: 'balanced',
|
|
7214
|
-
autoRouteRatingAvailable: false,
|
|
7215
|
-
autoRouteRatingRouteId: '',
|
|
7216
|
-
autoRouteRatingScore: 0,
|
|
7217
7294
|
workspaces: [],
|
|
7218
7295
|
currentWorkspace: '',
|
|
7219
7296
|
currentWorkspaceId: '',
|
|
@@ -7225,6 +7302,10 @@ var state = {
|
|
|
7225
7302
|
pendingOptionAnswers: {},
|
|
7226
7303
|
pendingOptionBatchKey: '',
|
|
7227
7304
|
pendingOptionsByTarget: {},
|
|
7305
|
+
// Runtime key of the conversation whose transcript is currently in #chat-area.
|
|
7306
|
+
// The pending-option block is appended to that transcript, so an async
|
|
7307
|
+
// snapshot may only (re)paint it while it still owns the on-screen area.
|
|
7308
|
+
renderedChatTargetKey: '',
|
|
7228
7309
|
pendingOptionAnswersByTarget: {},
|
|
7229
7310
|
promptAttachments: [],
|
|
7230
7311
|
nextQueue: [],
|
|
@@ -7591,7 +7672,8 @@ var NEWMARK_I18N = {
|
|
|
7591
7672
|
'model.githubModelsImported': 'Imported {count} models.',
|
|
7592
7673
|
'model.githubModelsWarning': 'Warning: {warning}',
|
|
7593
7674
|
'model.autoDetect': 'Auto detect',
|
|
7594
|
-
'model.modelName': '
|
|
7675
|
+
'model.modelName': 'API model identifier',
|
|
7676
|
+
'model.displayName': 'Display name',
|
|
7595
7677
|
'model.provider': 'Provider',
|
|
7596
7678
|
'model.contextSize': 'Context size',
|
|
7597
7679
|
'model.vision': 'Vision',
|
|
@@ -7613,15 +7695,21 @@ var NEWMARK_I18N = {
|
|
|
7613
7695
|
'model.openAIChat': 'Chat Completions',
|
|
7614
7696
|
'model.openAIResponses': 'Responses API',
|
|
7615
7697
|
'model.fallback': 'Fallback unavailable model',
|
|
7616
|
-
'model.preference': '
|
|
7698
|
+
'model.preference': 'Model decision tendency',
|
|
7699
|
+
'model.preferenceDesc': 'The current eligible model makes each Auto choice. This setting guides how readily it favors a different eligible model; it does not select a model or set a numeric cutoff.',
|
|
7617
7700
|
'model.costSaving': 'Cost saving',
|
|
7618
7701
|
'model.performance': 'Quality',
|
|
7619
|
-
'model.
|
|
7620
|
-
'model.
|
|
7621
|
-
'model.
|
|
7622
|
-
'model.
|
|
7623
|
-
'model.
|
|
7624
|
-
'model.
|
|
7702
|
+
'model.conservativeTendency': 'Conservative',
|
|
7703
|
+
'model.balancedTendency': 'Balanced',
|
|
7704
|
+
'model.aggressiveTendency': 'Aggressive',
|
|
7705
|
+
'model.jevTitle': 'Auto decision policy',
|
|
7706
|
+
'model.jevDesc': 'Uses the current eligible conversation model with native thinking disabled and strict JSON Schema output. Invalid output, unsupported controls, or timeout stops Auto routing.',
|
|
7707
|
+
'model.jevEnabled': 'Current-model decision enabled',
|
|
7708
|
+
'model.jevCurrentModelStatus': 'Current eligible model · thinking off · strict JSON Schema',
|
|
7709
|
+
'model.jevTimeout': 'Decision timeout (ms)',
|
|
7710
|
+
'model.jevLocked': 'Auto is off: current-model decision settings are locked and no model may change.',
|
|
7711
|
+
'model.jevSaved': 'Auto decision policy updated.',
|
|
7712
|
+
'model.jevRefused': 'The Auto decision policy was not changed.',
|
|
7625
7713
|
'model.validationTitle': 'Model validation',
|
|
7626
7714
|
'model.validating': 'Validating model latency and capabilities...',
|
|
7627
7715
|
'model.validationProgress': 'Validation progress',
|
|
@@ -8334,7 +8422,8 @@ var NEWMARK_I18N = {
|
|
|
8334
8422
|
'model.githubModelsImported': '已导入 {count} 个模型。',
|
|
8335
8423
|
'model.githubModelsWarning': '警告:{warning}',
|
|
8336
8424
|
'model.autoDetect': '自动检测',
|
|
8337
|
-
'model.modelName': '
|
|
8425
|
+
'model.modelName': '调用模型标识',
|
|
8426
|
+
'model.displayName': '显示名称',
|
|
8338
8427
|
'model.provider': '供应商',
|
|
8339
8428
|
'model.contextSize': '上下文规模',
|
|
8340
8429
|
'model.vision': '视觉',
|
|
@@ -8356,15 +8445,21 @@ var NEWMARK_I18N = {
|
|
|
8356
8445
|
'model.openAIChat': 'Chat Completions',
|
|
8357
8446
|
'model.openAIResponses': 'Responses API',
|
|
8358
8447
|
'model.fallback': '模型不可用时回退',
|
|
8359
|
-
'model.preference': '
|
|
8448
|
+
'model.preference': '模型决策倾向',
|
|
8449
|
+
'model.preferenceDesc': '每次 Auto 选择均由当前符合条件的模型决策。此设置只影响模型的决策倾向,不替模型选择部署,也不设数值阈值。',
|
|
8360
8450
|
'model.costSaving': '节省优先',
|
|
8361
8451
|
'model.performance': '质量优先',
|
|
8362
|
-
'model.
|
|
8363
|
-
'model.
|
|
8364
|
-
'model.
|
|
8365
|
-
'model.
|
|
8366
|
-
'model.
|
|
8367
|
-
'model.
|
|
8452
|
+
'model.conservativeTendency': '保守',
|
|
8453
|
+
'model.balancedTendency': '均衡',
|
|
8454
|
+
'model.aggressiveTendency': '激进',
|
|
8455
|
+
'model.jevTitle': 'Auto 决策策略',
|
|
8456
|
+
'model.jevDesc': '仅使用当前符合条件的对话模型,并原生关闭思考、要求严格 JSON Schema 输出。输出无效、关闭思考不受支持或请求超时都会停止 Auto 路由。',
|
|
8457
|
+
'model.jevEnabled': '启用当前模型决策',
|
|
8458
|
+
'model.jevCurrentModelStatus': '当前符合条件的模型 · 已关闭思考 · 严格 JSON Schema',
|
|
8459
|
+
'model.jevTimeout': '决策超时(毫秒)',
|
|
8460
|
+
'model.jevLocked': 'Auto 已关闭:当前模型决策设置已锁定,任何模型都不会变动。',
|
|
8461
|
+
'model.jevSaved': '已更新 Auto 决策策略。',
|
|
8462
|
+
'model.jevRefused': 'Auto 决策策略未做任何修改。',
|
|
8368
8463
|
'model.validationTitle': '模型校验',
|
|
8369
8464
|
'model.validating': '正在校验模型延迟和能力...',
|
|
8370
8465
|
'model.validationProgress': '校验进度',
|
|
@@ -9652,87 +9747,6 @@ window.copyMessageText = async function(button) {
|
|
|
9652
9747
|
var branchApiOverride = null;
|
|
9653
9748
|
window.__setBranchApiForTest = function(nextApi) { branchApiOverride = nextApi || null; };
|
|
9654
9749
|
|
|
9655
|
-
function applyAutoRouteRatingState(snapshot) {
|
|
9656
|
-
snapshot = snapshot || {};
|
|
9657
|
-
var decision = snapshot.routeDecision && typeof snapshot.routeDecision === 'object' ? snapshot.routeDecision : null;
|
|
9658
|
-
var routeId = decision ? String(decision.routeId || '') : '';
|
|
9659
|
-
var selection = decision && decision.requestedSelection;
|
|
9660
|
-
var available = !!routeId && !!decision.resolvedDeployment && selection && selection.kind === 'auto'
|
|
9661
|
-
&& snapshot.autoRouteRatingAvailable !== false;
|
|
9662
|
-
if (routeId !== state.autoRouteRatingRouteId) state.autoRouteRatingScore = 0;
|
|
9663
|
-
state.autoRouteRatingAvailable = !!available;
|
|
9664
|
-
state.autoRouteRatingRouteId = available ? routeId : '';
|
|
9665
|
-
if (!available) state.autoRouteRatingScore = 0;
|
|
9666
|
-
}
|
|
9667
|
-
|
|
9668
|
-
function autoRouteRatingLabel(score) {
|
|
9669
|
-
if (currentLang() === 'zh') return score > 0 ? '此 Auto 路由有帮助' : '此 Auto 路由没有帮助';
|
|
9670
|
-
return score > 0 ? 'This Auto route was helpful' : 'This Auto route was not helpful';
|
|
9671
|
-
}
|
|
9672
|
-
|
|
9673
|
-
function attachAutoRouteRatingControls(message) {
|
|
9674
|
-
if (!message || !api.rateAutoRoute || !state.autoRouteRatingAvailable || !state.autoRouteRatingRouteId) return;
|
|
9675
|
-
var actions = message.querySelector('.msg-actions');
|
|
9676
|
-
if (!actions) return;
|
|
9677
|
-
var old = actions.querySelectorAll('.route-rating-btn');
|
|
9678
|
-
for (var i = 0; i < old.length; i++) old[i].remove();
|
|
9679
|
-
[1, -1].forEach(function(score) {
|
|
9680
|
-
var label = autoRouteRatingLabel(score);
|
|
9681
|
-
var selected = Number(state.autoRouteRatingScore || 0) === score;
|
|
9682
|
-
var button = document.createElement('button');
|
|
9683
|
-
button.type = 'button';
|
|
9684
|
-
button.className = 'msg-action-btn route-rating-btn' + (selected ? ' route-rating-selected' : '');
|
|
9685
|
-
button.title = label;
|
|
9686
|
-
button.setAttribute('aria-label', label);
|
|
9687
|
-
button.setAttribute('aria-pressed', selected ? 'true' : 'false');
|
|
9688
|
-
button.setAttribute('data-route-id', state.autoRouteRatingRouteId);
|
|
9689
|
-
button.innerHTML = iconSvg(score > 0 ? 'thumbs-up' : 'thumbs-down', label, 'tiny');
|
|
9690
|
-
button.disabled = !!state.autoRouteRatingScore;
|
|
9691
|
-
button.addEventListener('click', function() { window.rateAutoRoute(button, score); });
|
|
9692
|
-
actions.appendChild(button);
|
|
9693
|
-
});
|
|
9694
|
-
}
|
|
9695
|
-
|
|
9696
|
-
function renderAutoRouteRatingControls() {
|
|
9697
|
-
if (!els['chat-area']) return;
|
|
9698
|
-
var existing = els['chat-area'].querySelectorAll('.route-rating-btn');
|
|
9699
|
-
for (var i = 0; i < existing.length; i++) existing[i].remove();
|
|
9700
|
-
if (!state.autoRouteRatingAvailable) return;
|
|
9701
|
-
var messages = els['chat-area'].querySelectorAll('.chat-msg.assistant:not(.work-run-message)');
|
|
9702
|
-
attachAutoRouteRatingControls(messages.length ? messages[messages.length - 1] : null);
|
|
9703
|
-
}
|
|
9704
|
-
|
|
9705
|
-
window.rateAutoRoute = async function(button, score) {
|
|
9706
|
-
if (!api.rateAutoRoute || !button) return;
|
|
9707
|
-
var message = button.closest ? button.closest('.chat-msg') : null;
|
|
9708
|
-
var buttons = message ? message.querySelectorAll('.route-rating-btn') : [];
|
|
9709
|
-
for (var i = 0; i < buttons.length; i++) buttons[i].disabled = true;
|
|
9710
|
-
var routeId = String(button.getAttribute('data-route-id') || '');
|
|
9711
|
-
try {
|
|
9712
|
-
var result = await api.rateAutoRoute({
|
|
9713
|
-
target: currentConversationTarget(),
|
|
9714
|
-
score: score,
|
|
9715
|
-
routeId: routeId
|
|
9716
|
-
});
|
|
9717
|
-
if (result && (result.ok || result.reason === 'already_rated')) {
|
|
9718
|
-
state.autoRouteRatingScore = Number(result.score) === -1 || Number(result.score) === 1 ? Number(result.score) : score;
|
|
9719
|
-
renderAutoRouteRatingControls();
|
|
9720
|
-
showUiNotice(currentLang() === 'zh' ? '已记录 Auto 路由反馈。' : 'Auto route feedback recorded.', 'info', 'auto-route-rating-' + routeId);
|
|
9721
|
-
return;
|
|
9722
|
-
}
|
|
9723
|
-
for (var j = 0; j < buttons.length; j++) buttons[j].disabled = false;
|
|
9724
|
-
var reason = result && result.reason ? String(result.reason) : 'no_active_auto_route';
|
|
9725
|
-
showUiNotice(
|
|
9726
|
-
currentLang() === 'zh' ? '无法记录 Auto 路由反馈:' + reason : 'Unable to record Auto route feedback: ' + reason,
|
|
9727
|
-
'error',
|
|
9728
|
-
'auto-route-rating-' + routeId
|
|
9729
|
-
);
|
|
9730
|
-
} catch (error) {
|
|
9731
|
-
for (var k = 0; k < buttons.length; k++) buttons[k].disabled = false;
|
|
9732
|
-
showUiNotice(error && error.message ? error.message : String(error), 'error', 'auto-route-rating-' + routeId);
|
|
9733
|
-
}
|
|
9734
|
-
};
|
|
9735
|
-
|
|
9736
9750
|
window.editUserMessage = function(button, messageIndex) {
|
|
9737
9751
|
var message = button && button.closest ? button.closest('.chat-msg') : null;
|
|
9738
9752
|
if (!message || message.querySelector('.message-inline-editor')) return;
|
|
@@ -10185,7 +10199,8 @@ function appendConversationImageAttachments(container, input) {
|
|
|
10185
10199
|
}
|
|
10186
10200
|
|
|
10187
10201
|
function addMsg(role, text, mode, model, messageIndex, messageMeta) {
|
|
10188
|
-
var
|
|
10202
|
+
var skipAutoScroll = !!(messageMeta && messageMeta.skipAutoScroll);
|
|
10203
|
+
var chatWasAtBottom = !skipAutoScroll && !!(els['chat-area'] && shouldAutoScroll(els['chat-area']));
|
|
10189
10204
|
text = redactSensitiveText(text);
|
|
10190
10205
|
var owningRunId = messageMeta && String(messageMeta.runId || '');
|
|
10191
10206
|
if (owningRunId && role === 'assistant' && els['chat-area']) {
|
|
@@ -10216,6 +10231,7 @@ function addMsg(role, text, mode, model, messageIndex, messageMeta) {
|
|
|
10216
10231
|
}
|
|
10217
10232
|
var div = document.createElement('div');
|
|
10218
10233
|
div.className = 'chat-msg ' + role;
|
|
10234
|
+
if (messageMeta && messageMeta.skipEntryAnimation) div.classList.add('history-static');
|
|
10219
10235
|
var metaLabel = role === 'user'
|
|
10220
10236
|
? (String(mode || '').toLowerCase() === 'flow-user-input' ? t('flow.userInput') : t('message.user'))
|
|
10221
10237
|
: (role === 'workflow' ? t('message.workflow') : (role === 'system' ? t('message.system') : t('message.agent')));
|
|
@@ -10232,7 +10248,11 @@ function addMsg(role, text, mode, model, messageIndex, messageMeta) {
|
|
|
10232
10248
|
if (model) metaInfo += (metaInfo ? ' | ' : '') + t('message.model') + ': ' + model;
|
|
10233
10249
|
div.innerHTML = '<div class="meta"><span class="msg-role">' + metaLabel + '</span><span> | ' + esc(displayTimestamp) + '</span>' +
|
|
10234
10250
|
(metaInfo ? '<span class="meta-extra"> | ' + esc(metaInfo) + '</span>' : '') + messageActionsHtml(role, text, messageIndex) + '</div><div class="msg-body">' + renderMessageContent(text) + '</div>';
|
|
10251
|
+
var msgActions = role === 'user' ? div.querySelector('.msg-actions') : null;
|
|
10252
|
+
div._newmarkEditUserButton = msgActions && msgActions.children.length > 1 ? msgActions.children[1] : null;
|
|
10235
10253
|
div._newmarkMessageText = String(text || '');
|
|
10254
|
+
div._newmarkMessageMode = String(mode || '');
|
|
10255
|
+
div._newmarkMessageModel = String(model || '');
|
|
10236
10256
|
div._newmarkMessageAttachments = appendConversationImageAttachments(div.querySelector('.msg-body'), messageMeta && messageMeta.attachments);
|
|
10237
10257
|
div._newmarkDisplayImages = role === 'assistant' && owningRunId
|
|
10238
10258
|
? appendWorkDisplayImages(div.querySelector('.msg-body'), owningRunId, messageMeta && messageMeta.target)
|
|
@@ -10255,7 +10275,7 @@ function addMsg(role, text, mode, model, messageIndex, messageMeta) {
|
|
|
10255
10275
|
else els['chat-area'].appendChild(div);
|
|
10256
10276
|
} else els['chat-area'].appendChild(div);
|
|
10257
10277
|
if (chatWasAtBottom) _chatShouldAutoScroll = true;
|
|
10258
|
-
autoScrollIfAtBottom();
|
|
10278
|
+
if (!skipAutoScroll) autoScrollIfAtBottom();
|
|
10259
10279
|
return div;
|
|
10260
10280
|
}
|
|
10261
10281
|
|
|
@@ -10473,6 +10493,8 @@ function updateMsg(div, text, role, mode, model, options) {
|
|
|
10473
10493
|
if (preserveRunFinalResponse) div.classList.add('run-final-response');
|
|
10474
10494
|
text = redactSensitiveText(text);
|
|
10475
10495
|
div._newmarkMessageText = String(text || '');
|
|
10496
|
+
if (mode !== undefined && mode !== null) div._newmarkMessageMode = String(mode || '');
|
|
10497
|
+
if (model !== undefined && model !== null) div._newmarkMessageModel = String(model || '');
|
|
10476
10498
|
var body = div.querySelector('.msg-body');
|
|
10477
10499
|
var streaming = !!(options && options.streaming);
|
|
10478
10500
|
if (body) {
|
|
@@ -10961,6 +10983,7 @@ function setConversationRuntimeState(target, status, runId, extra) {
|
|
|
10961
10983
|
if (!state.runningConversations) state.runningConversations = {};
|
|
10962
10984
|
if (['running', 'stopping', 'force_restarting'].indexOf(next.status) >= 0) state.runningConversations[key] = next;
|
|
10963
10985
|
else delete state.runningConversations[key];
|
|
10986
|
+
if (typeof ensureRuntimeReconcileWatchdog === 'function') ensureRuntimeReconcileWatchdog();
|
|
10964
10987
|
var branchIds = conversationBranchIdsForTarget(target);
|
|
10965
10988
|
var runs = workRunsForBranch(target, branchIds.runtime || branchIds.viewed);
|
|
10966
10989
|
var matchingRun = next.runId && runs.find(function(item) { return String(item.runId || '') === next.runId; });
|
|
@@ -11980,7 +12003,10 @@ function updateConversationWorkRunElement(run, element) {
|
|
|
11980
12003
|
// 重建/增量更新前后保持聊天视窗滚动位置:贴底时跟随最新,未贴底时
|
|
11981
12004
|
// 精确恢复 scrollTop,避免内容高度变化把视窗“强制拖向最新”。
|
|
11982
12005
|
var chatEl = els['chat-area'];
|
|
11983
|
-
|
|
12006
|
+
// The passive scroll listener keeps this sticky-bottom bit current. Re-read
|
|
12007
|
+
// scrollHeight here would force layout immediately before the DOM patch on
|
|
12008
|
+
// every incremental update, then the restoration below would force it again.
|
|
12009
|
+
var wasAtBottom = !!(chatEl && _chatShouldAutoScroll);
|
|
11984
12010
|
var scrollTopBefore = chatEl ? chatEl.scrollTop : 0;
|
|
11985
12011
|
var activityOpenStates = {};
|
|
11986
12012
|
var priorActivities = element.querySelectorAll ? element.querySelectorAll('details.conversation-work-activity[data-activity-key], details[data-work-detail-key]') : [];
|
|
@@ -12525,7 +12551,19 @@ function cacheConversationMessages(messages, target) {
|
|
|
12525
12551
|
var key = branchConversationViewKey(scopedTarget);
|
|
12526
12552
|
var cloned = Array.isArray(messages) ? messages.map(function(message) { return Object.assign({}, message); }) : [];
|
|
12527
12553
|
if (!state.conversationMessagesByTarget) state.conversationMessagesByTarget = {};
|
|
12554
|
+
// Disk remains authoritative. Limit retained branch snapshots, not history.
|
|
12555
|
+
delete state.conversationMessagesByTarget[key];
|
|
12528
12556
|
state.conversationMessagesByTarget[key] = cloned;
|
|
12557
|
+
var cacheKeys = Object.keys(state.conversationMessagesByTarget);
|
|
12558
|
+
var retainedChars = 0;
|
|
12559
|
+
for (var i = cacheKeys.length - 1; i >= 0; i--) {
|
|
12560
|
+
var cachedKey = cacheKeys[i];
|
|
12561
|
+
var rows = state.conversationMessagesByTarget[cachedKey];
|
|
12562
|
+
var chars = rows.reduce(function(sum, row) { return sum + String(row.content || '').length; }, 0);
|
|
12563
|
+
if (cacheKeys.length - i > 6 || retainedChars + chars > 4 * 1024 * 1024) {
|
|
12564
|
+
delete state.conversationMessagesByTarget[cachedKey];
|
|
12565
|
+
} else retainedChars += chars;
|
|
12566
|
+
}
|
|
12529
12567
|
return cloned.map(function(message) { return Object.assign({}, message); });
|
|
12530
12568
|
}
|
|
12531
12569
|
|
|
@@ -12616,6 +12654,9 @@ function renderChatMessagesUnsafe(messages, target) {
|
|
|
12616
12654
|
var chatWasAtBottom = shouldAutoScroll(els['chat-area']);
|
|
12617
12655
|
els['chat-area'].innerHTML = '';
|
|
12618
12656
|
if (chatWasAtBottom) _chatShouldAutoScroll = true;
|
|
12657
|
+
// This target now owns the transcript on screen; an async snapshot for it may
|
|
12658
|
+
// repaint its held option block, and a snapshot for any other target may not.
|
|
12659
|
+
state.renderedChatTargetKey = runtimeKeyFor(renderTarget.workspaceId, renderTarget.conversationId);
|
|
12619
12660
|
var renderConversationId = renderTarget.conversationId;
|
|
12620
12661
|
var guideMessages = guideMessagesForTarget(renderTarget);
|
|
12621
12662
|
var persistedGuideIds = {};
|
|
@@ -12632,6 +12673,10 @@ function renderChatMessagesUnsafe(messages, target) {
|
|
|
12632
12673
|
});
|
|
12633
12674
|
var persistedRunsById = {};
|
|
12634
12675
|
var persistedRunIndexes = {};
|
|
12676
|
+
// dev-0.6.6: the transcript loop below asks "is this message owned by a
|
|
12677
|
+
// persisted work run?" once per message. A linear scan there is O(rounds²)
|
|
12678
|
+
// for a long conversation, so resolve it through a lookup table instead.
|
|
12679
|
+
var persistedRunIdSet = {};
|
|
12635
12680
|
var renderedRunIds = {};
|
|
12636
12681
|
var recoveredAssistantRunIds = {};
|
|
12637
12682
|
var messageRunRoles = {};
|
|
@@ -12641,6 +12686,7 @@ function renderChatMessagesUnsafe(messages, target) {
|
|
|
12641
12686
|
if (runId) {
|
|
12642
12687
|
persistedRunsById[runId] = run;
|
|
12643
12688
|
persistedRunIndexes[runId] = runIndex;
|
|
12689
|
+
persistedRunIdSet[runId] = true;
|
|
12644
12690
|
}
|
|
12645
12691
|
});
|
|
12646
12692
|
normalizedMessages.forEach(function(message, messageIndex) {
|
|
@@ -12701,7 +12747,7 @@ function renderChatMessagesUnsafe(messages, target) {
|
|
|
12701
12747
|
finishToolBatch(renderConversationId);
|
|
12702
12748
|
var clientMessageId = String(m.clientMessageId || '');
|
|
12703
12749
|
if (clientMessageId) persistedGuideIds[clientMessageId] = true;
|
|
12704
|
-
if (clientMessageId && m.runId &&
|
|
12750
|
+
if (clientMessageId && m.runId && persistedRunIdSet[String(m.runId || '')]) {
|
|
12705
12751
|
delete guideMessages[clientMessageId];
|
|
12706
12752
|
continue;
|
|
12707
12753
|
}
|
|
@@ -12720,7 +12766,9 @@ function renderChatMessagesUnsafe(messages, target) {
|
|
|
12720
12766
|
guideId: m.guideId || '',
|
|
12721
12767
|
attachments: m.attachments || [],
|
|
12722
12768
|
target: renderTarget,
|
|
12723
|
-
timestamp: m.timestamp || ''
|
|
12769
|
+
timestamp: m.timestamp || '',
|
|
12770
|
+
skipAutoScroll: true,
|
|
12771
|
+
skipEntryAnimation: true
|
|
12724
12772
|
});
|
|
12725
12773
|
if (associatedRun && String(m.role || '') === 'user') renderOwnedWorkRun(associatedRun, false);
|
|
12726
12774
|
if (clientMessageId) delete guideMessages[clientMessageId];
|
|
@@ -12733,7 +12781,6 @@ function renderChatMessagesUnsafe(messages, target) {
|
|
|
12733
12781
|
});
|
|
12734
12782
|
if (pendingWorkReview) addWorkReview(pendingWorkReview.diffs || pendingWorkReview, pendingWorkReview.runId || '');
|
|
12735
12783
|
renderConversationBranchPagers();
|
|
12736
|
-
renderAutoRouteRatingControls();
|
|
12737
12784
|
if (typeof renderPendingOptionsInChat === 'function') renderPendingOptionsInChat(state.pendingOptions || []);
|
|
12738
12785
|
}
|
|
12739
12786
|
|
|
@@ -12742,6 +12789,153 @@ function renderChatMessagesUnsafe(messages, target) {
|
|
|
12742
12789
|
// the preceding window via conversation:loadEarlier and prepends it, so a
|
|
12743
12790
|
// long-running conversation can be browsed without ever transferring the whole
|
|
12744
12791
|
// transcript in one snapshot.
|
|
12792
|
+
function canPrependHistoryMessages(messages, existingMessages, target, liveArea, previousStart, pageStart) {
|
|
12793
|
+
if (!Array.isArray(messages) || !messages.length || !Array.isArray(existingMessages) || !existingMessages.length || !liveArea) return false;
|
|
12794
|
+
if (!Number.isFinite(Number(previousStart)) || !Number.isFinite(Number(pageStart))
|
|
12795
|
+
|| Number(pageStart) < 0 || Number(pageStart) + messages.length !== Number(previousStart)) return false;
|
|
12796
|
+
var targetKey = runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
12797
|
+
if (state.renderedChatTargetKey !== targetKey) return false;
|
|
12798
|
+
var persistedRuns = workRunsForTarget(target).slice();
|
|
12799
|
+
var persistedRunIds = Object.create(null);
|
|
12800
|
+
var stableRunStatuses = ['completed', 'interrupted', 'force_interrupted', 'error'];
|
|
12801
|
+
for (var runIndex = 0; runIndex < persistedRuns.length; runIndex++) {
|
|
12802
|
+
var persistedRun = persistedRuns[runIndex] || {};
|
|
12803
|
+
var persistedRunId = String(persistedRun.runId || '');
|
|
12804
|
+
if (!persistedRunId || stableRunStatuses.indexOf(String(persistedRun.status || '')) < 0) return false;
|
|
12805
|
+
persistedRunIds[persistedRunId] = true;
|
|
12806
|
+
}
|
|
12807
|
+
if ((state.conversationBranchGroups || []).length || (state.conversationBranches || []).length > 1
|
|
12808
|
+
|| state.activeConversationBranchId || (state.viewedConversationBranchNodePath || []).length) return false;
|
|
12809
|
+
var workUi = state.agentWorkUiByConversation && state.agentWorkUiByConversation[targetKey];
|
|
12810
|
+
if (workUi && (workUi.pendingWorkReview || (workUi.workReviews || []).length)) return false;
|
|
12811
|
+
if (pendingOptionsForTarget(target).length) return false;
|
|
12812
|
+
|
|
12813
|
+
function isPlainHistoryMessage(message, allowPersistedRun) {
|
|
12814
|
+
if (!message || (message.role !== 'user' && message.role !== 'assistant')) return false;
|
|
12815
|
+
var messageMode = String(message.mode || '').trim().toLowerCase();
|
|
12816
|
+
if (messageMode && messageMode !== 'chat' && messageMode !== 'build') return false;
|
|
12817
|
+
if (message.clientMessageId || message.guideId || isHiddenWorkflowMessage(message)) return false;
|
|
12818
|
+
if (Array.isArray(message.attachments) && message.attachments.length) return false;
|
|
12819
|
+
var runId = String(message.runId || '');
|
|
12820
|
+
if (runId && persistedRunIds[runId] && !allowPersistedRun) return false;
|
|
12821
|
+
return true;
|
|
12822
|
+
}
|
|
12823
|
+
if (!messages.every(function(message) { return isPlainHistoryMessage(message, false); })
|
|
12824
|
+
|| !existingMessages.every(function(message) { return isPlainHistoryMessage(message, true); })) return false;
|
|
12825
|
+
|
|
12826
|
+
var contentNodes = Array.prototype.filter.call(liveArea.children, function(node) {
|
|
12827
|
+
return !node.classList.contains('conversation-load-earlier');
|
|
12828
|
+
});
|
|
12829
|
+
var messageNodes = contentNodes.filter(function(node) {
|
|
12830
|
+
return node.classList.contains('chat-msg')
|
|
12831
|
+
&& !node.classList.contains('work-run-message')
|
|
12832
|
+
&& node.hasAttribute('data-message-index');
|
|
12833
|
+
});
|
|
12834
|
+
if (messageNodes.length !== existingMessages.length) return false;
|
|
12835
|
+
var auxiliaryNodes = contentNodes.filter(function(node) {
|
|
12836
|
+
return !node.classList.contains('chat-msg')
|
|
12837
|
+
|| node.classList.contains('work-run-message')
|
|
12838
|
+
|| !node.hasAttribute('data-message-index');
|
|
12839
|
+
});
|
|
12840
|
+
if (!auxiliaryNodes.every(function(node) {
|
|
12841
|
+
if (!node.classList.contains('chat-msg')) return false;
|
|
12842
|
+
if (node.classList.contains('work-run-message')) {
|
|
12843
|
+
var runElement = node.querySelector('.conversation-work-run[data-run-id]');
|
|
12844
|
+
return !!runElement && !!persistedRunIds[String(runElement.getAttribute('data-run-id') || '')];
|
|
12845
|
+
}
|
|
12846
|
+
return node.classList.contains('run-final-response')
|
|
12847
|
+
&& !!persistedRunIds[String(node.getAttribute('data-run-id') || '')];
|
|
12848
|
+
})) return false;
|
|
12849
|
+
var assistantRunIds = Object.create(null);
|
|
12850
|
+
var orderedMessages = messages.concat(existingMessages);
|
|
12851
|
+
for (var i = 0; i < orderedMessages.length; i++) {
|
|
12852
|
+
var message = orderedMessages[i];
|
|
12853
|
+
var runId = String(message && message.runId || '');
|
|
12854
|
+
if (message && message.role === 'assistant' && runId) {
|
|
12855
|
+
if (assistantRunIds[runId]) return false;
|
|
12856
|
+
assistantRunIds[runId] = true;
|
|
12857
|
+
}
|
|
12858
|
+
}
|
|
12859
|
+
return messageNodes.every(function(node, index) {
|
|
12860
|
+
var message = existingMessages[index] || {};
|
|
12861
|
+
var expectedId = String(message.messageId || '');
|
|
12862
|
+
var nodeTarget = node._newmarkMessageTarget;
|
|
12863
|
+
return node.classList.contains('chat-msg')
|
|
12864
|
+
&& node.classList.contains(String(message.role || ''))
|
|
12865
|
+
&& Number(node.getAttribute('data-message-index')) === index
|
|
12866
|
+
&& String(node.getAttribute('data-message-id') || '') === expectedId
|
|
12867
|
+
&& String(node.getAttribute('data-run-id') || '') === String(message.runId || '')
|
|
12868
|
+
&& String(node._newmarkMessageMode || '').trim().toLowerCase() === String(message.mode || '').trim().toLowerCase()
|
|
12869
|
+
&& String(node._newmarkMessageModel || '').trim() === String(message.model || '').trim()
|
|
12870
|
+
&& String(node._newmarkMessageText || '') === String(message.content || '')
|
|
12871
|
+
&& !!nodeTarget && nodeTarget.workspaceId === String(target.workspaceId || '')
|
|
12872
|
+
&& nodeTarget.conversationId === String(target.conversationId || 'default');
|
|
12873
|
+
});
|
|
12874
|
+
}
|
|
12875
|
+
|
|
12876
|
+
function prependHistoryMessagesIncrementally(messages, existingMessages, target, previousStart, pageStart) {
|
|
12877
|
+
var liveArea = els['chat-area'];
|
|
12878
|
+
if (!canPrependHistoryMessages(messages, existingMessages, target, liveArea, previousStart, pageStart)) return false;
|
|
12879
|
+
|
|
12880
|
+
var contentNodes = Array.prototype.filter.call(liveArea.children, function(node) {
|
|
12881
|
+
return !node.classList.contains('conversation-load-earlier');
|
|
12882
|
+
});
|
|
12883
|
+
var anchor = contentNodes[0] || null;
|
|
12884
|
+
var anchorTop = anchor ? anchor.getBoundingClientRect().top : 0;
|
|
12885
|
+
var wasAtBottom = shouldAutoScroll(liveArea);
|
|
12886
|
+
var previousAutoScroll = _chatShouldAutoScroll;
|
|
12887
|
+
var stagingArea = document.createElement('div');
|
|
12888
|
+
var priorArea = els['chat-area'];
|
|
12889
|
+
var renderFailed = false;
|
|
12890
|
+
try {
|
|
12891
|
+
els['chat-area'] = stagingArea;
|
|
12892
|
+
messages.forEach(function(message, index) {
|
|
12893
|
+
addMsg(message.role, message.content || '', message.mode || '', message.model || '', index, {
|
|
12894
|
+
runId: message.runId || '',
|
|
12895
|
+
messageId: message.messageId || '',
|
|
12896
|
+
target: target,
|
|
12897
|
+
timestamp: message.timestamp || '',
|
|
12898
|
+
skipAutoScroll: true,
|
|
12899
|
+
skipEntryAnimation: true
|
|
12900
|
+
});
|
|
12901
|
+
});
|
|
12902
|
+
} catch (error) {
|
|
12903
|
+
renderFailed = true;
|
|
12904
|
+
console.warn('[ui_history_prepend_failed]', error);
|
|
12905
|
+
} finally {
|
|
12906
|
+
els['chat-area'] = priorArea;
|
|
12907
|
+
_chatShouldAutoScroll = previousAutoScroll;
|
|
12908
|
+
}
|
|
12909
|
+
if (renderFailed || stagingArea.childNodes.length !== messages.length) return false;
|
|
12910
|
+
|
|
12911
|
+
var shiftedBy = messages.length;
|
|
12912
|
+
contentNodes.forEach(function(node) {
|
|
12913
|
+
if (!node.classList.contains('chat-msg') || !node.hasAttribute('data-message-index')) return;
|
|
12914
|
+
var nextIndex = Number(node.getAttribute('data-message-index')) + shiftedBy;
|
|
12915
|
+
node.setAttribute('data-message-index', String(nextIndex));
|
|
12916
|
+
var editButton = node._newmarkEditUserButton;
|
|
12917
|
+
if (editButton) editButton.setAttribute('onclick', 'window.editUserMessage(this,' + nextIndex + ')');
|
|
12918
|
+
});
|
|
12919
|
+
var fragment = document.createDocumentFragment();
|
|
12920
|
+
while (stagingArea.firstChild) fragment.appendChild(stagingArea.firstChild);
|
|
12921
|
+
liveArea.insertBefore(fragment, anchor);
|
|
12922
|
+
|
|
12923
|
+
var runAnchorIndexes = workRunAnchorIndexStore(target);
|
|
12924
|
+
Object.keys(runAnchorIndexes).forEach(function(runId) {
|
|
12925
|
+
var anchorIndex = Number(runAnchorIndexes[runId]);
|
|
12926
|
+
if (Number.isFinite(anchorIndex) && anchorIndex >= 0) runAnchorIndexes[runId] = anchorIndex + shiftedBy;
|
|
12927
|
+
});
|
|
12928
|
+
|
|
12929
|
+
var targetKey = runtimeKeyFor(target.workspaceId, target.conversationId);
|
|
12930
|
+
state.renderedChatMessages = cacheConversationMessages(messages.concat(existingMessages), target);
|
|
12931
|
+
state.renderedChatTargetKey = targetKey;
|
|
12932
|
+
_chatShouldAutoScroll = wasAtBottom;
|
|
12933
|
+
renderLoadEarlierButton();
|
|
12934
|
+
if (wasAtBottom) liveArea.scrollTop = liveArea.scrollHeight;
|
|
12935
|
+
else if (anchor) liveArea.scrollTop += anchor.getBoundingClientRect().top - anchorTop;
|
|
12936
|
+
return true;
|
|
12937
|
+
}
|
|
12938
|
+
|
|
12745
12939
|
function renderLoadEarlierButton() {
|
|
12746
12940
|
if (!els['chat-area']) return;
|
|
12747
12941
|
Array.prototype.forEach.call(els['chat-area'].querySelectorAll('.conversation-load-earlier'), function(button) { button.remove(); });
|
|
@@ -12772,9 +12966,12 @@ function renderLoadEarlierButton() {
|
|
|
12772
12966
|
var mergedMessages = result.chatMessages.concat(newer);
|
|
12773
12967
|
state.conversationLoadedBefore[loadKey] = Math.max(0, Number(result.windowStart) || 0);
|
|
12774
12968
|
var chatWasAtBottom = shouldAutoScroll(els['chat-area']);
|
|
12775
|
-
|
|
12776
|
-
if (!
|
|
12777
|
-
|
|
12969
|
+
var prepended = prependHistoryMessagesIncrementally(result.chatMessages, newer, loadTarget, loadBefore, result.windowStart);
|
|
12970
|
+
if (!prepended) {
|
|
12971
|
+
renderChatMessages(mergedMessages, loadTarget);
|
|
12972
|
+
if (!chatWasAtBottom && els['chat-area']) els['chat-area'].scrollTop = 0;
|
|
12973
|
+
renderLoadEarlierButton();
|
|
12974
|
+
}
|
|
12778
12975
|
}).catch(function() { button.disabled = false; });
|
|
12779
12976
|
});
|
|
12780
12977
|
els['chat-area'].insertBefore(button, els['chat-area'].firstChild);
|
|
@@ -12921,6 +13118,8 @@ function scheduleStreamingWorkflowTextFlush(ui, eventConversationId, eventWorksp
|
|
|
12921
13118
|
requestAnimationFrame(function() {
|
|
12922
13119
|
var current = conversationWorkUiState(eventConversationId, eventWorkspaceId);
|
|
12923
13120
|
current._streamFlushPending = false;
|
|
13121
|
+
// A queued frame can outlive navigation or its UI state object.
|
|
13122
|
+
if (current !== ui || !isActiveConversationTarget({ workspaceId: eventWorkspaceId, conversationId: eventConversationId })) return;
|
|
12924
13123
|
applyStreamingWorkflowText(current, eventConversationId, current._streamMode || state.mode, current._streamModel || state.model);
|
|
12925
13124
|
});
|
|
12926
13125
|
}
|
|
@@ -13392,6 +13591,15 @@ function applyConversationModelSelection(snapshot) {
|
|
|
13392
13591
|
}
|
|
13393
13592
|
}
|
|
13394
13593
|
|
|
13594
|
+
/**
|
|
13595
|
+
* dev-0.6.6 长程内存回收:渲染端启动时清掉主进程记住的修订号(页面重载后内存里
|
|
13596
|
+
* 的转录已不存在,必须重新取全量)。之后每次真正画完一份转录都会 ack。
|
|
13597
|
+
*/
|
|
13598
|
+
(function clearTranscriptRevisionsOnBoot() {
|
|
13599
|
+
if (!api || !api.ackTranscript) return;
|
|
13600
|
+
try { api.ackTranscript(currentConversationTarget(), ''); } catch (e) {}
|
|
13601
|
+
})();
|
|
13602
|
+
|
|
13395
13603
|
function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
13396
13604
|
if (!s) return;
|
|
13397
13605
|
if (s.computerUse) {
|
|
@@ -13411,23 +13619,34 @@ function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
|
13411
13619
|
var preserveReadableHistory = !!(options && options.preserveReadableHistory
|
|
13412
13620
|
&& !snapshotHasReadableConversationHistory(s)
|
|
13413
13621
|
&& (!options.allowEmptyHistory || conversationHasReadableHistory(snapshotTarget)));
|
|
13622
|
+
// dev-0.6.6 长程内存回收:宿主可能只回传"转录未变"的瘦快照(仅当本渲染端此前
|
|
13623
|
+
// 通过 ackTranscript 声明过同一修订号)。瘦快照只对"当前正画着这条对话"有效;
|
|
13624
|
+
// 其它情况必须取一次全量,绝不能用别的对话的画面顶替。
|
|
13625
|
+
var transcriptUnchanged = !!(s && s.transcriptUnchanged === true);
|
|
13414
13626
|
if (s && s.runtimeKey) registerRuntimeKey(snapshotTarget, s.runtimeKey);
|
|
13627
|
+
if (transcriptUnchanged && !preserveReadableHistory) {
|
|
13628
|
+
var unchangedKey = runtimeKeyFor(snapshotTarget.workspaceId, snapshotTarget.conversationId);
|
|
13629
|
+
if (String(state.renderedChatTargetKey || '') !== unchangedKey && api.getState) {
|
|
13630
|
+
api.getState(snapshotTarget, { transcript: 'force' }).then(function(full) {
|
|
13631
|
+
if (full && isActiveConversationTarget(snapshotTarget)) applyConversationSnapshot(full, snapshotTarget.conversationId, options);
|
|
13632
|
+
}).catch(function() {});
|
|
13633
|
+
}
|
|
13634
|
+
}
|
|
13415
13635
|
hydrateConversationDraftSnapshot(s, snapshotTarget);
|
|
13416
|
-
hydrateConversationBranchState(s);
|
|
13636
|
+
if (!transcriptUnchanged) hydrateConversationBranchState(s);
|
|
13417
13637
|
var modelRevisionMatches = !options || options.modelRevision === undefined
|
|
13418
13638
|
|| Number(state.modelSelectionRevision || 0) === Number(options.modelRevision || 0);
|
|
13419
13639
|
if (modelRevisionMatches && isActiveConversationTarget(snapshotTarget)) {
|
|
13420
13640
|
applyConversationModelSelection(s);
|
|
13421
13641
|
}
|
|
13422
13642
|
rebindQueueToRuntimeBranch(snapshotTarget);
|
|
13423
|
-
if (s && Array.isArray(s.workRuns) && !preserveReadableHistory) {
|
|
13643
|
+
if (s && Array.isArray(s.workRuns) && !preserveReadableHistory && !transcriptUnchanged) {
|
|
13424
13644
|
var viewedBranchIdForSync = Array.isArray(s.viewedBranchNodePath) && s.viewedBranchNodePath.length
|
|
13425
13645
|
? String(s.viewedBranchNodePath[s.viewedBranchNodePath.length - 1])
|
|
13426
13646
|
: '';
|
|
13427
13647
|
syncWorkRunsSnapshot(s.workRuns, snapshotTarget, viewedBranchIdForSync || undefined);
|
|
13428
13648
|
}
|
|
13429
|
-
|
|
13430
|
-
if (s && Number.isFinite(Number(s.totalMessages))) {
|
|
13649
|
+
if (s && Number.isFinite(Number(s.totalMessages)) && Array.isArray(s.chatMessages)) {
|
|
13431
13650
|
if (!state.conversationLoadedBefore) state.conversationLoadedBefore = {};
|
|
13432
13651
|
var windowKey = runtimeKeyFor(snapshotTarget.workspaceId, s.conversationId || requestedConversationId || 'default');
|
|
13433
13652
|
state.conversationLoadedBefore[windowKey] = Math.max(0, Number(s.totalMessages) - (Array.isArray(s.chatMessages) ? s.chatMessages.length : 0));
|
|
@@ -13439,9 +13658,16 @@ function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
|
13439
13658
|
window.renderContextWindow();
|
|
13440
13659
|
}
|
|
13441
13660
|
if (state.rightTab === 'status') window.renderRightStatusPanel();
|
|
13442
|
-
if (s && Array.isArray(s.chatMessages) && !preserveReadableHistory) {
|
|
13661
|
+
if (s && Array.isArray(s.chatMessages) && !preserveReadableHistory && !transcriptUnchanged) {
|
|
13443
13662
|
renderChatMessages(s.chatMessages, snapshotTarget);
|
|
13444
13663
|
renderLoadEarlierButton();
|
|
13664
|
+
// 声明"我已画好该修订",宿主下一次轮询才会回瘦快照。只在真正画到屏幕上
|
|
13665
|
+
// (或该目标就是当前渲染目标)时声明。
|
|
13666
|
+
if (typeof s.transcriptRevision === 'string' && s.transcriptRevision
|
|
13667
|
+
&& String(state.renderedChatTargetKey || '') === runtimeKeyFor(snapshotTarget.workspaceId, snapshotTarget.conversationId)
|
|
13668
|
+
&& api.ackTranscript) {
|
|
13669
|
+
try { api.ackTranscript(snapshotTarget, s.transcriptRevision); } catch (e) {}
|
|
13670
|
+
}
|
|
13445
13671
|
}
|
|
13446
13672
|
if (s && s.conversationId) {
|
|
13447
13673
|
state.activeBackendConversationId = s.conversationId;
|
|
@@ -13453,6 +13679,17 @@ function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
|
13453
13679
|
if (window.syncNextQueueFromBackend) window.syncNextQueueFromBackend(state.backendQueue, snapshotTarget);
|
|
13454
13680
|
}
|
|
13455
13681
|
syncQueueItemsFromSnapshot(s, snapshotTarget);
|
|
13682
|
+
// The transcript above is painted from state.pendingOptions, but the
|
|
13683
|
+
// per-target option store is only refreshed by the line just executed.
|
|
13684
|
+
// A conversation that asked its question while another conversation was
|
|
13685
|
+
// focused therefore has to be repainted here, otherwise the held question
|
|
13686
|
+
// never appears when the user returns to the conversation that asked it.
|
|
13687
|
+
if (isActiveConversationTarget(snapshotTarget)
|
|
13688
|
+
&& state.renderedChatTargetKey === runtimeKeyFor(snapshotTarget.workspaceId, snapshotTarget.conversationId)) {
|
|
13689
|
+
// Paint from the same projection renderChatMessages uses, so an already
|
|
13690
|
+
// answered question (cleared optimistically) is never resurrected.
|
|
13691
|
+
renderPendingOptionsInChat(state.pendingOptions || []);
|
|
13692
|
+
}
|
|
13456
13693
|
if (s && Array.isArray(s.workEvents)) {
|
|
13457
13694
|
var eventConversationId = runtimeKeyFor(snapshotTarget.workspaceId, s.conversationId || requestedConversationId || 'default');
|
|
13458
13695
|
if (!state.agentWorkEventsByConversation) state.agentWorkEventsByConversation = {};
|
|
@@ -13472,13 +13709,29 @@ function applyConversationSnapshot(s, requestedConversationId, options) {
|
|
|
13472
13709
|
if (s && s.subagents) state.subagents = s.subagents;
|
|
13473
13710
|
if (s && s.linkedPlan) state.linkedPlan = s.linkedPlan;
|
|
13474
13711
|
replayActiveAgentWorkEvents();
|
|
13475
|
-
// Ensure running state reflects work runs from snapshot for freshly loaded
|
|
13712
|
+
// Ensure running state reflects work runs from snapshot for freshly loaded
|
|
13713
|
+
// conversations. dev-0.6.6: a persisted `running` work run is NOT evidence
|
|
13714
|
+
// that the conversation is still running — a crashed utility host leaves the
|
|
13715
|
+
// record behind forever. Only a snapshot that reports a live run may
|
|
13716
|
+
// resurrect the running state; otherwise close the stale record locally so
|
|
13717
|
+
// the timer stops and the next input is a new send instead of a queued Next
|
|
13718
|
+
// (the reported "timestamp keeps running but nothing works" state).
|
|
13719
|
+
var runtimeSnapshot = s && s.runtime ? s.runtime : null;
|
|
13720
|
+
var snapshotStatusText = String(s && s.status || '').toLowerCase();
|
|
13721
|
+
var backendStillRunning = !!(runtimeSnapshot && (runtimeSnapshot.running || runtimeSnapshot.stopRequested))
|
|
13722
|
+
|| ['running', 'stopping', 'force_restarting'].indexOf(snapshotStatusText) >= 0;
|
|
13476
13723
|
var runs = state.workRunsByTarget && state.workRunsByTarget[runtimeKeyFor(snapshotTarget.workspaceId, snapshotTarget.conversationId)] || [];
|
|
13477
13724
|
for (var ri = 0; ri < runs.length; ri++) {
|
|
13478
|
-
if (runs[ri].status
|
|
13725
|
+
if (runs[ri].status !== 'running') continue;
|
|
13726
|
+
if (backendStillRunning) {
|
|
13479
13727
|
setConversationRuntimeState(snapshotTarget, 'running', runs[ri].runId);
|
|
13728
|
+
continue;
|
|
13480
13729
|
}
|
|
13730
|
+
runs[ri].status = 'interrupted';
|
|
13731
|
+
if (!runs[ri].endedAt) runs[ri].endedAt = new Date().toISOString();
|
|
13732
|
+
if (typeof updateConversationWorkRunElement === 'function') updateConversationWorkRunElement(runs[ri]);
|
|
13481
13733
|
}
|
|
13734
|
+
if (typeof reconcileLocalRuntimeFromSnapshot === 'function') reconcileLocalRuntimeFromSnapshot(s, snapshotTarget);
|
|
13482
13735
|
var isRunning = !!runningConversationRecord(snapshotTarget.conversationId, snapshotTarget.workspaceId);
|
|
13483
13736
|
setWorking(isRunning);
|
|
13484
13737
|
}
|
|
@@ -13913,6 +14166,66 @@ async function reconcileStaleLocalRuntimeBeforeSend(target) {
|
|
|
13913
14166
|
return true;
|
|
13914
14167
|
}
|
|
13915
14168
|
|
|
14169
|
+
/**
|
|
14170
|
+
* dev-0.6.6: 用权威快照纠正「本地还在跑、后端其实已经死了」的状态。
|
|
14171
|
+
*
|
|
14172
|
+
* 真实现场:utility host 进程被 V8 OOM 杀死后不会再发终态事件,渲染端于是永远
|
|
14173
|
+
* 停在「运行中」——计时一直在走、输入被当成 Next 排进队列、队列也永远等不到执行者。
|
|
14174
|
+
* 这里用已经在手的快照做纠正(不额外请求 IPC),并且绝不与渲染端在飞的发送抢状态。
|
|
14175
|
+
*/
|
|
14176
|
+
function reconcileLocalRuntimeFromSnapshot(snapshot, target) {
|
|
14177
|
+
if (!snapshot || typeof snapshot !== 'object') return false;
|
|
14178
|
+
var owner = normalizeQueuedConversationTarget(target || currentConversationTarget());
|
|
14179
|
+
if (!owner) return false;
|
|
14180
|
+
var key = runtimeKeyFor(owner.workspaceId, owner.conversationId);
|
|
14181
|
+
if (state.activeSendCallsByTarget && state.activeSendCallsByTarget[key]) return false;
|
|
14182
|
+
if (!runningConversationRecord(owner.conversationId, owner.workspaceId)) return false;
|
|
14183
|
+
var runtimeSnapshot = snapshot.runtime || null;
|
|
14184
|
+
var snapshotStatus = String(snapshot.status || '').toLowerCase();
|
|
14185
|
+
var backendStillRunning = !!(runtimeSnapshot && (runtimeSnapshot.running || runtimeSnapshot.stopRequested))
|
|
14186
|
+
|| ['running', 'stopping', 'force_restarting'].indexOf(snapshotStatus) >= 0;
|
|
14187
|
+
if (backendStillRunning) return false;
|
|
14188
|
+
if (['idle', 'completed', 'interrupted', 'force_interrupted', 'error'].indexOf(snapshotStatus) < 0) return false;
|
|
14189
|
+
var runId = String((runtimeSnapshot && runtimeSnapshot.runId) || snapshot.runId || '');
|
|
14190
|
+
var generation = Number((runtimeSnapshot && runtimeSnapshot.generation) || snapshot.generation || 0);
|
|
14191
|
+
setConversationRuntimeState(owner, snapshotStatus, runId, { provisional: false, generation: generation });
|
|
14192
|
+
if (state.nextQueueSchedulesByTarget) {
|
|
14193
|
+
delete state.nextQueueSchedulesByTarget[key];
|
|
14194
|
+
delete state.nextQueueSchedulesByTarget[runtimeKeyFor('', owner.conversationId)];
|
|
14195
|
+
}
|
|
14196
|
+
state._sendInFlight = Object.keys(state.runningConversations || {}).length > 0;
|
|
14197
|
+
setWorking(state._sendInFlight);
|
|
14198
|
+
updateSubmitButtonState();
|
|
14199
|
+
if (window.renderInputStack) window.renderInputStack();
|
|
14200
|
+
return true;
|
|
14201
|
+
}
|
|
14202
|
+
|
|
14203
|
+
/**
|
|
14204
|
+
* While the renderer believes a conversation is running, verify that belief
|
|
14205
|
+
* against the backend. A crashed host cannot publish its own terminal event, so
|
|
14206
|
+
* the watchdog is the only thing that can clear the phantom "running" state.
|
|
14207
|
+
*/
|
|
14208
|
+
var _runtimeReconcileWatchdog = null;
|
|
14209
|
+
function ensureRuntimeReconcileWatchdog() {
|
|
14210
|
+
if (_runtimeReconcileWatchdog || !api.getState) return;
|
|
14211
|
+
_runtimeReconcileWatchdog = setInterval(function() {
|
|
14212
|
+
var records = Object.keys(state.runningConversations || {});
|
|
14213
|
+
if (!records.length) {
|
|
14214
|
+
clearInterval(_runtimeReconcileWatchdog);
|
|
14215
|
+
_runtimeReconcileWatchdog = null;
|
|
14216
|
+
return;
|
|
14217
|
+
}
|
|
14218
|
+
records.forEach(function(key) {
|
|
14219
|
+
var record = (state.runningConversations || {})[key];
|
|
14220
|
+
var target = record && record.target ? record.target : null;
|
|
14221
|
+
if (!target || !target.conversationId) return;
|
|
14222
|
+
Promise.resolve(api.getState(target)).then(function(snapshot) {
|
|
14223
|
+
reconcileLocalRuntimeFromSnapshot(snapshot, target);
|
|
14224
|
+
}).catch(function() {});
|
|
14225
|
+
});
|
|
14226
|
+
}, 15000);
|
|
14227
|
+
}
|
|
14228
|
+
|
|
13916
14229
|
function setSubmitButtonVisual(icon, label, running, stopMode) {
|
|
13917
14230
|
var btn = els['submit-btn'] || document.getElementById('submit-btn');
|
|
13918
14231
|
if (!btn) return;
|
|
@@ -14596,19 +14909,14 @@ function modelDeploymentValueForName(name, providerId) {
|
|
|
14596
14909
|
var modelName = String(name || '').trim();
|
|
14597
14910
|
if (!modelName) return '';
|
|
14598
14911
|
var providers = Array.isArray(state.providers) ? state.providers : [];
|
|
14599
|
-
var
|
|
14600
|
-
|
|
14601
|
-
|
|
14602
|
-
if (
|
|
14603
|
-
|
|
14604
|
-
|
|
14605
|
-
|
|
14606
|
-
|
|
14607
|
-
}
|
|
14608
|
-
}
|
|
14609
|
-
if (!match) return modelName;
|
|
14610
|
-
var providerIdValue = String(match.provider.id || match.provider.name || '');
|
|
14611
|
-
return 'deployment:' + encodeURIComponent(providerIdValue) + ':' + encodeURIComponent(match.name);
|
|
14912
|
+
var matches = [];
|
|
14913
|
+
providers.forEach(function(provider) {
|
|
14914
|
+
if (providerId && String(provider.id || '') !== String(providerId)) return;
|
|
14915
|
+
if ((provider.models || []).some(function(model) { return (typeof model === 'string' ? model : model.name) === modelName; })) matches.push(provider);
|
|
14916
|
+
});
|
|
14917
|
+
// Missing/ambiguous identities must never select another provider's credentials.
|
|
14918
|
+
if (matches.length !== 1 || !matches[0].id) return '';
|
|
14919
|
+
return 'deployment:' + encodeURIComponent(matches[0].id) + ':' + encodeURIComponent(modelName);
|
|
14612
14920
|
}
|
|
14613
14921
|
|
|
14614
14922
|
// 不可用模型回退同步输入框下方选择区:固定模型选择被回退时,选择区
|
|
@@ -18500,8 +18808,6 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
18500
18808
|
addMsg('user', displayText, opts.fromQueue ? 'next-build' : requestedMode, state.model, opts.optimisticMessageIndex, { attachments: optimisticAttachments });
|
|
18501
18809
|
}
|
|
18502
18810
|
renderConversationBranchPagers();
|
|
18503
|
-
applyAutoRouteRatingState(null);
|
|
18504
|
-
renderAutoRouteRatingControls();
|
|
18505
18811
|
}
|
|
18506
18812
|
var responseMsg = null;
|
|
18507
18813
|
var sendFailure = '';
|
|
@@ -18545,7 +18851,6 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
18545
18851
|
setConversationRuntimeState(lockedTarget, receivedSendStatus, r.runId, { provisional: false, generation: r.generation || 0, runtimeKey: r.runtimeKey || '' });
|
|
18546
18852
|
}
|
|
18547
18853
|
var stillActive = sendResultOwnsRuntime && sendLoadGeneration === state.conversationLoadGeneration && isActiveConversationTarget(lockedTarget) && isViewingRuntimeConversationBranch(lockedTarget);
|
|
18548
|
-
if (stillActive) applyAutoRouteRatingState(r);
|
|
18549
18854
|
if (stillActive && r && Array.isArray(r.workRuns)) syncWorkRunsSnapshot(r.workRuns, lockedTarget);
|
|
18550
18855
|
var responseOwnedByRun = !!(r && Array.isArray(r.workRuns) && r.workRuns.some(function(run) {
|
|
18551
18856
|
return (run.events || []).some(function(event) { return ['response', 'final_response'].indexOf(String(event.type || '').toLowerCase()) >= 0; });
|
|
@@ -18648,7 +18953,6 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
18648
18953
|
}
|
|
18649
18954
|
if (stillActiveAfterRefresh && s && s.conversationPlan) state.conversationPlan = normalizeConversationPlan(s.conversationPlan);
|
|
18650
18955
|
if (stillActiveAfterRefresh && s && Array.isArray(s.workRuns)) {
|
|
18651
|
-
applyAutoRouteRatingState(s);
|
|
18652
18956
|
hydrateConversationBranchState(s);
|
|
18653
18957
|
syncWorkRunsSnapshot(s.workRuns, lockedTarget);
|
|
18654
18958
|
if (Array.isArray(s.chatMessages)) renderChatMessages(s.chatMessages, lockedTarget);
|
|
@@ -19241,8 +19545,6 @@ window.refreshRightStatus = function(options) {
|
|
|
19241
19545
|
var statusContextRequest = captureContextWindowSnapshotRequest(statusTarget);
|
|
19242
19546
|
return api.getState(statusTarget).then(function(s) {
|
|
19243
19547
|
if (!isActiveConversationTarget(statusTarget) || statusLoadGeneration !== state.conversationLoadGeneration) return null;
|
|
19244
|
-
applyAutoRouteRatingState(s);
|
|
19245
|
-
renderAutoRouteRatingControls();
|
|
19246
19548
|
applyConversationCommandSnapshot(s, statusTarget);
|
|
19247
19549
|
if (s && s.model !== undefined) state.model = s.model;
|
|
19248
19550
|
if (s && s.modelLabel) state.modelLabel = s.modelLabel;
|
|
@@ -20516,6 +20818,29 @@ function renderGeneralSettings() {
|
|
|
20516
20818
|
'</div>';
|
|
20517
20819
|
}
|
|
20518
20820
|
|
|
20821
|
+
/**
|
|
20822
|
+
* Current-conversation-model decision policy. No separate JEV model, runtime,
|
|
20823
|
+
* model ID, artifact path, or selector is exposed here. When Auto is off every
|
|
20824
|
+
* policy control is locked.
|
|
20825
|
+
*/
|
|
20826
|
+
function jevDecisionModelSettingsHtml() {
|
|
20827
|
+
var jev = state.jevDecisionModel && typeof state.jevDecisionModel === 'object' ? state.jevDecisionModel : {};
|
|
20828
|
+
var locked = jev.locked === true || state.autoSwitch === 'off';
|
|
20829
|
+
var disabled = locked ? ' disabled' : '';
|
|
20830
|
+
var enabled = jev.enabled !== false;
|
|
20831
|
+
var currentModelStatus = t('model.jevCurrentModelStatus');
|
|
20832
|
+
return '<div class="provider-card" id="jev-decision-model-card" data-jev-locked="' + (locked ? 'true' : 'false') + '" style="margin-top:12px;">' +
|
|
20833
|
+
'<div id="jev-decision-model-title" style="font-size:13px;font-weight:700;color:var(--text);margin-bottom:4px;">' + esc(t('model.jevTitle')) + '</div>' +
|
|
20834
|
+
'<div id="jev-decision-model-desc" style="font-size:11px;color:var(--text-dim);line-height:1.45;margin-bottom:6px;">' + esc(t('model.jevDesc')) + '</div>' +
|
|
20835
|
+
(locked ? '<div id="jev-decision-model-lock" class="model-eval-pending" style="font-size:11px;margin-bottom:6px;">' + esc(t('model.jevLocked')) + '</div>' : '') +
|
|
20836
|
+
'<div class="setting-row"><span class="setting-label">' + esc(t('model.jevEnabled')) + '</span><div class="setting-control">' +
|
|
20837
|
+
'<input type="checkbox" id="jev-field-enabled" data-jev-field="enabled" role="switch"' + (enabled ? ' checked' : '') + disabled + ' onchange="window.setJevDecisionModel({enabled:this.checked})"></div></div>' +
|
|
20838
|
+
'<div class="setting-row"><span class="setting-label">' + esc(t('model.jevTimeout')) + '</span><div class="setting-control">' +
|
|
20839
|
+
'<input type="number" id="jev-field-timeout" data-jev-field="timeout-ms" min="100" max="30000" step="100" style="width:90px;" value="' + escAttr(String(Number(jev.timeoutMs) || 3000)) + '"' + disabled + ' onchange="window.setJevDecisionModel({timeoutMs:Number(this.value)})"></div></div>' +
|
|
20840
|
+
'<div id="jev-decision-model-status" data-jev-model="current-eligible-model" data-jev-thinking="off" data-jev-output="strict-json-schema" style="font-size:11px;color:var(--text-dim);margin-top:4px;">' + esc(currentModelStatus) + '</div>' +
|
|
20841
|
+
'</div>';
|
|
20842
|
+
}
|
|
20843
|
+
|
|
20519
20844
|
function renderModelSettings() {
|
|
20520
20845
|
var providersHtml = '';
|
|
20521
20846
|
for (var i = 0; i < state.providers.length; i++) {
|
|
@@ -20591,11 +20916,11 @@ function renderModelSettings() {
|
|
|
20591
20916
|
'<option value="off"' + (state.fallbackOnUnavailable === 'off' ? ' selected' : '') + '>' + esc(t('common.off')) + '</option>' +
|
|
20592
20917
|
'<option value="on"' + (state.fallbackOnUnavailable === 'on' ? ' selected' : '') + '>' + esc(t('common.on')) + '</option></select></div></div>' +
|
|
20593
20918
|
'<div class="setting-row"><span class="setting-label">' + esc(t('model.preference')) + '</span><div class="setting-control"><select onchange="window.setSwitchTendency(this.value)">' +
|
|
20594
|
-
'<option value="
|
|
20595
|
-
'<option value="balanced"' + (state.switchTendency === 'balanced' ? ' selected' : '') + '>' + esc(t('model.
|
|
20596
|
-
'<option value="
|
|
20597
|
-
'<
|
|
20598
|
-
|
|
20919
|
+
'<option value="conservative"' + (state.switchTendency === 'conservative' ? ' selected' : '') + '>' + esc(t('model.conservativeTendency')) + '</option>' +
|
|
20920
|
+
'<option value="balanced"' + (state.switchTendency === 'balanced' ? ' selected' : '') + '>' + esc(t('model.balancedTendency')) + '</option>' +
|
|
20921
|
+
'<option value="aggressive"' + (state.switchTendency === 'aggressive' ? ' selected' : '') + '>' + esc(t('model.aggressiveTendency')) + '</option></select></div></div>' +
|
|
20922
|
+
'<div class="setting-desc" style="margin:4px 0 12px;">' + esc(t('model.preferenceDesc')) + '</div>' +
|
|
20923
|
+
jevDecisionModelSettingsHtml() +
|
|
20599
20924
|
'</div>' +
|
|
20600
20925
|
'<div class="provider-card" style="margin-top:12px;">' +
|
|
20601
20926
|
'<label for="global-agent-prompt" style="display:block;font-size:13px;font-weight:700;color:var(--text);margin-bottom:6px;">' + esc(t('model.globalAgentPrompt')) + '</label>' +
|
|
@@ -21076,6 +21401,47 @@ window.setAutoSwitchMode = function(v) {
|
|
|
21076
21401
|
state.autoSwitchScope = v === 'provider' ? 'provider' : 'all';
|
|
21077
21402
|
api.saveConfig({ autoSwitch: state.autoSwitch, autoSwitchScope: state.autoSwitchScope });
|
|
21078
21403
|
window.refreshModelSelect();
|
|
21404
|
+
// Auto off locks the decision-model editor; Auto on unlocks it again. Ask the
|
|
21405
|
+
// backend for the authoritative lock state instead of guessing locally.
|
|
21406
|
+
window.refreshJevDecisionModel();
|
|
21407
|
+
};
|
|
21408
|
+
|
|
21409
|
+
window.refreshJevDecisionModel = function() {
|
|
21410
|
+
return api.getState().then(function(s) {
|
|
21411
|
+
window.applyJevDecisionModelState(s);
|
|
21412
|
+
var panel = document.getElementById('stab-models');
|
|
21413
|
+
if (panel && panel.getAttribute('data-lazy') !== '1') panel.innerHTML = renderModelSettings();
|
|
21414
|
+
return state.jevDecisionModel;
|
|
21415
|
+
}).catch(function() { return state.jevDecisionModel; });
|
|
21416
|
+
};
|
|
21417
|
+
|
|
21418
|
+
window.applyJevDecisionModelState = function(snapshot) {
|
|
21419
|
+
if (snapshot && typeof snapshot === 'object' && snapshot.jevDecisionModel && typeof snapshot.jevDecisionModel === 'object') {
|
|
21420
|
+
state.jevDecisionModel = snapshot.jevDecisionModel;
|
|
21421
|
+
}
|
|
21422
|
+
};
|
|
21423
|
+
|
|
21424
|
+
/**
|
|
21425
|
+
* Persist one decision-model field. The backend is authoritative: it refuses
|
|
21426
|
+
* every edit while Auto is off (`auto_disabled`) and rejects invalid values, so
|
|
21427
|
+
* the UI never claims a change that did not happen.
|
|
21428
|
+
*/
|
|
21429
|
+
window.setJevDecisionModel = function(patch) {
|
|
21430
|
+
if (!api.setJevDecisionModel) return Promise.resolve(null);
|
|
21431
|
+
return api.setJevDecisionModel(patch || {}).then(function(result) {
|
|
21432
|
+
if (result && result.state) state.jevDecisionModel = result.state;
|
|
21433
|
+
var panel = document.getElementById('stab-models');
|
|
21434
|
+
if (panel) panel.innerHTML = renderModelSettings();
|
|
21435
|
+
if (!result || result.ok === false) {
|
|
21436
|
+
showUiNotice(result && result.reason === 'auto_disabled' ? t('model.jevLocked') : t('model.jevRefused'), 'error', 'jev-decision-model');
|
|
21437
|
+
} else {
|
|
21438
|
+
showUiNotice(t('model.jevSaved'), 'info', 'jev-decision-model');
|
|
21439
|
+
}
|
|
21440
|
+
return result;
|
|
21441
|
+
}).catch(function(error) {
|
|
21442
|
+
showUiNotice(t('model.jevRefused') + ' ' + (error && error.message ? error.message : String(error)), 'error', 'jev-decision-model');
|
|
21443
|
+
return null;
|
|
21444
|
+
});
|
|
21079
21445
|
};
|
|
21080
21446
|
|
|
21081
21447
|
var systemColorScheme = window.matchMedia('(prefers-color-scheme: dark)');
|
|
@@ -21218,14 +21584,10 @@ window.setFallbackOnUnavailable = function(v) {
|
|
|
21218
21584
|
};
|
|
21219
21585
|
|
|
21220
21586
|
window.setSwitchTendency = function(v) {
|
|
21221
|
-
state.switchTendency = ['
|
|
21587
|
+
state.switchTendency = ['conservative', 'balanced', 'aggressive'].indexOf(v) >= 0 ? v : 'balanced';
|
|
21222
21588
|
api.saveConfig({ switchTendency: state.switchTendency });
|
|
21223
21589
|
};
|
|
21224
21590
|
|
|
21225
|
-
window.clearLearnedAutoPreferences = function() {
|
|
21226
|
-
api.saveConfig({ clearLearnedAutoPreferences: true }).catch(function(){});
|
|
21227
|
-
};
|
|
21228
|
-
|
|
21229
21591
|
window.setNativeToolEnabled = function(name, enabled) {
|
|
21230
21592
|
if (!name) return;
|
|
21231
21593
|
state.nativeToolEnabled = state.nativeToolEnabled || {};
|
|
@@ -21336,6 +21698,7 @@ window.removeProvider = function(idx) {
|
|
|
21336
21698
|
|
|
21337
21699
|
window.addModel = function() {
|
|
21338
21700
|
window.openSubWin(t('model.addModel'), '<div class="auto-input-group"><label>' + esc(t('model.modelName')) + '</label><input id="new-model-name" placeholder="gpt-4"></div>' +
|
|
21701
|
+
'<div class="auto-input-group"><label>' + esc(t('model.displayName')) + '</label><input id="new-model-display"></div>' +
|
|
21339
21702
|
'<div class="auto-input-group"><label>' + esc(t('model.provider')) + '</label><select id="new-model-provider">' + state.providers.map(function(p,i){return '<option value="'+i+'">'+esc(p.name)+'</option>';}).join('') + '</select></div>' +
|
|
21340
21703
|
'<div class="auto-input-group"><label>' + esc(t('model.contextSize')) + '</label><input id="new-model-ctx" type="number" value="128000" placeholder="tokens"></div>' +
|
|
21341
21704
|
'<div style="display:flex;gap:10px;margin-bottom:10px;"><label style="font-size:11px;color:var(--text-dim);"><input type="checkbox" id="new-model-vision"> ' + esc(t('model.vision')) + '</label>' +
|
|
@@ -21367,7 +21730,8 @@ window.serializeThinkingTierMap = function(map) {
|
|
|
21367
21730
|
};
|
|
21368
21731
|
|
|
21369
21732
|
window.saveNewModel = function() {
|
|
21370
|
-
var name = document.getElementById('new-model-name').value;
|
|
21733
|
+
var name = document.getElementById('new-model-name').value.trim();
|
|
21734
|
+
var display = document.getElementById('new-model-display').value.trim();
|
|
21371
21735
|
var provIdx = parseInt(document.getElementById('new-model-provider').value);
|
|
21372
21736
|
var ctxEl = document.getElementById('new-model-ctx');
|
|
21373
21737
|
var visionEl = document.getElementById('new-model-vision');
|
|
@@ -21376,10 +21740,11 @@ window.saveNewModel = function() {
|
|
|
21376
21740
|
var tierMapEl = document.getElementById('new-model-tier-map');
|
|
21377
21741
|
if (name && !isNaN(provIdx) && state.providers[provIdx]) {
|
|
21378
21742
|
if (!state.providers[provIdx].models) state.providers[provIdx].models = [];
|
|
21743
|
+
if (state.providers[provIdx].models.some(function(m) { return String(typeof m === 'string' ? m : m.name).toLowerCase() === name.toLowerCase(); })) { showUiNotice('该供应商下模型标识已存在', 'error', 'duplicate-model'); return; }
|
|
21379
21744
|
var tierMap = window.parseThinkingTierMap(tierMapEl ? tierMapEl.value : '');
|
|
21380
21745
|
var entry = {
|
|
21381
21746
|
name: name,
|
|
21382
|
-
display: name,
|
|
21747
|
+
display: display || name,
|
|
21383
21748
|
description: descEl ? descEl.value : '',
|
|
21384
21749
|
max_tokens: ctxEl ? parseInt(ctxEl.value || '128000') : 128000,
|
|
21385
21750
|
vision: !!(visionEl && visionEl.checked),
|
|
@@ -21410,8 +21775,10 @@ window.editModel = function(provIdx, modelIdx) {
|
|
|
21410
21775
|
var model = provider.models[modelIdx];
|
|
21411
21776
|
if (!model) return;
|
|
21412
21777
|
if (typeof model === 'string') model = { name: model, display: model };
|
|
21778
|
+
state.modelEditIdentity = { providerId: provider.id, modelName: model.name };
|
|
21413
21779
|
window.openSubWin(t('common.edit'), '<div class="auto-input-group"><label>' + esc(t('model.modelName')) + '</label><input id="edit-model-name" value="' + escAttr(model.name || '') + '"></div>' +
|
|
21414
|
-
'<div class="auto-input-group"><label>' + esc(t('model.
|
|
21780
|
+
'<div class="auto-input-group"><label>' + esc(t('model.displayName')) + '</label><input id="edit-model-display" value="' + escAttr(model.display || '') + '"></div>' +
|
|
21781
|
+
'<div class="auto-input-group"><label>' + esc(t('model.provider')) + '</label><select id="edit-model-provider" disabled>' + state.providers.map(function(p,i){return '<option value="'+i+'"' + (i === provIdx ? ' selected' : '') + '>'+esc(p.name)+'</option>';}).join('') + '</select></div>' +
|
|
21415
21782
|
'<div class="auto-input-group"><label>' + esc(t('model.contextSize')) + '</label><input id="edit-model-ctx" type="number" value="' + escAttr(model.max_tokens || 4096) + '"></div>' +
|
|
21416
21783
|
'<div style="display:flex;gap:10px;margin-bottom:10px;"><label style="font-size:11px;color:var(--text-dim);"><input type="checkbox" id="edit-model-vision"' + (model.vision ? ' checked' : '') + '> ' + esc(t('model.vision')) + '</label>' +
|
|
21417
21784
|
'<label style="font-size:11px;color:var(--text-dim);"><input type="checkbox" id="edit-model-thinking"' + (model.thinking ? ' checked' : '') + '> ' + esc(t('model.thinking')) + '</label></div>' +
|
|
@@ -21422,7 +21789,11 @@ window.editModel = function(provIdx, modelIdx) {
|
|
|
21422
21789
|
};
|
|
21423
21790
|
|
|
21424
21791
|
window.saveModelEdit = function(oldProvIdx, modelIdx) {
|
|
21792
|
+
var identity = state.modelEditIdentity;
|
|
21793
|
+
if (!identity) return;
|
|
21794
|
+
oldProvIdx = state.providers.findIndex(function(p) { return p.id === identity.providerId; });
|
|
21425
21795
|
var oldProvider = state.providers[oldProvIdx];
|
|
21796
|
+
modelIdx = oldProvider && oldProvider.models ? oldProvider.models.findIndex(function(m) { return (typeof m === 'string' ? m : m.name) === identity.modelName; }) : -1;
|
|
21426
21797
|
if (!oldProvider || !oldProvider.models || !oldProvider.models[modelIdx]) return;
|
|
21427
21798
|
var nameEl = document.getElementById('edit-model-name');
|
|
21428
21799
|
var provEl = document.getElementById('edit-model-provider');
|
|
@@ -21432,15 +21803,17 @@ window.saveModelEdit = function(oldProvIdx, modelIdx) {
|
|
|
21432
21803
|
var descEl = document.getElementById('edit-model-desc');
|
|
21433
21804
|
var tierMapEl = document.getElementById('edit-model-tier-map');
|
|
21434
21805
|
var name = nameEl ? nameEl.value.trim() : '';
|
|
21435
|
-
var newProvIdx =
|
|
21806
|
+
var newProvIdx = oldProvIdx;
|
|
21807
|
+
var displayEl = document.getElementById('edit-model-display');
|
|
21436
21808
|
if (!name || isNaN(newProvIdx) || !state.providers[newProvIdx]) return;
|
|
21809
|
+
if (oldProvider.models.some(function(m, i) { return i !== modelIdx && String(typeof m === 'string' ? m : m.name).toLowerCase() === name.toLowerCase(); })) { showUiNotice('该供应商下模型标识已存在', 'error', 'duplicate-model'); return; }
|
|
21437
21810
|
var previous = oldProvider.models[modelIdx];
|
|
21438
21811
|
if (typeof previous === 'string') previous = { name: previous, display: previous };
|
|
21439
21812
|
var tierMap = window.parseThinkingTierMap(tierMapEl ? tierMapEl.value : '');
|
|
21440
21813
|
var updated = Object.assign({}, previous, {
|
|
21441
21814
|
_previous_name: previous.name || name,
|
|
21442
21815
|
name: name,
|
|
21443
|
-
display: name,
|
|
21816
|
+
display: displayEl ? displayEl.value.trim() || name : name,
|
|
21444
21817
|
description: descEl ? descEl.value : '',
|
|
21445
21818
|
max_tokens: ctxEl ? parseInt(ctxEl.value || '4096') : 4096,
|
|
21446
21819
|
vision: !!(visionEl && visionEl.checked),
|
|
@@ -24611,6 +24984,34 @@ window.activateConversationWithGlass = function(idx, row) {
|
|
|
24611
24984
|
} });
|
|
24612
24985
|
};
|
|
24613
24986
|
|
|
24987
|
+
/**
|
|
24988
|
+
* dev-0.6.6 hotfix: 拖动排序期间,行的顺序归用户所有。
|
|
24989
|
+
*
|
|
24990
|
+
* 拖动过程中任何权威列表推送(重命名、跑马灯状态、其他窗口或移动端)都会走到
|
|
24991
|
+
* renderConversations()。旧实现无条件把行重排回 state 顺序,用户刚刚让出来的落点
|
|
24992
|
+
* 会被抹掉,松手时提交的就是被重置后的位置——「拖到哪就落在哪」因此失效。
|
|
24993
|
+
* 拖动期间以 DOM 现有顺序为准,只把刚出现的新行按 state 位置插进去。
|
|
24994
|
+
*/
|
|
24995
|
+
function conversationRowOrderDuringDrag(orderedRows, list) {
|
|
24996
|
+
var dragRow = conversationNativeDrag.row;
|
|
24997
|
+
if (!dragRow || dragRow.parentNode !== list) return orderedRows;
|
|
24998
|
+
var domRows = Array.prototype.slice.call(list.querySelectorAll('.conv-item'));
|
|
24999
|
+
var stateIndex = new Map();
|
|
25000
|
+
orderedRows.forEach(function(row, index) { stateIndex.set(row, index); });
|
|
25001
|
+
var sequence = domRows.filter(function(row) { return stateIndex.has(row); });
|
|
25002
|
+
if (!sequence.length) return orderedRows;
|
|
25003
|
+
orderedRows.forEach(function(row) {
|
|
25004
|
+
if (sequence.indexOf(row) >= 0) return;
|
|
25005
|
+
var index = stateIndex.get(row);
|
|
25006
|
+
var insertAt = sequence.length;
|
|
25007
|
+
for (var i = 0; i < sequence.length; i++) {
|
|
25008
|
+
if (stateIndex.get(sequence[i]) > index) { insertAt = i; break; }
|
|
25009
|
+
}
|
|
25010
|
+
sequence.splice(insertAt, 0, row);
|
|
25011
|
+
});
|
|
25012
|
+
return sequence;
|
|
25013
|
+
}
|
|
25014
|
+
|
|
24614
25015
|
function renderConversations() {
|
|
24615
25016
|
var list = els['conversation-list'];
|
|
24616
25017
|
if (!list) return;
|
|
@@ -24771,7 +25172,8 @@ function renderConversations() {
|
|
|
24771
25172
|
var stale = existingRows[id];
|
|
24772
25173
|
if (stale && stale.parentNode === list) stale.parentNode.removeChild(stale);
|
|
24773
25174
|
});
|
|
24774
|
-
orderedRows
|
|
25175
|
+
var rowsToPlace = conversationRowOrderDuringDrag(orderedRows, list);
|
|
25176
|
+
rowsToPlace.forEach(function(row, idx) {
|
|
24775
25177
|
var current = list.children[idx];
|
|
24776
25178
|
if (current === row) return;
|
|
24777
25179
|
list.insertBefore(row, current || null);
|
|
@@ -24860,6 +25262,7 @@ window.newConversation = function(workspaceReference, branchCommunication) {
|
|
|
24860
25262
|
state._promptBoundKey = conversationDraftKey();
|
|
24861
25263
|
renderConversations();
|
|
24862
25264
|
els['chat-area'].innerHTML = '';
|
|
25265
|
+
state.renderedChatTargetKey = '';
|
|
24863
25266
|
var target = currentConversationTarget(id);
|
|
24864
25267
|
var modelRevisionAtActivation = Number(state.modelSelectionRevision || 0);
|
|
24865
25268
|
var activation = api.activateConversation ? api.activateConversation(target) : (api.ensureConversation ? api.ensureConversation(target) : Promise.resolve(null));
|
|
@@ -24947,6 +25350,11 @@ window.switchConversation = function(idx) {
|
|
|
24947
25350
|
setActiveWorkspaceConversation(idx);
|
|
24948
25351
|
var activeId = activeConversationId();
|
|
24949
25352
|
var activeBrowserTarget = currentConversationTarget(activeId);
|
|
25353
|
+
// Rebind the visible composer to the selected conversation before any async
|
|
25354
|
+
// backend hydration. Users can type as soon as the row is selected; leaving
|
|
25355
|
+
// _promptBoundKey on the previous conversation would persist that input to
|
|
25356
|
+
// the wrong target while syncBackendConversation is still in flight.
|
|
25357
|
+
restoreConversationDraft(activeBrowserTarget);
|
|
24950
25358
|
var activeBrowserView = browserGuestForTarget(activeBrowserTarget);
|
|
24951
25359
|
if (activeBrowserView) setActiveBrowserGuest(activeBrowserView, activeBrowserTarget);
|
|
24952
25360
|
else if (state.rightTab === 'browser') window.ensureBrowserPanel({ target: activeBrowserTarget, activate: true }).catch(function(error) {
|
|
@@ -25073,6 +25481,7 @@ window.archiveConv = function(conversationId) {
|
|
|
25073
25481
|
if (priorActiveId === targetId) {
|
|
25074
25482
|
state.activeBackendConversationId = '';
|
|
25075
25483
|
if (els['chat-area']) els['chat-area'].innerHTML = '';
|
|
25484
|
+
state.renderedChatTargetKey = '';
|
|
25076
25485
|
if (window.renderFlowTakeover) window.renderFlowTakeover(false, '', { target: targetRuntime });
|
|
25077
25486
|
}
|
|
25078
25487
|
} else {
|
|
@@ -25085,6 +25494,7 @@ window.archiveConv = function(conversationId) {
|
|
|
25085
25494
|
if (priorActiveId === targetId) {
|
|
25086
25495
|
state.activeBackendConversationId = nextActiveId;
|
|
25087
25496
|
if (els['chat-area']) els['chat-area'].innerHTML = '';
|
|
25497
|
+
state.renderedChatTargetKey = '';
|
|
25088
25498
|
}
|
|
25089
25499
|
}
|
|
25090
25500
|
if (state.flowTakeovers) {
|
|
@@ -26964,7 +27374,15 @@ window.commitConversationDomOrder = function() {
|
|
|
26964
27374
|
var ids = Array.from(list.querySelectorAll('.conv-item')).map(function(row) {
|
|
26965
27375
|
return String(row.getAttribute('data-conversation-id') || '');
|
|
26966
27376
|
}).filter(function(id) { return !!byId[id]; });
|
|
26967
|
-
if (ids.length
|
|
27377
|
+
if (!ids.length) return renderConversations();
|
|
27378
|
+
// dev-0.6.6 hotfix: 拖动期间权威列表可能新增/删除会话(重命名、其他窗口、
|
|
27379
|
+
// 移动端)。旧实现一旦发现行数与 state 不一致就整表重渲染,用户刚拖出来的落点
|
|
27380
|
+
// 会被丢弃。这里以 DOM 顺序为准,把没有出现在 DOM 里的会话按 state 顺序补在末尾,
|
|
27381
|
+
// 保证提交的永远是一份完整、且以用户拖动结果为准的顺序。
|
|
27382
|
+
var missingIds = convs
|
|
27383
|
+
.filter(function(item) { return ids.indexOf(String(item.id)) < 0; })
|
|
27384
|
+
.map(function(item) { return String(item.id); });
|
|
27385
|
+
ids = ids.concat(missingIds);
|
|
26968
27386
|
var activeId = activeConversationId();
|
|
26969
27387
|
convs.splice.apply(convs, [0, convs.length].concat(ids.map(function(id) { return byId[id]; })));
|
|
26970
27388
|
setActiveWorkspaceConversationById(activeId);
|
|
@@ -28439,7 +28857,6 @@ function schedulePostStartupUiRendering() {
|
|
|
28439
28857
|
if (Array.isArray(s.wslDistros)) state.wslDistros = s.wslDistros;
|
|
28440
28858
|
if (s.conversationId) state.activeBackendConversationId = s.conversationId;
|
|
28441
28859
|
if (s.conversationPlan) state.conversationPlan = normalizeConversationPlan(s.conversationPlan);
|
|
28442
|
-
applyAutoRouteRatingState(s);
|
|
28443
28860
|
if (s.chatMessages) {
|
|
28444
28861
|
hydrateConversationBranchState(s);
|
|
28445
28862
|
renderChatMessages(s.chatMessages);
|
|
@@ -28447,10 +28864,11 @@ function schedulePostStartupUiRendering() {
|
|
|
28447
28864
|
if (s.defaultFlow) state.defaultFlow = s.defaultFlow;
|
|
28448
28865
|
if (s.autoSwitch !== undefined) state.autoSwitch = s.autoSwitch ? 'on' : 'off';
|
|
28449
28866
|
if (s.autoSwitchScope) state.autoSwitchScope = s.autoSwitchScope === 'provider' ? 'provider' : 'all';
|
|
28867
|
+
window.applyJevDecisionModelState(s);
|
|
28450
28868
|
if (s.openAIApiMode) state.openAIApiMode = (s.openAIApiMode === 'chat' || s.openAIApiMode === 'responses') ? s.openAIApiMode : 'chat_stream';
|
|
28451
28869
|
if (s.fallbackOnUnavailable !== undefined) state.fallbackOnUnavailable = s.fallbackOnUnavailable ? 'on' : 'off';
|
|
28452
28870
|
if (s.switchTendency) {
|
|
28453
|
-
state.switchTendency =
|
|
28871
|
+
state.switchTendency = ['conservative', 'balanced', 'aggressive'].indexOf(s.switchTendency) >= 0 ? s.switchTendency : 'balanced';
|
|
28454
28872
|
}
|
|
28455
28873
|
if (s.providers) {
|
|
28456
28874
|
state.providers = s.providers;
|