zames_pro 2.4.1 → 2.4.3
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/agent-loop.js +6 -0
- package/dist/browser.js +68 -4
- package/dist/input.js +1 -1
- package/dist/spinner.js +1 -1
- package/package.json +1 -1
package/dist/agent-loop.js
CHANGED
|
@@ -64,6 +64,12 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
64
64
|
const rawResponse = await browser.ask(message);
|
|
65
65
|
await reportChat();
|
|
66
66
|
transcript?.log('assistant_raw', { response: rawResponse });
|
|
67
|
+
// Пользователь прервал генерацию (Esc/Ctrl+C): не считаем это ответом
|
|
68
|
+
// модели и не запускаем инструменты — корректно завершаем задачу.
|
|
69
|
+
if (/^\s*\(прервано пользователем\)\s*$/.test(rawResponse)) {
|
|
70
|
+
transcript?.log('user_aborted');
|
|
71
|
+
return rawResponse;
|
|
72
|
+
}
|
|
67
73
|
const parsed = parseToolCall(rawResponse);
|
|
68
74
|
if (parsed) {
|
|
69
75
|
const thought = extractPreToolText(rawResponse);
|
package/dist/browser.js
CHANGED
|
@@ -316,20 +316,78 @@ export class DeepSeekBrowser {
|
|
|
316
316
|
return '';
|
|
317
317
|
return raw;
|
|
318
318
|
}
|
|
319
|
+
// Найти кнопку Stop в интерфейсе DeepSeek. Полагаться только на класс
|
|
320
|
+
// нельзя: во время генерации кнопка отправки (та же circle-кнопка)
|
|
321
|
+
// меняет иконку на «квадрат» (stop), сохраняя классы. Поэтому:
|
|
322
|
+
// 1) проверяем явные селекторы (aria-label/текст Stop);
|
|
323
|
+
// 2) ищем primary circle-кнопку и смотрим на её иконку: rect/квадрат =
|
|
324
|
+
// stop, path/стрелка = send.
|
|
325
|
+
async _stopButtonVisible() {
|
|
326
|
+
const explicit = await this._findVisible(STOP_SELECTORS, 250);
|
|
327
|
+
if (explicit)
|
|
328
|
+
return true;
|
|
329
|
+
return await this.page
|
|
330
|
+
.evaluate(() => {
|
|
331
|
+
const btns = Array.from(document.querySelectorAll('div[role="button"], button'));
|
|
332
|
+
for (const b of btns) {
|
|
333
|
+
const cls = (b.className || '').toString();
|
|
334
|
+
if (!/ds-button--(circle|primary|filled)/i.test(cls))
|
|
335
|
+
continue;
|
|
336
|
+
const label = ((b.getAttribute('aria-label') || '') +
|
|
337
|
+
' ' +
|
|
338
|
+
(b.getAttribute('title') || '') +
|
|
339
|
+
' ' +
|
|
340
|
+
(b.textContent || '')).toLowerCase();
|
|
341
|
+
if (/stop|останов/.test(label))
|
|
342
|
+
return true;
|
|
343
|
+
// Иконка-квадрат = кнопка Stop; стрелка (path без rect) = отправка.
|
|
344
|
+
const svg = b.querySelector('svg');
|
|
345
|
+
if (svg && svg.querySelector('rect'))
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
})
|
|
350
|
+
.catch(() => false);
|
|
351
|
+
}
|
|
319
352
|
async _isGenerating() {
|
|
320
|
-
|
|
321
|
-
return !!stop;
|
|
353
|
+
return await this._stopButtonVisible();
|
|
322
354
|
}
|
|
323
355
|
async stopGeneration() {
|
|
324
356
|
this._abort = true;
|
|
357
|
+
// 1) Пробуем явные селекторы Stop.
|
|
325
358
|
const btn = await this._findVisible(STOP_SELECTORS, 500);
|
|
326
359
|
if (btn) {
|
|
327
360
|
try {
|
|
328
|
-
await btn.click({ timeout:
|
|
361
|
+
await btn.click({ timeout: 1500 });
|
|
329
362
|
return true;
|
|
330
363
|
}
|
|
331
364
|
catch { }
|
|
332
365
|
}
|
|
366
|
+
// 2) Кликаем по primary circle-кнопке, если у неё иконка-квадрат.
|
|
367
|
+
const clicked = await this.page
|
|
368
|
+
.evaluate(() => {
|
|
369
|
+
const btns = Array.from(document.querySelectorAll('div[role="button"], button'));
|
|
370
|
+
for (const b of btns) {
|
|
371
|
+
const cls = (b.className || '').toString();
|
|
372
|
+
if (!/ds-button--(circle|primary|filled)/i.test(cls))
|
|
373
|
+
continue;
|
|
374
|
+
const svg = b.querySelector('svg');
|
|
375
|
+
if (svg && svg.querySelector('rect')) {
|
|
376
|
+
b.click();
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return false;
|
|
381
|
+
})
|
|
382
|
+
.catch(() => false);
|
|
383
|
+
if (clicked)
|
|
384
|
+
return true;
|
|
385
|
+
// 3) Крайний случай — нажать Esc в поле ввода (иногда останавливает).
|
|
386
|
+
try {
|
|
387
|
+
await this.page.keyboard.press('Escape');
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
catch { }
|
|
333
391
|
return false;
|
|
334
392
|
}
|
|
335
393
|
async ask(prompt, { timeout = this.answerTimeoutMs } = {}) {
|
|
@@ -430,6 +488,10 @@ export class DeepSeekBrowser {
|
|
|
430
488
|
await this.page.waitForTimeout(gap);
|
|
431
489
|
}
|
|
432
490
|
async _askOnce(prompt, { timeout }) {
|
|
491
|
+
// Сбрасываем флаг прерывания ТОЛЬКО в самом начале отправки. Раньше он
|
|
492
|
+
// сбрасывался перед циклом ожидания — и Esc/Ctrl+C, нажатые во время
|
|
493
|
+
// старта генерации, терялись, запрос не останавливался.
|
|
494
|
+
this._abort = false;
|
|
433
495
|
const input = await this._findVisible(INPUT_SELECTORS, 10_000);
|
|
434
496
|
if (!input) {
|
|
435
497
|
throw new Error('Не найдено поле ввода. Запустите /debug-dom и поправьте INPUT_SELECTORS.');
|
|
@@ -468,6 +530,9 @@ export class DeepSeekBrowser {
|
|
|
468
530
|
.catch(() => 0);
|
|
469
531
|
let started = false;
|
|
470
532
|
while (Date.now() < startDeadline) {
|
|
533
|
+
// Прервали (Esc/Ctrl+C) ещё до старта генерации — выходим сразу.
|
|
534
|
+
if (this._abort)
|
|
535
|
+
return '(прервано пользователем)';
|
|
471
536
|
const pageText = await this._readPageText();
|
|
472
537
|
if (isRateLimitText(pageText)) {
|
|
473
538
|
throw new RateLimitError(pageText.slice(0, 300));
|
|
@@ -499,7 +564,6 @@ export class DeepSeekBrowser {
|
|
|
499
564
|
const deadline = Date.now() + timeout;
|
|
500
565
|
let last = '';
|
|
501
566
|
let stable = 0;
|
|
502
|
-
this._abort = false;
|
|
503
567
|
while (Date.now() < deadline) {
|
|
504
568
|
if (this._abort) {
|
|
505
569
|
return last || '(прервано пользователем)';
|
package/dist/input.js
CHANGED
|
@@ -29,7 +29,7 @@ const PASTE_END = ESC + '[201~';
|
|
|
29
29
|
// Ширину выравниваем по максимуму (3), чтобы подсказка не смещалась.
|
|
30
30
|
const DOTS = ['', '.', '..', '...'];
|
|
31
31
|
const DOTS_PAD = ' ';
|
|
32
|
-
const HINT = theme.dim(' ·
|
|
32
|
+
const HINT = theme.dim(' · Esc — стоп');
|
|
33
33
|
function safeJson(v) {
|
|
34
34
|
try {
|
|
35
35
|
return JSON.stringify(v);
|
package/dist/spinner.js
CHANGED
|
@@ -66,7 +66,7 @@ export function createSpinner() {
|
|
|
66
66
|
};
|
|
67
67
|
// Подсказка в строке статуса: во время работы агента терминал живой,
|
|
68
68
|
// можно печатать следующее сообщение. Без неё это неочевидно.
|
|
69
|
-
const HINT = theme.dim(' ·
|
|
69
|
+
const HINT = theme.dim(' · Esc — стоп');
|
|
70
70
|
// Запуск анимированного статуса: коричневый текст + «бегущие» точки.
|
|
71
71
|
const startThinking = () => {
|
|
72
72
|
const base = theme.brown(stripEllipsis(randomThinkingPhrase()));
|