zames_pro 2.29.11 → 2.29.13

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.
@@ -4,7 +4,7 @@ import { getGitContext, formatGitContext } from './gitTools.js';
4
4
  import { parseXmlToolCalls } from './xml-toolcall.js';
5
5
  import { translate } from './i18n.js';
6
6
  import { normText } from './browser.js';
7
- export async function runAgentLoop({ browser, tools, task, workdir, maxIterations = 0, freshChat = false, sendSystemPrompt = false, transcript = null, attachments = [], onThinking = () => { }, onSendPause = () => { }, onAssistantThought = () => { }, onToolCall = () => { }, onToolResult = () => { }, onAssistantMessage = () => { }, onChatReady = () => { }, onWarning = () => { }, debugLog = false, locale = 'ru', }) {
7
+ export async function runAgentLoop({ browser, tools, task, workdir, maxIterations = 0, freshChat = false, sendSystemPrompt = false, transcript = null, attachments = [], onThinking = () => { }, onSendPause = () => { }, onNotice = () => { }, onAssistantThought = () => { }, onToolCall = () => { }, onToolResult = () => { }, onAssistantMessage = () => { }, onChatReady = () => { }, onWarning = () => { }, debugLog = false, locale = 'ru', }) {
8
8
  // UI callbacks must NEVER break the agent loop. A rendering error (a huge
9
9
  // tool result, a broken markdown frame, a closed terminal) used to throw
10
10
  // out of the loop right after a tool call — the session looked "stopped
@@ -30,6 +30,11 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
30
30
  // seconds, the UI shows an animated status instead of a frozen line.
31
31
  const safeSendPause = safe(onSendPause);
32
32
  browser.onSendPause = safeSendPause;
33
+ // Service notices (rate limit, server busy, resend) must reach the OPERATOR.
34
+ // While a LineEditor is active a raw console.error is overwritten by the
35
+ // editor's repaint, so the operator only saw the spinner. `onNotice` prints
36
+ // the notice ABOVE the input line.
37
+ browser.onNotice = safe(onNotice);
33
38
  const safeAssistantThought = safe(onAssistantThought);
34
39
  const safeToolCall = safe(onToolCall);
35
40
  const safeToolResult = safe(onToolResult);
package/dist/browser.js CHANGED
@@ -363,6 +363,13 @@ export class DeepSeekBrowser {
363
363
  // remaining seconds. Lets the UI animate the pause status instead of
364
364
  // printing a static line (the dots used to be frozen during the pause).
365
365
  onSendPause;
366
+ // A service notice for the OPERATOR (rate limit, server busy, resend, …).
367
+ // IMPORTANT: these must NOT go through `console.error` directly — while a
368
+ // LineEditor is active it repaints its own status line and the raw stderr
369
+ // write is overwritten, so the operator saw only the spinner and no reason
370
+ // (the "агент завис на спиннере" symptom after "Messages too frequent").
371
+ // When set, the UI prints the notice ABOVE the input line.
372
+ onNotice;
366
373
  // User-Agent sent by the browser. In headless mode the "Headless" marker
367
374
  // must be stripped (DeepSeek's CDN answers 403 to it); computed in the
368
375
  // constructor, overridable by an explicit UA in the options/config.
@@ -411,6 +418,7 @@ export class DeepSeekBrowser {
411
418
  this._netHookInstalled = false;
412
419
  this.onSendStart = null;
413
420
  this.onSendPause = null;
421
+ this.onNotice = null;
414
422
  // An explicit UA (config/options) is respected as-is. Otherwise it stays
415
423
  // empty here and is derived from the real engine UA after launch — see
416
424
  // _fixHeadlessUserAgent (a headless "HeadlessChrome/..." UA gets 403 from
@@ -421,6 +429,19 @@ export class DeepSeekBrowser {
421
429
  _t(key, params) {
422
430
  return translate(this.locale)(key, params);
423
431
  }
432
+ // A service notice for the operator. When a UI hook is wired (LineEditor is
433
+ // active during a task) the notice goes ABOVE the input line so it is not
434
+ // overwritten by the editor's repaint; otherwise it falls back to stderr.
435
+ _notice(text) {
436
+ if (this.onNotice) {
437
+ try {
438
+ this.onNotice(text);
439
+ return;
440
+ }
441
+ catch { }
442
+ }
443
+ console.error(text);
444
+ }
424
445
  async launch() {
425
446
  // NOTE: we deliberately do NOT kill stray chrome processes here. Per the
426
447
  // project rules, an agent must never kill a browser it did not start —
@@ -1379,13 +1400,13 @@ export class DeepSeekBrowser {
1379
1400
  if (e instanceof RateLimitError) {
1380
1401
  rateLimitRetries++;
1381
1402
  if (rateLimitRetries > this.maxRateLimitRetries) {
1382
- console.error(theme.error(this._t('ds.rate_limit_give_up', {
1403
+ this._notice(theme.error(this._t('ds.rate_limit_give_up', {
1383
1404
  attempt: rateLimitRetries,
1384
1405
  min: Math.ceil(this.rateLimitWaitMs / 60000),
1385
1406
  })));
1386
1407
  throw e;
1387
1408
  }
1388
- console.error(theme.warn(this._t('ds.rate_limit_wait', {
1409
+ this._notice(theme.warn(this._t('ds.rate_limit_wait', {
1389
1410
  min: Math.ceil(this.rateLimitWaitMs / 60000),
1390
1411
  attempt: rateLimitRetries,
1391
1412
  max: this.maxRateLimitRetries,
@@ -1408,12 +1429,12 @@ export class DeepSeekBrowser {
1408
1429
  if (e instanceof ServerBusyError) {
1409
1430
  serverBusyRetries++;
1410
1431
  if (serverBusyRetries > this.maxServerBusyRetries) {
1411
- console.error(theme.error(this._t('ds.server_busy_give_up', {
1432
+ this._notice(theme.error(this._t('ds.server_busy_give_up', {
1412
1433
  attempt: serverBusyRetries,
1413
1434
  })));
1414
1435
  throw e;
1415
1436
  }
1416
- console.error(theme.warn(this._t('ds.server_busy_wait', {
1437
+ this._notice(theme.warn(this._t('ds.server_busy_wait', {
1417
1438
  sec: Math.ceil(this.serverBusyWaitMs / 1000),
1418
1439
  attempt: serverBusyRetries,
1419
1440
  max: this.maxServerBusyRetries,
@@ -1431,12 +1452,12 @@ export class DeepSeekBrowser {
1431
1452
  if (e instanceof GenerationIncompleteError) {
1432
1453
  incompleteRetries++;
1433
1454
  if (incompleteRetries > this.maxIncompleteRetries) {
1434
- console.error(theme.error(this._t('ds.incomplete_give_up', {
1455
+ this._notice(theme.error(this._t('ds.incomplete_give_up', {
1435
1456
  attempt: incompleteRetries,
1436
1457
  })));
1437
1458
  throw e;
1438
1459
  }
1439
- console.error(theme.warn(this._t('ds.incomplete_retry', {
1460
+ this._notice(theme.warn(this._t('ds.incomplete_retry', {
1440
1461
  attempt: incompleteRetries,
1441
1462
  max: this.maxIncompleteRetries,
1442
1463
  })));
@@ -1445,25 +1466,24 @@ export class DeepSeekBrowser {
1445
1466
  return '(прервано пользователем)';
1446
1467
  continue;
1447
1468
  }
1448
- console.error('\n' +
1449
- theme.warn(this._t('ds.ask_retry', {
1450
- attempt,
1451
- max: this.askRetries,
1452
- error: e.message,
1453
- })));
1469
+ this._notice(theme.warn(this._t('ds.ask_retry', {
1470
+ attempt,
1471
+ max: this.askRetries,
1472
+ error: e.message,
1473
+ })));
1454
1474
  // Only a genuinely LOST page justifies a restart. "Target page,
1455
1475
  // context or browser has been closed" means the page is gone for good;
1456
1476
  // a bare "browser" mention (e.g. a selector error containing the word)
1457
1477
  // must NOT tear the window down — that was the "a new window opened"
1458
1478
  // surprise during a resumed chat. We re-open the SAME context instead.
1459
1479
  if (/target page, context or browser has been closed|page.*has been closed/i.test(e.message)) {
1460
- console.error(theme.warn(this._t('ds.ask_restart_browser')));
1480
+ this._notice(theme.warn(this._t('ds.ask_restart_browser')));
1461
1481
  try {
1462
1482
  await this.restart();
1463
1483
  await this.waitForLogin();
1464
1484
  }
1465
1485
  catch (re) {
1466
- console.error(theme.warn(this._t('ds.ask_restart_failed', {
1486
+ this._notice(theme.warn(this._t('ds.ask_restart_failed', {
1467
1487
  error: re.message,
1468
1488
  })));
1469
1489
  }
@@ -1551,10 +1571,10 @@ export class DeepSeekBrowser {
1551
1571
  bufPayload.push({ name: f.name, mimeType: f.mime, buffer });
1552
1572
  }
1553
1573
  catch (e) {
1554
- console.error(theme.warn('⚠ не удалось прочитать вложение ' +
1555
- f.name +
1556
- ': ' +
1557
- e.message));
1574
+ this._notice(theme.warn(this._t('ds.attach_read_failed', {
1575
+ name: f.name,
1576
+ error: e.message,
1577
+ })));
1558
1578
  }
1559
1579
  }
1560
1580
  if (!bufPayload.length)
@@ -1590,14 +1610,14 @@ export class DeepSeekBrowser {
1590
1610
  // Fallback: set the files directly on the hidden <input type=file>.
1591
1611
  const input = this.page.locator('input[type="file"]').first();
1592
1612
  if ((await input.count()) === 0) {
1593
- console.error(theme.warn('⚠ не найдено поле загрузки файлов на странице — вложения не прикреплены'));
1613
+ this._notice(theme.warn(this._t('ds.attach_no_input')));
1594
1614
  return;
1595
1615
  }
1596
1616
  try {
1597
1617
  await input.setInputFiles(bufPayload, { timeout: 15_000 });
1598
1618
  }
1599
1619
  catch (e) {
1600
- console.error(theme.warn('⚠ не удалось прикрепить файлы: ' + e.message));
1620
+ this._notice(theme.warn(this._t('ds.attach_failed', { error: e.message })));
1601
1621
  return;
1602
1622
  }
1603
1623
  // Wait for the upload to finish (the attach preview to appear).
package/dist/config.js CHANGED
@@ -40,7 +40,7 @@ export const DEFAULTS = {
40
40
  askRetries: 3,
41
41
  stabilityChecks: 2,
42
42
  stabilityDelayMs: 400,
43
- minSendIntervalMs: 15000,
43
+ minSendIntervalMs: 20000,
44
44
  rateLimitWaitMs: 300000,
45
45
  maxRateLimitRetries: 6,
46
46
  maxIncompleteRetries: 4,
package/dist/i18n.js CHANGED
@@ -537,6 +537,18 @@ const CATALOG = {
537
537
  ru: '⏳ DeepSeek оборвал ответ и кнопки Continue нет — переотправляю задачу ({attempt}/{max})...',
538
538
  en: '⏳ DeepSeek truncated the answer and there is no Continue button — resending the task ({attempt}/{max})...',
539
539
  },
540
+ 'ds.attach_read_failed': {
541
+ ru: '⚠ не удалось прочитать вложение {name}: {error}',
542
+ en: '⚠ could not read the attachment {name}: {error}',
543
+ },
544
+ 'ds.attach_no_input': {
545
+ ru: '⚠ не найдено поле загрузки файлов — вложения не прикреплены',
546
+ en: '⚠ file upload field not found — the attachments were not attached',
547
+ },
548
+ 'ds.attach_failed': {
549
+ ru: '⚠ не удалось прикрепить файлы: {error}',
550
+ en: '⚠ could not attach the files: {error}',
551
+ },
540
552
  'ds.incomplete_give_up': {
541
553
  ru: '✖ DeepSeek {attempt} раз подряд оборвал ответ, не дойдя до конца. Возможно, включён режим размышления — попробуй выключить его (browser.deepThinking) или повтори позже.',
542
554
  en: '✖ DeepSeek truncated the answer {attempt} times in a row. The reasoning mode (deep thinking) may be on — try turning it off (browser.deepThinking) or retry later.',
package/dist/index.js CHANGED
@@ -975,6 +975,7 @@ async function runTask(browser, tools, taskText, workdir, opts, attachments = []
975
975
  transcript,
976
976
  onThinking: () => ui.thinking(),
977
977
  onSendPause: (seconds) => ui.sendPause(seconds),
978
+ onNotice: (msg) => ui.warning(msg),
978
979
  onToolCall: (name, toolArgs) => ui.toolCall(name, toolArgs),
979
980
  onToolResult: (result) => ui.toolResult(result),
980
981
  onAssistantMessage: (msg) => ui.assistant(msg),
@@ -1034,6 +1035,7 @@ async function runTask(browser, tools, taskText, workdir, opts, attachments = []
1034
1035
  // start a stale spinner.
1035
1036
  browser.onSendStart = null;
1036
1037
  browser.onSendPause = null;
1038
+ browser.onNotice = null;
1037
1039
  }
1038
1040
  }
1039
1041
  // ---------- main ----------
package/dist/input.js CHANGED
@@ -977,6 +977,13 @@ export class LineEditor {
977
977
  this.attachments.reset();
978
978
  if (this.onSubmit)
979
979
  this.onSubmit(text, attached);
980
+ // When a message is queued DURING a running task (`busy`), keep the
981
+ // "agent is working" indicator alive: `_stopDots()` above cleared the
982
+ // status, and nothing would restart it until the next send/tool — so the
983
+ // operator saw the spinner vanish and could not tell whether the agent
984
+ // was still working. Restart the animated status unless we are idle.
985
+ if (this.busy)
986
+ this._startThinking();
980
987
  }
981
988
  _insert(text) {
982
989
  const chars = Array.from(this.buf);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.29.11",
3
+ "version": "2.29.13",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {