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 +36 -0
- package/dist/i18n.js +2 -2
- package/dist/index.js +19 -8
- package/dist/input.js +88 -1
- package/dist/theme.js +2 -0
- package/package.json +1 -1
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: ' Контекст чата сохранён. Системный промпт
|
|
666
|
-
en: ' Chat context kept.
|
|
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)
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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;
|