zames_pro 2.25.0 → 2.27.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/dist/browser.js CHANGED
@@ -280,6 +280,10 @@ export class DeepSeekBrowser {
280
280
  // completion stream and from /api/v0/chat/history_messages. Null until the
281
281
  // first answer (or history fetch) delivers it. Shown by /cost and /status.
282
282
  _lastTokenUsage;
283
+ // Last known DeepSeek toggle states (deep thinking / web search), kept in
284
+ // sync by _applyToggles()/getToggleStates(). Exposed synchronously so the
285
+ // editor can draw the toggle icons on every render without awaiting.
286
+ _toggles;
283
287
  // Authorization / PoW headers sniffed from DeepSeek's own API requests, so
284
288
  // fetchChatMessages can replay them (a bare fetch does not get them).
285
289
  _apiAuth;
@@ -332,6 +336,7 @@ export class DeepSeekBrowser {
332
336
  this._netCaptureAt = 0;
333
337
  this._netChatId = null;
334
338
  this._lastTokenUsage = null;
339
+ this._toggles = { deepThinking, webSearch };
335
340
  this._apiAuth = '';
336
341
  this._apiPow = '';
337
342
  this._lastHistoryError = '';
@@ -1039,6 +1044,37 @@ export class DeepSeekBrowser {
1039
1044
  async _applyToggles() {
1040
1045
  await this._setToggle(/глубок|deep\s*think/i, this.deepThinking);
1041
1046
  await this._setToggle(/поиск|search/i, this.webSearch);
1047
+ this._toggles = await this.getToggleStates();
1048
+ }
1049
+ // Synchronous snapshot of the last known toggle states, for the editor's
1050
+ // status icons (called on every render, must not await).
1051
+ getToggleStatesSync() {
1052
+ return this._toggles;
1053
+ }
1054
+ // The CURRENT state of the two DeepSeek toggles, read from the DOM
1055
+ // (aria-pressed) so the status line reflects what is actually on, not what
1056
+ // the config asked for. Falls back to the configured values when the buttons
1057
+ // are not found.
1058
+ async getToggleStates() {
1059
+ const read = async (labelRe, fallback) => {
1060
+ try {
1061
+ const btns = this.page.locator('.ds-toggle-button');
1062
+ const count = await btns.count().catch(() => 0);
1063
+ for (let i = 0; i < count; i++) {
1064
+ const b = btns.nth(i);
1065
+ const txt = ((await b.textContent().catch(() => '')) || '').trim();
1066
+ if (!labelRe.test(txt))
1067
+ continue;
1068
+ return ((await b.getAttribute('aria-pressed').catch(() => null)) === 'true');
1069
+ }
1070
+ }
1071
+ catch { }
1072
+ return fallback;
1073
+ };
1074
+ return {
1075
+ deepThinking: await read(/глубок|deep\s*think/i, this.deepThinking),
1076
+ webSearch: await read(/поиск|search/i, this.webSearch),
1077
+ };
1042
1078
  }
1043
1079
  async stopGeneration() {
1044
1080
  this._abort = true;
package/dist/i18n.js CHANGED
@@ -662,8 +662,8 @@ const CATALOG = {
662
662
  },
663
663
  'chats.opening': { ru: 'Открываю: {v}', en: 'Opening: {v}' },
664
664
  'chats.context_kept': {
665
- ru: ' Контекст чата сохранён. Системный промпт не пересылается (--resend-prompt чтобы дослать).\n',
666
- en: ' Chat context kept. System prompt is not resent (--resend-prompt to force).\n',
665
+ ru: ' Контекст чата сохранён. Системный промпт будет переслан на следующей задаче.\n',
666
+ en: ' Chat context kept. The system prompt will be resent on the next task.\n',
667
667
  },
668
668
  'chats.prompt_will_resend': {
669
669
  ru: ' Системный промпт будет переслан на следующей задаче.\n',
package/dist/index.js CHANGED
@@ -412,7 +412,18 @@ async function resolveAttachPath(workdir, raw) {
412
412
  s = s.replace(/\\ /g, ' ');
413
413
  if (!looksLikeFilePath(s) && !isImageName(s))
414
414
  return null;
415
- const candidates = path.isAbsolute(s) ? [s] : [path.resolve(workdir, s)];
415
+ const candidates = path.isAbsolute(s)
416
+ ? [s]
417
+ : [
418
+ // Most common: relative to the project (working) directory.
419
+ path.resolve(workdir, s),
420
+ // The operator may paste a path relative to the terminal's cwd (which
421
+ // can be the parent of the project) or copy it with the project name
422
+ // included, e.g. "zames_pro/tmp/image.png" while workdir is already
423
+ // .../zames_pro. Try both so the marker appears instead of raw text.
424
+ path.resolve(process.cwd(), s),
425
+ path.resolve(workdir, '..', s),
426
+ ];
416
427
  for (const c of candidates) {
417
428
  const st = await fs.stat(c).catch(() => null);
418
429
  if (st && st.isFile())
@@ -1170,7 +1181,7 @@ async function main() {
1170
1181
  await browser.openChat(resumeId);
1171
1182
  currentChatId = resumeId;
1172
1183
  freshChatNext = false;
1173
- sendSystemPromptNext = resendPrompt;
1184
+ sendSystemPromptNext = true;
1174
1185
  saveLastChat(resumeId, currentWorkdir);
1175
1186
  console.log(theme.system(t('msg.chat_opened') + ' ' + resumeId + String.fromCharCode(10)));
1176
1187
  await printRestoredHistory(browser, null, resumeId);
@@ -1219,6 +1230,9 @@ async function main() {
1219
1230
  // The editor pulls the number on every render, so it follows the live
1220
1231
  // DeepSeek counter (accumulated_token_usage) without a polling timer.
1221
1232
  ed.onContextQuery = () => browser.getLastTokenUsage();
1233
+ // Toggle icons (🧠 deep thinking, 🌐 web search) before the context
1234
+ // counter. The editor pulls the live state on every render.
1235
+ ed.onToggleQuery = () => browser.getToggleStatesSync();
1222
1236
  ed.onAttach = async (raw) => {
1223
1237
  // Case 1: the paste is the image data itself (data URL / base64 blob).
1224
1238
  const image = parseImagePaste(raw);
@@ -1863,13 +1877,11 @@ async function main() {
1863
1877
  await browser.openChat(pick.id);
1864
1878
  currentChatId = pick.id;
1865
1879
  freshChatNext = false;
1866
- sendSystemPromptNext = resendPrompt;
1880
+ sendSystemPromptNext = true;
1867
1881
  saveLastChat(pick.id, currentWorkdir, pick.title);
1868
1882
  transcript.log('resume_chat', { id: pick.id, title: pick.title });
1869
1883
  console.log(theme.assistant(t('msg.chat_opened')) +
1870
- theme.system(resendPrompt
1871
- ? ' Системный промпт будет переслан на следующей задаче.\n'
1872
- : ' Контекст чата сохранён. Системный промпт не пересылается (--resend-prompt чтобы дослать).\n'));
1884
+ theme.system(' Системный промпт будет переслан на следующей задаче (чтобы модель видела инструменты).\n'));
1873
1885
  await printRestoredHistory(browser, editor, pick.id);
1874
1886
  }
1875
1887
  catch (e) {
@@ -1923,7 +1935,7 @@ async function main() {
1923
1935
  await browser.openChat(id);
1924
1936
  currentChatId = id;
1925
1937
  freshChatNext = false;
1926
- sendSystemPromptNext = resendPrompt;
1938
+ sendSystemPromptNext = true;
1927
1939
  saveLastChat(id, currentWorkdir);
1928
1940
  transcript.log('resume_chat', { id });
1929
1941
  console.log(theme.assistant('Чат открыт.') +
@@ -2123,7 +2135,6 @@ async function main() {
2123
2135
  console.log(theme.system(t('status.chat', { v: currentChatId || t('common.none') })));
2124
2136
  console.log(theme.system(t('status.fresh_next', { v: freshChatNext ? yes : no })));
2125
2137
  console.log(theme.system(t('status.prompt_next', { v: sendSystemPromptNext ? yes : no })));
2126
- console.log(theme.system(t('status.resend', { v: resendPrompt ? yes : no })));
2127
2138
  console.log(theme.system(t('status.last_chat', {
2128
2139
  v: loadLastChat(currentWorkdir) || t('common.none'),
2129
2140
  })));
package/dist/input.js CHANGED
@@ -289,6 +289,14 @@ export class LineEditor {
289
289
  contextStatus;
290
290
  // The raw token count behind contextStatus (for the color level).
291
291
  contextTokens;
292
+ // Whether the DeepSeek "Deep thinking" / "Smart search" toggles are ON.
293
+ // Shown as two colored icons before the context counter: a dim gray icon
294
+ // means off, a teal-green icon means on.
295
+ thinkingEnabled;
296
+ searchEnabled;
297
+ // A callback the editor calls to fetch the CURRENT toggle states before
298
+ // every render, so the icons follow the live chat state.
299
+ onToggleQuery;
292
300
  // A callback the editor calls to fetch the CURRENT token count before every
293
301
  // status render, so the status line stays fresh without the caller polling.
294
302
  onContextQuery;
@@ -328,8 +336,27 @@ export class LineEditor {
328
336
  this.locked = false;
329
337
  this.contextStatus = null;
330
338
  this.contextTokens = null;
339
+ this.thinkingEnabled = false;
340
+ this.searchEnabled = false;
341
+ this.onToggleQuery = null;
331
342
  this.onContextQuery = null;
332
343
  }
344
+ // Refresh the toggle icon states from onToggleQuery (if wired) before a
345
+ // render. A broken callback must never break the render.
346
+ _refreshToggles() {
347
+ if (!this.onToggleQuery)
348
+ return;
349
+ try {
350
+ const s = this.onToggleQuery();
351
+ if (s) {
352
+ this.thinkingEnabled = !!s.deepThinking;
353
+ this.searchEnabled = !!s.webSearch;
354
+ }
355
+ }
356
+ catch {
357
+ // ignore
358
+ }
359
+ }
333
360
  // The token status for the CURRENT render: refreshed from onContextQuery
334
361
  // when wired, otherwise the last value passed to setContextStatus().
335
362
  _contextForRender() {
@@ -359,6 +386,22 @@ export class LineEditor {
359
386
  return theme.warn(ctx);
360
387
  return theme.success(ctx);
361
388
  }
389
+ // Two colored icons for the DeepSeek toggles: 🧠 "Deep thinking" and 🌐 "Smart
390
+ // search". A dim gray icon = off, a teal-green icon = on. Placed just before
391
+ // the context counter, so the operator sees the live chat state at a glance.
392
+ // U+FE0E (VARIATION SELECTOR-15) forces the TEXT presentation of the emoji:
393
+ // without it the terminal paints the emoji in its own colors (pink brain,
394
+ // blue globe) and the ANSI color is ignored — "the icons are colored, and it
395
+ // is unclear which is on". With the selector they become monochrome glyphs
396
+ // and take the on/off color.
397
+ _toggleIcons() {
398
+ const on = (s) => theme.toggleOn(s);
399
+ const off = (s) => theme.toggleOff(s);
400
+ const TEXT = '\uFE0E';
401
+ const brain = this.thinkingEnabled ? on('🧠' + TEXT) : off('🧠' + TEXT);
402
+ const globe = this.searchEnabled ? on('🌐' + TEXT) : off('🌐' + TEXT);
403
+ return brain + ' ' + globe + ' ';
404
+ }
362
405
  // Read the OS clipboard for an image and insert its marker. Used when the
363
406
  // terminal sends no usable paste data (Ctrl+V / right-click / empty paste).
364
407
  async _tryClipboard() {
@@ -545,7 +588,12 @@ export class LineEditor {
545
588
  // though it PRINTS `status + ' '.repeat(space) + ctxText` with a DIFFERENT
546
589
  // total width, so at certain widths `top` disagreed with the printed rows.
547
590
  // Both branches below now derive `top` from the exact string they print.
548
- const ctxText = this._contextText();
591
+ // Toggle icons (always shown) + the context counter (when known). The
592
+ // icons reflect the live DeepSeek toggles; a fresh chat has no token count
593
+ // but the icons are still meaningful.
594
+ const ctxRaw = this._contextText();
595
+ this._refreshToggles();
596
+ const ctxText = this._toggleIcons() + ctxRaw;
549
597
  let statusOut = '';
550
598
  let top = 0;
551
599
  // Never fill a row to the FULL terminal width: on terminals with
@@ -812,6 +860,45 @@ export class LineEditor {
812
860
  this._render();
813
861
  return;
814
862
  }
863
+ // Fallback for terminals that do NOT send a bracketed paste: if the WHOLE
864
+ // input is a path to an existing image/file, attach it and send the marker
865
+ // instead of the raw path. Without this the operator pasted a path, saw
866
+ // plain text, and no [image#N] marker appeared.
867
+ if (this.onAttach && !this.attachments.items.length) {
868
+ const t = display.trim();
869
+ const quoted = (t.startsWith('"') && t.endsWith('"')) ||
870
+ (t.startsWith("'") && t.endsWith("'"));
871
+ // Only treat the input as a path when it is a SINGLE token (no spaces)
872
+ // that looks like a file name — so a normal sentence containing a dot is
873
+ // never swallowed as an attachment.
874
+ const singleToken = !/\s/.test(t);
875
+ const looksPath = singleToken && /[./\\]/.test(t) && /\.[a-z0-9]{1,6}$/i.test(t);
876
+ if (looksPath || (quoted && /[./\\]/.test(t))) {
877
+ void this._attachOnSubmit(display);
878
+ return;
879
+ }
880
+ }
881
+ this._doSubmit(display);
882
+ }
883
+ // Try to attach a whole-input path, then submit the marker (or the raw text
884
+ // when the attachment fails).
885
+ async _attachOnSubmit(display) {
886
+ if (this.onAttach) {
887
+ try {
888
+ const att = await this.onAttach(display.trim());
889
+ if (att) {
890
+ this.buf = att.marker;
891
+ this.cursor = Array.from(this.buf).length;
892
+ this._render();
893
+ this._doSubmit(att.marker);
894
+ return;
895
+ }
896
+ }
897
+ catch { }
898
+ }
899
+ this._doSubmit(display);
900
+ }
901
+ _doSubmit(display) {
815
902
  // The input line shows compact markers "[Pasted lines#N]" instead of large
816
903
  // pastes — expand them back into the original text before sending.
817
904
  const text = expandPastes(this.pastes, display);
package/dist/theme.js CHANGED
@@ -27,6 +27,8 @@ export const theme = {
27
27
  error: chalk.hex('#c98a80'), // muted terracotta
28
28
  success: chalk.hex('#a9c08c'), // soft sage
29
29
  brown: chalk.hex('#a1723f'), // brown (status/spinner)
30
+ toggleOn: chalk.hex('#6fd0b0'), // teal-green — a toggle is ON
31
+ toggleOff: chalk.hex('#4a4f5a'), // dim gray — a toggle is OFF
30
32
  bold: chalk.bold,
31
33
  };
32
34
  export default theme;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.25.0",
3
+ "version": "2.27.0",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {