newmark-agent 0.5.15 → 0.6.1
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/dist/conversation-utility-host.bundle.cjs +1271 -1171
- package/dist/core/agent.d.ts +3 -2
- package/dist/core/agent.js +53 -97
- package/dist/core/agentKernelRunner.js +14 -22
- package/dist/core/autoRouter.js +1 -24
- package/dist/core/config.d.ts +2 -0
- package/dist/core/config.js +7 -1
- package/dist/core/conversationKernel.js +15 -8
- package/dist/core/modelResponseHealth.d.ts +17 -0
- package/dist/core/modelResponseHealth.js +193 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/llm/provider.js +4 -4
- package/dist/ui/index.html +138 -74
- package/dist/wsl-agent-host.bundle.cjs +1274 -1174
- package/package.json +2 -2
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readModelResponseHealth = readModelResponseHealth;
|
|
37
|
+
exports.recordModelResponseHealth = recordModelResponseHealth;
|
|
38
|
+
exports.observeModelResponses = observeModelResponses;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const crypto_1 = require("crypto");
|
|
42
|
+
const facets = ['text', 'vision', 'tools'];
|
|
43
|
+
let lastReceiptStamp = 0;
|
|
44
|
+
const cache = new Map();
|
|
45
|
+
function directory(root, identity) {
|
|
46
|
+
const key = (0, crypto_1.createHash)('sha256').update(JSON.stringify(identity)).digest('hex');
|
|
47
|
+
return path.join(root, 'Runtime', 'model-response-health', key);
|
|
48
|
+
}
|
|
49
|
+
function records(dir) {
|
|
50
|
+
try {
|
|
51
|
+
return fs.readdirSync(dir).filter(name => /^\d{16}-[a-f0-9-]+\.json$/.test(name)).sort().reverse().flatMap(name => {
|
|
52
|
+
try {
|
|
53
|
+
return [{ name, health: JSON.parse(fs.readFileSync(path.join(dir, name), 'utf8')) }];
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function readModelResponseHealth(root, identity) {
|
|
65
|
+
const dir = directory(root, identity), saved = cache.get(dir);
|
|
66
|
+
let revision = -1;
|
|
67
|
+
try {
|
|
68
|
+
revision = fs.statSync(dir).mtimeMs;
|
|
69
|
+
}
|
|
70
|
+
catch { /* no observations yet */ }
|
|
71
|
+
if (saved && saved.until > Date.now() && saved.revision === revision)
|
|
72
|
+
return saved.health;
|
|
73
|
+
const health = {};
|
|
74
|
+
for (const record of records(dir))
|
|
75
|
+
for (const facet of facets) {
|
|
76
|
+
const entry = record.health[facet];
|
|
77
|
+
if (!health[facet] && entry && typeof entry.ok === 'boolean')
|
|
78
|
+
health[facet] = entry;
|
|
79
|
+
}
|
|
80
|
+
if (cache.size > 512)
|
|
81
|
+
cache.clear();
|
|
82
|
+
cache.set(dir, { until: Date.now() + 150, revision, health });
|
|
83
|
+
return health;
|
|
84
|
+
}
|
|
85
|
+
function recordModelResponseHealth(root, identity, updates) {
|
|
86
|
+
// Immutable, atomic receipts merge independently across GUI/utility/WSL
|
|
87
|
+
// workers. A text result never overwrites a vision/tool result. No secrets,
|
|
88
|
+
// request bodies or server error text enter the observation files.
|
|
89
|
+
const dir = directory(root, identity), now = Date.now(), health = {};
|
|
90
|
+
for (const facet of facets)
|
|
91
|
+
if (typeof updates[facet] === 'boolean')
|
|
92
|
+
health[facet] = { ok: updates[facet], at: new Date(now).toISOString() };
|
|
93
|
+
if (!Object.keys(health).length)
|
|
94
|
+
return;
|
|
95
|
+
try {
|
|
96
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
97
|
+
const latestStamp = Number(records(dir)[0]?.name.split('-')[0] || 0);
|
|
98
|
+
lastReceiptStamp = Math.max(now * 1000, lastReceiptStamp + 1, latestStamp + 1);
|
|
99
|
+
const file = path.join(dir, `${String(lastReceiptStamp).padStart(16, '0')}-${(0, crypto_1.randomUUID)()}.json`);
|
|
100
|
+
fs.writeFileSync(file + '.tmp', JSON.stringify(health));
|
|
101
|
+
fs.renameSync(file + '.tmp', file);
|
|
102
|
+
const counts = {};
|
|
103
|
+
for (const [index, record] of records(dir).entries()) {
|
|
104
|
+
let keep = index < 4;
|
|
105
|
+
for (const facet of facets)
|
|
106
|
+
if (record.health[facet]) {
|
|
107
|
+
counts[facet] = (counts[facet] || 0) + 1;
|
|
108
|
+
if (counts[facet] <= 2)
|
|
109
|
+
keep = true;
|
|
110
|
+
}
|
|
111
|
+
if (!keep) {
|
|
112
|
+
try {
|
|
113
|
+
fs.unlinkSync(path.join(dir, record.name));
|
|
114
|
+
}
|
|
115
|
+
catch { /* another writer may have pruned it */ }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
cache.delete(dir);
|
|
119
|
+
}
|
|
120
|
+
catch { /* Diagnostic persistence must never break input or transport. */ }
|
|
121
|
+
}
|
|
122
|
+
function hasImage(value) {
|
|
123
|
+
if (Array.isArray(value))
|
|
124
|
+
return value.some(hasImage);
|
|
125
|
+
if (!value || typeof value !== 'object')
|
|
126
|
+
return false;
|
|
127
|
+
const entry = value;
|
|
128
|
+
return ['image_url', 'input_image', 'image'].includes(String(entry.type || '')) || hasImage(entry.content);
|
|
129
|
+
}
|
|
130
|
+
function failedFacet(error, images) {
|
|
131
|
+
const text = String(error).toLowerCase();
|
|
132
|
+
if (/image|vision|multimodal/.test(text) && images)
|
|
133
|
+
return 'vision';
|
|
134
|
+
if (/tools?\b|tool[_ -]?(call|choice|use)|function[_ -]?call/.test(text))
|
|
135
|
+
return 'tools';
|
|
136
|
+
return 'text';
|
|
137
|
+
}
|
|
138
|
+
function observeModelResponses(provider, record) {
|
|
139
|
+
const report = (updates) => { try {
|
|
140
|
+
record(updates);
|
|
141
|
+
}
|
|
142
|
+
catch { /* observation only */ } };
|
|
143
|
+
const chat = provider.chat.bind(provider);
|
|
144
|
+
provider.chat = async (...args) => {
|
|
145
|
+
const images = hasImage(args[1]);
|
|
146
|
+
try {
|
|
147
|
+
const response = await chat(...args);
|
|
148
|
+
if (response.trim())
|
|
149
|
+
report({ text: true, ...(images ? { vision: true } : {}) });
|
|
150
|
+
else
|
|
151
|
+
report({ text: false });
|
|
152
|
+
return response;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
if (!args[5]?.aborted && error?.name !== 'AbortError')
|
|
156
|
+
report({ [failedFacet(error, images)]: false });
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const stream = provider.chatStreamWithTools.bind(provider);
|
|
161
|
+
provider.chatStreamWithTools = async function* (...args) {
|
|
162
|
+
const images = hasImage(args[1]);
|
|
163
|
+
let meaningful = false, tools = false, failed = false;
|
|
164
|
+
try {
|
|
165
|
+
for await (const token of stream(...args)) {
|
|
166
|
+
if (token.providerError === true) {
|
|
167
|
+
failed = true;
|
|
168
|
+
report({ [failedFacet(token.text, images)]: false });
|
|
169
|
+
}
|
|
170
|
+
if (token.type === 'text' && token.text?.trim())
|
|
171
|
+
meaningful = true;
|
|
172
|
+
if (token.type === 'tool_call') {
|
|
173
|
+
meaningful = true;
|
|
174
|
+
tools = true;
|
|
175
|
+
}
|
|
176
|
+
yield token;
|
|
177
|
+
}
|
|
178
|
+
if (!failed && !args[6]?.aborted) {
|
|
179
|
+
if (meaningful)
|
|
180
|
+
report({ text: true, ...(images ? { vision: true } : {}), ...(tools || args[5].length ? { tools: true } : {}) });
|
|
181
|
+
else
|
|
182
|
+
report({ text: false });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
if (!failed && !args[6]?.aborted && error?.name !== 'AbortError')
|
|
187
|
+
report({ [failedFacet(error, images)]: false });
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
return provider;
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=modelResponseHealth.js.map
|
package/dist/core/types.d.ts
CHANGED
|
@@ -46,6 +46,8 @@ export interface ProviderUsageReported {
|
|
|
46
46
|
cacheWrite: boolean;
|
|
47
47
|
}
|
|
48
48
|
export interface StreamToken {
|
|
49
|
+
/** Transport-originated failure, distinct from model-generated prose. */
|
|
50
|
+
providerError?: boolean;
|
|
49
51
|
type: 'text' | 'tool_call' | 'status' | 'usage';
|
|
50
52
|
text: string;
|
|
51
53
|
toolCall?: {
|
package/dist/llm/provider.js
CHANGED
|
@@ -1085,7 +1085,7 @@ class LLMProvider {
|
|
|
1085
1085
|
yield* this.adapterResponsesBridge(model, messages, systemPrompt, temperature, maxTokens, tools, signal, downgradeTier);
|
|
1086
1086
|
return;
|
|
1087
1087
|
}
|
|
1088
|
-
yield { type: 'text', text: event.error };
|
|
1088
|
+
yield { type: 'text', text: event.error, providerError: true };
|
|
1089
1089
|
return;
|
|
1090
1090
|
}
|
|
1091
1091
|
}
|
|
@@ -1141,7 +1141,7 @@ class LLMProvider {
|
|
|
1141
1141
|
continue;
|
|
1142
1142
|
}
|
|
1143
1143
|
if (event.type === 'response.failed') {
|
|
1144
|
-
yield { type: 'text', text: event.error };
|
|
1144
|
+
yield { type: 'text', text: event.error, providerError: true };
|
|
1145
1145
|
return;
|
|
1146
1146
|
}
|
|
1147
1147
|
}
|
|
@@ -1363,7 +1363,7 @@ class LLMProvider {
|
|
|
1363
1363
|
yield { type: 'usage', text: '', usage: this.toStreamUsage(event.usage) };
|
|
1364
1364
|
}
|
|
1365
1365
|
else if (event.type === 'response.failed') {
|
|
1366
|
-
yield { type: 'text', text: event.error };
|
|
1366
|
+
yield { type: 'text', text: event.error, providerError: true };
|
|
1367
1367
|
return;
|
|
1368
1368
|
}
|
|
1369
1369
|
}
|
|
@@ -1411,7 +1411,7 @@ class LLMProvider {
|
|
|
1411
1411
|
return;
|
|
1412
1412
|
}
|
|
1413
1413
|
if (completeCalls.some(block => !block.id || !block.name || !block.input || typeof block.input !== 'object' || Array.isArray(block.input))) {
|
|
1414
|
-
yield { type: 'text', text: '[LLM Error] Provider returned incomplete or invalid tool-call arguments.' };
|
|
1414
|
+
yield { type: 'text', text: '[LLM Error] Provider returned incomplete or invalid tool-call arguments.', providerError: true };
|
|
1415
1415
|
return;
|
|
1416
1416
|
}
|
|
1417
1417
|
for (const block of completeCalls) {
|
package/dist/ui/index.html
CHANGED
|
@@ -5414,17 +5414,20 @@ html.newmark-memory-overview-viewer body {
|
|
|
5414
5414
|
}
|
|
5415
5415
|
|
|
5416
5416
|
.model-chip {
|
|
5417
|
-
display:
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
}
|
|
5417
|
+
display:flex; align-items:center; gap:12px; box-sizing:border-box;
|
|
5418
|
+
width:100%; min-width:0; margin:6px 0; padding:10px 12px;
|
|
5419
|
+
border-radius:var(--radius-sm); background:rgba(91,120,255,0.1);
|
|
5420
|
+
border:1px solid rgba(91,120,255,0.2); font-size:11px; color:var(--text-accent);
|
|
5421
|
+
}
|
|
5422
|
+
.model-chip-info { flex:1; min-width:0; text-align:left; overflow-wrap:anywhere; }
|
|
5423
|
+
.model-chip-actions { display:flex; flex:0 0 auto; align-items:center; gap:6px; margin-left:auto; }
|
|
5424
|
+
.model-response-warning { color:var(--text-dim); font-size:inherit; margin-left:4px; font-weight:400; }
|
|
5425
|
+
.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; }
|
|
5426
|
+
.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); }
|
|
5427
|
+
.model-enable-switch[aria-checked="true"] { background:rgba(91,120,255,.35); }
|
|
5428
|
+
.model-enable-switch[aria-checked="true"] .model-enable-thumb { transform:translateX(16px); }
|
|
5429
|
+
.model-enable-switch:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
|
|
5430
|
+
.model-enable-switch:disabled { opacity:.6; }
|
|
5428
5431
|
|
|
5429
5432
|
.model-chip .remove {
|
|
5430
5433
|
cursor: pointer;
|
|
@@ -6273,7 +6276,7 @@ input[type="checkbox"].liquid-switch-dragging::before {
|
|
|
6273
6276
|
transition: opacity 105ms var(--ease-out-expo), transform 105ms var(--ease-out-expo);
|
|
6274
6277
|
}
|
|
6275
6278
|
.liquid-switch-float.visible { opacity: 1; transform: translate(-50%, -50%) scale(1.22); }
|
|
6276
|
-
.liquid-switch-float.landing { opacity:
|
|
6279
|
+
.liquid-switch-float.landing { opacity: 1; transform: translate(-50%, -50%) scale(.8181818); }
|
|
6277
6280
|
|
|
6278
6281
|
/* Center composer and terminal stay solid. Only their left/right edge catches
|
|
6279
6282
|
light so the glass sidebars remain the visual frame. */
|
|
@@ -7613,6 +7616,8 @@ var NEWMARK_I18N = {
|
|
|
7613
7616
|
'model.available': 'available',
|
|
7614
7617
|
'model.degraded': 'available (limited capabilities)',
|
|
7615
7618
|
'model.unavailable': 'unavailable',
|
|
7619
|
+
'model.responseAnomaly': '(response abnormal)',
|
|
7620
|
+
'model.enabled': 'Enable model',
|
|
7616
7621
|
'model.no': 'no',
|
|
7617
7622
|
'model.yes': 'yes',
|
|
7618
7623
|
'model.notChecked': 'not checked',
|
|
@@ -8354,6 +8359,8 @@ var NEWMARK_I18N = {
|
|
|
8354
8359
|
'model.available': '可用',
|
|
8355
8360
|
'model.degraded': '可用(部分能力受限)',
|
|
8356
8361
|
'model.unavailable': '不可用',
|
|
8362
|
+
'model.responseAnomaly': '(响应异常)',
|
|
8363
|
+
'model.enabled': '启用模型',
|
|
8357
8364
|
'model.no': '否',
|
|
8358
8365
|
'model.yes': '是',
|
|
8359
8366
|
'model.notChecked': '未检测',
|
|
@@ -16601,82 +16608,99 @@ requestAnimationFrame(function() {
|
|
|
16601
16608
|
wireDirectPopupOptionPress();
|
|
16602
16609
|
});
|
|
16603
16610
|
|
|
16604
|
-
//
|
|
16605
|
-
//
|
|
16611
|
+
// The body-level glass thumb survives settings rerenders and lands only after
|
|
16612
|
+
// reaching the released endpoint. Native keyboard clicks keep checkbox semantics.
|
|
16606
16613
|
document.addEventListener('pointerdown', function(event) {
|
|
16607
16614
|
var input = event.target && event.target.closest ? event.target.closest('input[type="checkbox"]') : null;
|
|
16608
|
-
if (!input || input.disabled || event.button !== 0) return;
|
|
16615
|
+
if (!input || input.disabled || event.button !== 0 || input._liquidSettling) return;
|
|
16609
16616
|
event.preventDefault();
|
|
16610
|
-
var rect = input.getBoundingClientRect();
|
|
16611
|
-
var
|
|
16612
|
-
var
|
|
16613
|
-
var
|
|
16614
|
-
var startY = event.clientY;
|
|
16615
|
-
var dragging = false;
|
|
16616
|
-
var canceledToScroll = false;
|
|
16617
|
+
var rect = input.getBoundingClientRect(), pointer = event.pointerId;
|
|
16618
|
+
var initial = input.checked, fraction = initial ? 1 : 0;
|
|
16619
|
+
var startX = event.clientX, startY = event.clientY;
|
|
16620
|
+
var dragging = false, canceledToScroll = false, finished = false;
|
|
16617
16621
|
var dragThreshold = event.pointerType === 'mouse' ? 4 : 8;
|
|
16622
|
+
var float = document.createElement('div');
|
|
16623
|
+
float.className = 'liquid-switch-float';
|
|
16624
|
+
float.setAttribute('aria-hidden', 'true');
|
|
16625
|
+
document.body.appendChild(float);
|
|
16626
|
+
float.style.width = '22px'; float.style.height = '22px';
|
|
16627
|
+
attachKyantLiquidRenderer(float, liquidBackdropCachedFrame);
|
|
16628
|
+
captureLiquidBackdropFrame().then(function(frame) {
|
|
16629
|
+
if (float.isConnected && frame) attachKyantLiquidRenderer(float, frame);
|
|
16630
|
+
});
|
|
16618
16631
|
input.dataset.liquidSuppressClick = 'true';
|
|
16619
16632
|
clearTimeout(input._liquidSuppressClickTimer);
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
|
|
16624
|
-
|
|
16625
|
-
|
|
16626
|
-
|
|
16627
|
-
input.style.setProperty('--liquid-switch-x', (18 * fraction) + 'px');
|
|
16628
|
-
input.style.setProperty('--liquid-switch-progress', (100 * fraction) + '%');
|
|
16633
|
+
function paint(value, lift) {
|
|
16634
|
+
fraction = value;
|
|
16635
|
+
input.style.setProperty('--liquid-switch-x', (18 * value) + 'px');
|
|
16636
|
+
input.style.setProperty('--liquid-switch-progress', (100 * value) + '%');
|
|
16637
|
+
float.style.left = (rect.left + 12 + 18 * value) + 'px';
|
|
16638
|
+
float.style.top = (rect.top + rect.height / 2 - (lift ? 2 : 0)) + 'px';
|
|
16639
|
+
if (float._renderKyantGlass) float._renderKyantGlass({left:rect.left + 1 + 18 * value,top:rect.top + rect.height / 2 - 11 - (lift ? 2 : 0),width:22,height:22},true);
|
|
16629
16640
|
}
|
|
16630
16641
|
function finish(commit) {
|
|
16642
|
+
if (finished) return;
|
|
16643
|
+
finished = true;
|
|
16631
16644
|
input.removeEventListener('pointermove', move);
|
|
16632
16645
|
input.removeEventListener('pointerup', up);
|
|
16633
16646
|
input.removeEventListener('pointercancel', cancel);
|
|
16634
|
-
input.
|
|
16635
|
-
input.style.removeProperty('--liquid-switch-x');
|
|
16636
|
-
input.style.removeProperty('--liquid-switch-progress');
|
|
16647
|
+
input.removeEventListener('lostpointercapture', cancel);
|
|
16637
16648
|
try { input.releasePointerCapture(pointer); } catch (_) {}
|
|
16638
|
-
|
|
16639
|
-
var
|
|
16640
|
-
|
|
16641
|
-
|
|
16642
|
-
|
|
16643
|
-
|
|
16644
|
-
|
|
16649
|
+
var next = commit && !canceledToScroll ? (dragging ? fraction >= 0.5 : !initial) : initial;
|
|
16650
|
+
var from = fraction, to = next ? 1 : 0, started;
|
|
16651
|
+
input._liquidSettling = true;
|
|
16652
|
+
// Suppress the synthetic click even after a long hold; never time out mid-drag.
|
|
16653
|
+
input._liquidSuppressClickTimer = setTimeout(function() { delete input.dataset.liquidSuppressClick; }, 700);
|
|
16654
|
+
function frame(now) {
|
|
16655
|
+
if (started === undefined) started = now;
|
|
16656
|
+
var progress = Math.min(1, (now - started) / 180);
|
|
16657
|
+
paint(from + (to - from) * (1 - Math.pow(1 - progress, 3)), true);
|
|
16658
|
+
if (progress < 1) { requestAnimationFrame(frame); return; }
|
|
16659
|
+
// The glass remains present during landing, at the destination anchor.
|
|
16660
|
+
float.classList.add('landing');
|
|
16661
|
+
paint(to, false);
|
|
16662
|
+
setTimeout(function() {
|
|
16663
|
+
input.checked = next;
|
|
16664
|
+
input.classList.remove('liquid-switch-dragging');
|
|
16665
|
+
input.style.removeProperty('--liquid-switch-x');
|
|
16666
|
+
input.style.removeProperty('--liquid-switch-progress');
|
|
16667
|
+
delete input._liquidSettling;
|
|
16668
|
+
if (float._releaseKyantGlass) float._releaseKyantGlass();
|
|
16669
|
+
float.remove();
|
|
16670
|
+
if (next !== initial) {
|
|
16671
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
16672
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
16673
|
+
}
|
|
16674
|
+
}, 120);
|
|
16645
16675
|
}
|
|
16676
|
+
requestAnimationFrame(frame);
|
|
16646
16677
|
}
|
|
16647
|
-
function move(
|
|
16648
|
-
if (
|
|
16678
|
+
function move(e) {
|
|
16679
|
+
if (e.pointerId !== pointer || canceledToScroll) return;
|
|
16649
16680
|
if (!dragging) {
|
|
16650
|
-
var
|
|
16651
|
-
|
|
16652
|
-
if (Math.
|
|
16653
|
-
if (Math.abs(deltaY) > Math.abs(deltaX)) {
|
|
16654
|
-
canceledToScroll = true;
|
|
16655
|
-
return;
|
|
16656
|
-
}
|
|
16681
|
+
var dx = e.clientX - startX, dy = e.clientY - startY;
|
|
16682
|
+
if (Math.hypot(dx, dy) < dragThreshold) return;
|
|
16683
|
+
if (Math.abs(dy) > Math.abs(dx)) { canceledToScroll = true; finish(false); return; }
|
|
16657
16684
|
dragging = true;
|
|
16658
16685
|
}
|
|
16659
|
-
|
|
16660
|
-
|
|
16661
|
-
}
|
|
16662
|
-
function up(upEvent) {
|
|
16663
|
-
if (upEvent.pointerId === pointer) finish(true);
|
|
16664
|
-
}
|
|
16665
|
-
function cancel(cancelEvent) {
|
|
16666
|
-
if (cancelEvent.pointerId === pointer) finish(false);
|
|
16686
|
+
e.preventDefault();
|
|
16687
|
+
paint(Math.max(0, Math.min(1, (initial ? 1 : 0) + (e.clientX - startX) / 18)), true);
|
|
16667
16688
|
}
|
|
16689
|
+
function up(e) { if (e.pointerId === pointer) finish(true); }
|
|
16690
|
+
function cancel(e) { if (e.pointerId === pointer) finish(false); }
|
|
16668
16691
|
input.classList.add('liquid-switch-dragging');
|
|
16669
|
-
|
|
16692
|
+
paint(fraction, true);
|
|
16693
|
+
float.getBoundingClientRect();
|
|
16694
|
+
float.classList.add('visible');
|
|
16695
|
+
try { input.setPointerCapture(pointer); } catch (_) { finish(false); return; }
|
|
16670
16696
|
input.addEventListener('pointermove', move);
|
|
16671
16697
|
input.addEventListener('pointerup', up);
|
|
16672
16698
|
input.addEventListener('pointercancel', cancel);
|
|
16699
|
+
input.addEventListener('lostpointercapture', cancel);
|
|
16673
16700
|
}, true);
|
|
16674
16701
|
document.addEventListener('click', function(event) {
|
|
16675
16702
|
var input = event.target && event.target.closest ? event.target.closest('input[type="checkbox"]') : null;
|
|
16676
|
-
if (!input || input.dataset.liquidSuppressClick !== 'true') return;
|
|
16677
|
-
clearTimeout(input._liquidSuppressClickTimer);
|
|
16678
|
-
delete input.dataset.liquidSuppressClick;
|
|
16679
|
-
delete input._liquidSuppressClickTimer;
|
|
16703
|
+
if (!input || (input.dataset.liquidSuppressClick !== 'true' && !input._liquidSettling)) return;
|
|
16680
16704
|
event.preventDefault();
|
|
16681
16705
|
event.stopImmediatePropagation();
|
|
16682
16706
|
}, true);
|
|
@@ -16734,24 +16758,29 @@ document.addEventListener('click', function(event) {
|
|
|
16734
16758
|
event.stopImmediatePropagation();
|
|
16735
16759
|
}, true);
|
|
16736
16760
|
|
|
16761
|
+
function modelOptionLabelHtml(option) {
|
|
16762
|
+
return esc(option.textContent || option.value) + (option.dataset.responseAnomaly === 'true'
|
|
16763
|
+
? '<span class="model-response-warning">' + esc(t('model.responseAnomaly')) + '</span>' : '');
|
|
16764
|
+
}
|
|
16765
|
+
|
|
16737
16766
|
window.syncModelSelectSurface = function() {
|
|
16738
16767
|
var sel = els['model-select'] || document.getElementById('model-select');
|
|
16739
16768
|
var label = document.getElementById('model-select-label');
|
|
16740
16769
|
var menu = document.getElementById('model-select-menu');
|
|
16741
16770
|
if (!sel || !label || !menu) return;
|
|
16742
16771
|
var selected = sel.options[sel.selectedIndex] || sel.options[0];
|
|
16743
|
-
label.
|
|
16772
|
+
label.innerHTML = selected ? modelOptionLabelHtml(selected) : esc(state.modelLabel || state.model || t('language.auto'));
|
|
16744
16773
|
var html = '';
|
|
16745
16774
|
Array.from(sel.children).forEach(function(child) {
|
|
16746
16775
|
if (child.tagName === 'OPTGROUP') {
|
|
16747
16776
|
html += '<div class="model-select-menu-group" role="presentation">' + esc(child.label || '') + '</div>';
|
|
16748
16777
|
Array.from(child.children).forEach(function(option) {
|
|
16749
|
-
html += '<button type="button" class="model-select-menu-option' + (option.value === sel.value ? ' selected' : '') + '" role="option" aria-selected="' + (option.value === sel.value ? 'true' : 'false') + '" data-value="' + escAttr(option.value) + '" onclick="window.selectModelFromMenu(this.getAttribute(\'data-value\'))">' +
|
|
16778
|
+
html += '<button type="button" class="model-select-menu-option' + (option.value === sel.value ? ' selected' : '') + '" role="option" aria-selected="' + (option.value === sel.value ? 'true' : 'false') + '" data-value="' + escAttr(option.value) + '" onclick="window.selectModelFromMenu(this.getAttribute(\'data-value\'))">' + modelOptionLabelHtml(option) + '</button>';
|
|
16750
16779
|
});
|
|
16751
16780
|
return;
|
|
16752
16781
|
}
|
|
16753
16782
|
if (child.tagName === 'OPTION') {
|
|
16754
|
-
html += '<button type="button" class="model-select-menu-option' + (child.value === sel.value ? ' selected' : '') + '" role="option" aria-selected="' + (child.value === sel.value ? 'true' : 'false') + '" data-value="' + escAttr(child.value) + '" onclick="window.selectModelFromMenu(this.getAttribute(\'data-value\'))">' +
|
|
16783
|
+
html += '<button type="button" class="model-select-menu-option' + (child.value === sel.value ? ' selected' : '') + '" role="option" aria-selected="' + (child.value === sel.value ? 'true' : 'false') + '" data-value="' + escAttr(child.value) + '" onclick="window.selectModelFromMenu(this.getAttribute(\'data-value\'))">' + modelOptionLabelHtml(child) + '</button>';
|
|
16755
16784
|
}
|
|
16756
16785
|
});
|
|
16757
16786
|
// Rebuilding the option list must not destroy the carrier block. A pending
|
|
@@ -17042,13 +17071,13 @@ window.refreshModelSelect = function(options) {
|
|
|
17042
17071
|
for (var midx = 0; midx < p.models.length; midx++) {
|
|
17043
17072
|
var modelEntry = p.models[midx];
|
|
17044
17073
|
var effectiveStatus = effectiveUiModelStatus(modelEntry);
|
|
17045
|
-
if (
|
|
17074
|
+
if (modelEntry && modelEntry.enabled === false) continue;
|
|
17046
17075
|
var modelName = typeof modelEntry === 'string' ? modelEntry : (modelEntry.name || '');
|
|
17047
17076
|
var modelLabel = typeof modelEntry === 'string' ? modelEntry : (modelEntry.display || modelEntry.name || '');
|
|
17048
17077
|
var providerId = String(p.id || p.name || '');
|
|
17049
17078
|
var modelValue = providerId && modelName ? 'deployment:' + encodeURIComponent(providerId) + ':' + encodeURIComponent(modelName) : modelName;
|
|
17050
17079
|
if (!firstConcreteModel && modelValue) firstConcreteModel = modelValue;
|
|
17051
|
-
providerOpts += '<option value="' + esc(modelValue) + '">' + esc(p.name + ' / ' + modelLabel) + '</option>';
|
|
17080
|
+
providerOpts += '<option data-response-anomaly="' + (modelHasResponseAnomaly(modelEntry) ? 'true' : 'false') + '" value="' + esc(modelValue) + '">' + esc(p.name + ' / ' + modelLabel) + '</option>';
|
|
17052
17081
|
}
|
|
17053
17082
|
if (providerOpts) modelOpts += '<optgroup label="' + esc(p.name) + '">' + providerOpts + '</optgroup>';
|
|
17054
17083
|
}
|
|
@@ -18208,6 +18237,7 @@ window.sendMessage = async function(modeOverride, queuedText, opts) {
|
|
|
18208
18237
|
var completedWorkRequest = captureConversationWorkSnapshotRequest(lockedTarget);
|
|
18209
18238
|
api.getState(lockedTarget).then(function(s) {
|
|
18210
18239
|
if (!isCurrentConversationWorkSnapshotRequest(completedWorkRequest)) return;
|
|
18240
|
+
if (s && s.providers) { state.providers = s.providers; window.refreshModelSelect(); }
|
|
18211
18241
|
if (s && s.runtimeKey) registerRuntimeKey(lockedTarget, s.runtimeKey);
|
|
18212
18242
|
var stillActiveAfterRefresh = isActiveConversationTarget(lockedTarget) && isViewingRuntimeConversationBranch(lockedTarget);
|
|
18213
18243
|
if (stillActiveAfterRefresh) applyReturnedGoalState(s);
|
|
@@ -19907,8 +19937,18 @@ function formatModelStatus(status) {
|
|
|
19907
19937
|
return status || t('model.notChecked');
|
|
19908
19938
|
}
|
|
19909
19939
|
|
|
19940
|
+
function modelHasResponseAnomaly(modelEntry) {
|
|
19941
|
+
if (!modelEntry || typeof modelEntry === 'string') return false;
|
|
19942
|
+
var health = modelEntry.response_health || {};
|
|
19943
|
+
if (Object.keys(health).some(function(key) { return health[key].ok === false; })) return true;
|
|
19944
|
+
if (health.text && health.text.ok) return false;
|
|
19945
|
+
return ['unavailable', 'auth_error', 'invalid_config', 'rate_limited', 'failed', 'error'].indexOf(effectiveUiModelStatus(modelEntry)) >= 0;
|
|
19946
|
+
}
|
|
19947
|
+
|
|
19910
19948
|
function effectiveUiModelStatus(modelEntry) {
|
|
19911
19949
|
if (!modelEntry || typeof modelEntry === 'string') return 'unvalidated';
|
|
19950
|
+
var responseHealth = modelEntry.response_health || {};
|
|
19951
|
+
if (responseHealth.text) return responseHealth.text.ok ? 'available' : 'unavailable';
|
|
19912
19952
|
var rawValidation = String((modelEntry.validation && modelEntry.validation.status) || '').toLowerCase();
|
|
19913
19953
|
var rawEvaluation = String((modelEntry.evaluation && modelEntry.evaluation.status) || 'unvalidated').toLowerCase();
|
|
19914
19954
|
var capabilities = modelEntry.validation && modelEntry.validation.capabilities || {};
|
|
@@ -20093,18 +20133,22 @@ function renderModelSettings() {
|
|
|
20093
20133
|
if (typeof modelEntry !== 'string') {
|
|
20094
20134
|
var tierMapCount = modelEntry.thinking_tier_map && typeof modelEntry.thinking_tier_map === 'object' ? Object.keys(modelEntry.thinking_tier_map).length : 0;
|
|
20095
20135
|
details = '<span class="model-eval-line">ctx ' + esc(modelEntry.max_tokens || '') +
|
|
20096
|
-
' | ' + esc(t('model.vision')) + ' ' + (modelEntry.vision ? t('common.on') : t('common.off')) +
|
|
20136
|
+
' | ' + esc(t('model.vision')) + ' ' + ((modelEntry.response_health && modelEntry.response_health.vision ? modelEntry.response_health.vision.ok : modelEntry.vision) ? t('common.on') : t('common.off')) +
|
|
20097
20137
|
' | ' + esc(t('model.thinking')) + ' ' + (modelEntry.thinking ? t('common.on') : t('common.off')) +
|
|
20098
20138
|
(tierMapCount ? ' | ' + esc(t('model.thinkingTierMap')) + ' ' + tierMapCount : '') +
|
|
20099
20139
|
' | ' + esc(t('model.cost')) + ' ' + esc(evaluation ? evaluation.cost_rating : '-') +
|
|
20100
20140
|
' | ' + esc(t('model.performanceShort')) + ' ' + esc(modelEntry.capability_rating || '-') +
|
|
20101
20141
|
' | ' + esc(t('model.speed')) + ' ' + esc(modelEntry.speed_rating || '-') + '</span>';
|
|
20102
20142
|
}
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
'<
|
|
20107
|
-
'<span class="
|
|
20143
|
+
var modelEnabled = typeof modelEntry === 'string' || modelEntry.enabled !== false;
|
|
20144
|
+
var anomaly = modelHasResponseAnomaly(modelEntry);
|
|
20145
|
+
modelsHtml += '<div class="model-chip' + (modelEnabled ? '' : ' is-user-disabled') + '" data-model-name="' + escAttr(modelName) + '">' +
|
|
20146
|
+
'<div class="model-chip-info"><span title="' + escAttr(modelName) + '">' + esc(modelLabel) + '</span>' +
|
|
20147
|
+
(anomaly ? '<span class="model-response-warning">' + esc(t('model.responseAnomaly')) + '</span>' : '<span class="' + statusClass + '"> ' + esc(status) + '</span>') + details + '</div>' +
|
|
20148
|
+
'<div class="model-chip-actions">' +
|
|
20149
|
+
'<input type="checkbox" class="model-enable-switch" role="switch" ' + (modelEnabled ? 'checked ' : '') + 'aria-label="' + escAttr(t('model.enabled') + ': ' + modelLabel) + '" onchange="window.toggleModelEnabled(' + i + ',' + j + ',this)">' +
|
|
20150
|
+
'<button type="button" class="provider-icon-btn" onclick="window.editModel(' + i + ',' + j + ')" aria-label="' + escAttr(t('common.edit')) + '">' + iconSvg('square-pen', t('common.edit'), 'tiny') + '</button>' +
|
|
20151
|
+
'<button type="button" class="provider-icon-btn danger" onclick="window.removeModel(' + i + ',' + j + ')" aria-label="' + escAttr(t('common.delete')) + '">' + iconSvg('trash-2', t('common.delete'), 'tiny') + '</button></div></div>';
|
|
20108
20152
|
}
|
|
20109
20153
|
var providerEnabled = p.enabled !== false;
|
|
20110
20154
|
var providerToggleLabel = providerEnabled ? t('model.disableProvider') : t('model.enableProvider');
|
|
@@ -21399,6 +21443,26 @@ window.deleteArchive = function(idx, scope) {
|
|
|
21399
21443
|
});
|
|
21400
21444
|
};
|
|
21401
21445
|
|
|
21446
|
+
window.toggleModelEnabled = async function(provIdx, modelIdx, button) {
|
|
21447
|
+
var provider = state.providers[provIdx];
|
|
21448
|
+
if (!provider || !provider.models || !provider.models[modelIdx]) return;
|
|
21449
|
+
var next = JSON.parse(JSON.stringify(state.providers));
|
|
21450
|
+
var previous = provider.models[modelIdx];
|
|
21451
|
+
next[provIdx].models[modelIdx] = typeof previous === 'string' ? { name: previous, enabled: false }
|
|
21452
|
+
: Object.assign({}, previous, { enabled: previous.enabled === false });
|
|
21453
|
+
if (button) button.disabled = true;
|
|
21454
|
+
try {
|
|
21455
|
+
var result = await api.saveConfig({ providers: next });
|
|
21456
|
+
if (result === false || (result && result.error)) throw new Error(result.error || 'Model update failed');
|
|
21457
|
+
state.providers = next;
|
|
21458
|
+
window.refreshModelSelect();
|
|
21459
|
+
window.openSettings('models');
|
|
21460
|
+
} catch (error) {
|
|
21461
|
+
if (button) { button.disabled = false; button.checked = typeof previous === 'string' || previous.enabled !== false; }
|
|
21462
|
+
showUiNotice(String(error.message || error), 'error', 'model-enabled');
|
|
21463
|
+
}
|
|
21464
|
+
};
|
|
21465
|
+
|
|
21402
21466
|
window.removeModel = function(provIdx, modelIdx) {
|
|
21403
21467
|
if (state.providers[provIdx] && state.providers[provIdx].models) {
|
|
21404
21468
|
state.providers[provIdx].models.splice(modelIdx, 1);
|