zames_pro 2.15.5 → 2.16.0

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/README.md CHANGED
@@ -206,6 +206,8 @@ Examples:
206
206
  ```
207
207
  /config set maxIterations 20
208
208
  /config set confirmation.bash false
209
+ /config set browser.deepThinking true # DeepSeek Deep thinking (slow; reasoning is hidden)
210
+ /config set browser.webSearch false # DeepSeek Smart web search
209
211
  /config lang en
210
212
  ```
211
213
 
package/dist/browser.js CHANGED
@@ -125,6 +125,11 @@ export class DeepSeekBrowser {
125
125
  maxRateLimitRetries;
126
126
  maxServerBusyRetries;
127
127
  serverBusyWaitMs;
128
+ // Desired state of the DeepSeek chat toggles, applied before each send.
129
+ // deepThinking: the "Deep thinking" toggle (reasoning; the reasoning text
130
+ // is never read/shown). webSearch: the "Smart search" toggle.
131
+ deepThinking;
132
+ webSearch;
128
133
  _lastSentAt;
129
134
  _abort;
130
135
  // The user pressed Esc/Ctrl+C — a "stop" for the WHOLE current batch of
@@ -144,7 +149,7 @@ export class DeepSeekBrowser {
144
149
  // a generation really begins, not during the pre-send phase (chat open,
145
150
  // throttle wait), which used to show a spinner with no work in flight.
146
151
  onSendStart;
147
- constructor({ headless = false, debug = false, channel = 'chrome', answerTimeoutMs = 180000, askRetries = 3, stabilityChecks = 2, stabilityDelayMs = 400, minSendIntervalMs = 15000, rateLimitWaitMs = 300000, maxRateLimitRetries = 6, maxServerBusyRetries = 5, serverBusyWaitMs = 3000, } = {}) {
152
+ constructor({ headless = false, debug = false, channel = 'chrome', answerTimeoutMs = 180000, askRetries = 3, stabilityChecks = 2, stabilityDelayMs = 400, minSendIntervalMs = 15000, rateLimitWaitMs = 300000, maxRateLimitRetries = 6, maxServerBusyRetries = 5, serverBusyWaitMs = 3000, deepThinking = false, webSearch = true, } = {}) {
148
153
  this.headless = headless;
149
154
  this.debug = debug;
150
155
  this.channel = channel;
@@ -157,6 +162,8 @@ export class DeepSeekBrowser {
157
162
  this.maxRateLimitRetries = maxRateLimitRetries;
158
163
  this.maxServerBusyRetries = maxServerBusyRetries;
159
164
  this.serverBusyWaitMs = serverBusyWaitMs;
165
+ this.deepThinking = deepThinking;
166
+ this.webSearch = webSearch;
160
167
  this._lastSentAt = 0;
161
168
  this._abort = false;
162
169
  this._stopped = false;
@@ -329,11 +336,34 @@ export class DeepSeekBrowser {
329
336
  return this._netCapture;
330
337
  }
331
338
  return await this.page.evaluate((sels) => {
339
+ // DeepSeek stores the model's reasoning in .ds-think-content blocks.
340
+ // They are NOT the answer and must never be picked up as the answer
341
+ // (otherwise the terminal would show the long reasoning). We also
342
+ // prefer the first (most specific) selector with a hit.
343
+ const inThink = (e) => {
344
+ let n = e;
345
+ while (n) {
346
+ const cls = (n.className || '').toString();
347
+ if (/ds-think-content|thinking-content/i.test(cls))
348
+ return true;
349
+ n = n.parentElement;
350
+ }
351
+ return false;
352
+ };
332
353
  let el = null;
333
354
  for (const s of sels) {
334
355
  const list = document.querySelectorAll(s);
335
- if (list.length)
336
- el = list[list.length - 1];
356
+ if (!list.length)
357
+ continue;
358
+ for (let i = list.length - 1; i >= 0; i--) {
359
+ const cand = list[i];
360
+ if (inThink(cand))
361
+ continue;
362
+ el = cand;
363
+ break;
364
+ }
365
+ if (el)
366
+ break;
337
367
  }
338
368
  if (!el)
339
369
  return '';
@@ -411,6 +441,34 @@ export class DeepSeekBrowser {
411
441
  async _isGenerating() {
412
442
  return await this._stopButtonVisible();
413
443
  }
444
+ // DeepSeek exposes two toggle buttons above the input: "Deep thinking"
445
+ // (reasoning) and "Smart search" (web search). Their labels are localized,
446
+ // so we match by a loose regex on the visible text and read the state from
447
+ // aria-pressed. We click ONLY when the state differs, so a send does not
448
+ // flip a toggle the operator set by hand.
449
+ async _setToggle(labelRe, want) {
450
+ try {
451
+ const btns = this.page.locator('.ds-toggle-button');
452
+ const count = await btns.count().catch(() => 0);
453
+ for (let i = 0; i < count; i++) {
454
+ const b = btns.nth(i);
455
+ const txt = ((await b.textContent().catch(() => '')) || '').trim();
456
+ if (!labelRe.test(txt))
457
+ continue;
458
+ const pressed = (await b.getAttribute('aria-pressed').catch(() => null)) === 'true';
459
+ if (pressed !== want) {
460
+ await b.click({ timeout: 2000 }).catch(() => { });
461
+ await this.page.waitForTimeout(150);
462
+ }
463
+ return;
464
+ }
465
+ }
466
+ catch { }
467
+ }
468
+ async _applyToggles() {
469
+ await this._setToggle(/глубок|deep\s*think/i, this.deepThinking);
470
+ await this._setToggle(/поиск|search/i, this.webSearch);
471
+ }
414
472
  async stopGeneration() {
415
473
  this._abort = true;
416
474
  this._stopped = true;
@@ -691,6 +749,10 @@ export class DeepSeekBrowser {
691
749
  }
692
750
  this._netCapture = '';
693
751
  this._netCaptureAt = 0;
752
+ // Align the DeepSeek chat toggles (deep thinking / web search) with the
753
+ // configured state BEFORE typing. Doing it here (after the send-pause) it
754
+ // does not flip toggles for a generation that is not going to happen.
755
+ await this._applyToggles();
694
756
  // Attach files/images FIRST (before the text): the DeepSeek upload widget
695
757
  // shows them above the input, and only then the message can be sent.
696
758
  if (attachments.length) {
package/dist/config.js CHANGED
@@ -41,6 +41,8 @@ export const DEFAULTS = {
41
41
  minSendIntervalMs: 15000,
42
42
  rateLimitWaitMs: 300000,
43
43
  maxRateLimitRetries: 6,
44
+ deepThinking: false,
45
+ webSearch: true,
44
46
  },
45
47
  ui: {
46
48
  locale: DEFAULT_LOCALE,
@@ -94,6 +96,8 @@ export const CONFIG_SCHEMA = [
94
96
  { path: 'browser.minSendIntervalMs', type: 'number', min: 0, max: 600000, labelKey: 'cfg.f.browser_minSendIntervalMs', groupKey: 'cfg.group.browser' },
95
97
  { path: 'browser.rateLimitWaitMs', type: 'number', min: 0, max: 3600000, labelKey: 'cfg.f.browser_rateLimitWaitMs', groupKey: 'cfg.group.browser' },
96
98
  { path: 'browser.maxRateLimitRetries', type: 'number', min: 0, max: 100, labelKey: 'cfg.f.browser_maxRateLimitRetries', groupKey: 'cfg.group.browser' },
99
+ { path: 'browser.deepThinking', type: 'boolean', labelKey: 'cfg.f.browser_deepThinking', groupKey: 'cfg.group.browser' },
100
+ { path: 'browser.webSearch', type: 'boolean', labelKey: 'cfg.f.browser_webSearch', groupKey: 'cfg.group.browser' },
97
101
  ];
98
102
  export function getConfigField(path) {
99
103
  return CONFIG_SCHEMA.find((f) => f.path === path);
package/dist/i18n.js CHANGED
@@ -293,6 +293,8 @@ const CATALOG = {
293
293
  'cfg.f.browser_minSendIntervalMs': { ru: 'Мин. пауза между отправками, мс', en: 'Min pause between sends, ms' },
294
294
  'cfg.f.browser_rateLimitWaitMs': { ru: 'Пауза при лимите частоты, мс', en: 'Wait on rate limit, ms' },
295
295
  'cfg.f.browser_maxRateLimitRetries': { ru: 'Число повторов при лимите', en: 'Rate-limit retry count' },
296
+ 'cfg.f.browser_deepThinking': { ru: 'Глубокое мышление (долго; размышления не выводятся)', en: 'Deep thinking (slow; reasoning is hidden)' },
297
+ 'cfg.f.browser_webSearch': { ru: 'Умный поиск в интернете', en: 'Smart web search' },
296
298
  'cfg.menu.title': { ru: '⚙ Настройки — выбери параметр', en: '⚙ Settings — choose a parameter' },
297
299
  'cfg.menu.hint': { ru: '↑/↓ — выбор, Enter — изменить, q/Esc — выйти', en: '↑/↓ select, Enter edit, q/Esc quit' },
298
300
  'cfg.menu.edit_hint': { ru: 'Введи значение и Enter (пусто — отмена)', en: 'Enter a value and press Enter (empty — cancel)' },
package/dist/index.js CHANGED
@@ -1263,6 +1263,16 @@ async function main() {
1263
1263
  editor.setPrompt(buildPrompt());
1264
1264
  }
1265
1265
  }
1266
+ // Reflect browser toggles on the LIVE browser object. The browser is
1267
+ // created once at startup, so without this a /config set browser.*
1268
+ // change would only apply after a restart.
1269
+ if (path === 'browser.deepThinking' || path === 'browser.webSearch') {
1270
+ const b = browser;
1271
+ if (path === 'browser.deepThinking')
1272
+ b.deepThinking = !!value;
1273
+ else
1274
+ b.webSearch = !!value;
1275
+ }
1266
1276
  }
1267
1277
  function configSetRaw(field, raw) {
1268
1278
  const value = validateConfigValue(field, raw);
@@ -43,6 +43,12 @@ function parseDataLines(body) {
43
43
  // incremental APPEND chunks response/fragments/-1/content.
44
44
  export function extractFromSse(body) {
45
45
  let out = '';
46
+ // Type of the CURRENT last fragment. When deep thinking is on, the
47
+ // stream first fills a THINK fragment (the reasoning) and only then a
48
+ // RESPONSE fragment (the answer), and BOTH use the same APPEND path
49
+ // response/fragments/-1/content. Without tracking this, the reasoning was
50
+ // concatenated into the answer and leaked to the terminal.
51
+ let lastType = '';
46
52
  for (const obj of parseDataLines(body)) {
47
53
  if (obj == null || typeof obj !== 'object')
48
54
  continue;
@@ -58,21 +64,43 @@ export function extractFromSse(body) {
58
64
  ? v.response
59
65
  : undefined;
60
66
  if (resp && Array.isArray(resp.fragments)) {
61
- for (const fr of resp.fragments) {
67
+ const frags = resp.fragments;
68
+ for (const fr of frags) {
62
69
  if (fr && fr.type === 'RESPONSE' && typeof fr.content === 'string') {
63
70
  out += fr.content;
64
71
  }
65
72
  }
73
+ const lastFrag = frags[frags.length - 1];
74
+ if (lastFrag && typeof lastFrag.type === 'string')
75
+ lastType = lastFrag.type;
76
+ continue;
77
+ }
78
+ // A NEW fragment is appended to response/fragments[]. The LAST fragment
79
+ // decides what the following content chunks belong to (THINK vs
80
+ // RESPONSE).
81
+ if (o.p === 'response/fragments' && Array.isArray(o.v)) {
82
+ const frags = o.v;
83
+ for (const fr of frags) {
84
+ if (fr && fr.type === 'RESPONSE' && typeof fr.content === 'string') {
85
+ out += fr.content;
86
+ }
87
+ }
88
+ const lastFrag = frags[frags.length - 1];
89
+ if (lastFrag && typeof lastFrag.type === 'string')
90
+ lastType = lastFrag.type;
66
91
  continue;
67
92
  }
68
93
  // Incremental APPEND: p='response/fragments/-1/content' -> v=string;
69
- // subsequent chunks come with a single v field.
94
+ // subsequent chunks come with a single v field. Append ONLY when the
95
+ // current fragment is RESPONSE; a THINK fragment is the reasoning.
70
96
  if (o.p === 'response/fragments/-1/content' && typeof o.v === 'string') {
71
- out += o.v;
97
+ if (lastType === '' || lastType === 'RESPONSE')
98
+ out += o.v;
72
99
  continue;
73
100
  }
74
101
  if (o.p === undefined && typeof o.v === 'string') {
75
- out += o.v;
102
+ if (lastType === '' || lastType === 'RESPONSE')
103
+ out += o.v;
76
104
  }
77
105
  }
78
106
  return out;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.15.5",
3
+ "version": "2.16.0",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {