zames_pro 2.75.2 → 2.75.4
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/CHANGELOG.md +16 -0
- package/README.md +1 -1
- package/dist/config.js +15 -0
- package/dist/i18n/config.js +4 -0
- package/dist/index.js +69 -11
- package/dist/markdown.js +17 -1
- package/dist/qwen-ui.js +41 -0
- package/package.json +2 -2
- /package/{logo-small.jpg → logo.jpg} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.75.4] - 2026-10-15
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **qwen:** per-model context window for the status bar and auto-compact
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **test:** format qwen-context test with prettier
|
|
19
|
+
|
|
20
|
+
## [2.75.3] - 2026-10-15
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **markdown:** always apply the theme in rendered answers; rename logo-small.jpg to logo.jpg
|
|
25
|
+
|
|
10
26
|
## [2.75.2] - 2026-10-15
|
|
11
27
|
|
|
12
28
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/logo
|
|
2
|
+
<img src="https://raw.githubusercontent.com/Viqto0r/zames_pro/master/logo.jpg" alt="zames logo" width="430">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">zames_pro</h1>
|
package/dist/config.js
CHANGED
|
@@ -21,6 +21,13 @@ export const DEFAULTS = {
|
|
|
21
21
|
qwen: {
|
|
22
22
|
model: '',
|
|
23
23
|
enabled: false,
|
|
24
|
+
// Qwen's context window is PER-MODEL, not a site constant: Qwen3-Max is
|
|
25
|
+
// 258K while the 3.7/3.8-Max and Coder builds are 1M. The default assumes
|
|
26
|
+
// the common Qwen3-Max; `/model` re-derives it from the picked id (see
|
|
27
|
+
// qwenContextWindow in src/qwen-ui.ts) and `/config set qwen.contextLimit`
|
|
28
|
+
// overrides it. Kept SEPARATE from ui.contextLimit (DeepSeek's 1M) so one
|
|
29
|
+
// backend's window never becomes the other's denominator.
|
|
30
|
+
contextLimit: 258_048,
|
|
24
31
|
// Qwen DOES rate-limit a rapid burst: measured live, the 5th back-to-back
|
|
25
32
|
// send at 0 ms gap hit "too frequent" and needed a 5-min back-off. A 1 s
|
|
26
33
|
// gap was too small for a real editing session, so the default is now 4 s
|
|
@@ -237,6 +244,14 @@ export const CONFIG_SCHEMA = [
|
|
|
237
244
|
labelKey: 'cfg.f.qwen_model',
|
|
238
245
|
groupKey: 'cfg.group.browser',
|
|
239
246
|
},
|
|
247
|
+
{
|
|
248
|
+
path: 'qwen.contextLimit',
|
|
249
|
+
type: 'number',
|
|
250
|
+
min: 10_000,
|
|
251
|
+
max: 10_000_000,
|
|
252
|
+
labelKey: 'cfg.f.qwen_contextLimit',
|
|
253
|
+
groupKey: 'cfg.group.browser',
|
|
254
|
+
},
|
|
240
255
|
{
|
|
241
256
|
path: 'qwen.minSendIntervalMs',
|
|
242
257
|
type: 'number',
|
package/dist/i18n/config.js
CHANGED
|
@@ -11,6 +11,10 @@ export const configMessages = {
|
|
|
11
11
|
ru: 'Модель Qwen (id)',
|
|
12
12
|
en: 'Qwen model (id)',
|
|
13
13
|
},
|
|
14
|
+
'cfg.f.qwen_contextLimit': {
|
|
15
|
+
ru: 'Qwen: размер контекста, токены (258K у Qwen3-Max, 1M у 3.7/3.8/Coder)',
|
|
16
|
+
en: 'Qwen: context window, tokens (258K on Qwen3-Max, 1M on 3.7/3.8/Coder)',
|
|
17
|
+
},
|
|
14
18
|
'cfg.f.qwen_minSendIntervalMs': {
|
|
15
19
|
ru: 'Qwen: пауза между отправками (мс)',
|
|
16
20
|
en: 'Qwen: pause between sends (ms)',
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ import { applyBrowserLiveConfig, applyStoreLiveConfig } from './config-live.js';
|
|
|
33
33
|
import { buildHandoffText, saveProviderHandoff, readProviderHandoff, clearProviderHandoff, } from './provider-handoff.js';
|
|
34
34
|
import { Scheduler, parseInterval, formatInterval, formatJobLine, parseCron, } from './scheduler.js';
|
|
35
35
|
import { createCliArgs } from './cli-args.js';
|
|
36
|
+
import { qwenContextWindow } from './qwen-ui.js';
|
|
36
37
|
import { renderMarkdown, setAnswerWidth } from './markdown.js';
|
|
37
38
|
import { extractCodeBlocks } from './code-blocks.js';
|
|
38
39
|
import { contentWidth } from './width.js';
|
|
@@ -660,6 +661,42 @@ function providerAnswerTimeoutMs() {
|
|
|
660
661
|
function browserProviderUrl() {
|
|
661
662
|
return providerChatUrlPrefix(config.provider);
|
|
662
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* The context window (tokens) for the ACTIVE provider. The two backends
|
|
666
|
+
* advertise different windows and Qwen's is PER-MODEL, so a single global
|
|
667
|
+
* number would either overstate Qwen's (258K models) or understate the 1M
|
|
668
|
+
* builds — and the number is the denominator of the auto-compact threshold,
|
|
669
|
+
* so an error directly delays (or prematurely fires) compaction.
|
|
670
|
+
*
|
|
671
|
+
* `config.qwen.contextLimit` / `ui.contextLimit` are the operator-tunable
|
|
672
|
+
* values; `/model` rewrites the Qwen one from the picked model id.
|
|
673
|
+
*/
|
|
674
|
+
function effectiveContextLimit() {
|
|
675
|
+
if (config.provider === 'qwen') {
|
|
676
|
+
return config.qwen.contextLimit > 0
|
|
677
|
+
? config.qwen.contextLimit
|
|
678
|
+
: config.ui.contextLimit;
|
|
679
|
+
}
|
|
680
|
+
return config.ui.contextLimit;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Re-derive `qwen.contextLimit` from a picked model id and persist it. Called
|
|
684
|
+
* by `/model` on an explicit pick (never on startup), so a manual
|
|
685
|
+
* `/config set qwen.contextLimit` survives until the operator switches model
|
|
686
|
+
* again. An UNKNOWN model id leaves the limit alone: guessing would either
|
|
687
|
+
* fire auto-compact too early or too late. The live editor is repainted by
|
|
688
|
+
* the caller (it owns the `editor` reference); this only touches config.
|
|
689
|
+
*/
|
|
690
|
+
function applyModelContextLimit(model) {
|
|
691
|
+
const window = qwenContextWindow(model);
|
|
692
|
+
if (window === null)
|
|
693
|
+
return;
|
|
694
|
+
config.qwen.contextLimit = window;
|
|
695
|
+
try {
|
|
696
|
+
writeConfigValue('home', 'qwen.contextLimit', String(window));
|
|
697
|
+
}
|
|
698
|
+
catch { }
|
|
699
|
+
}
|
|
663
700
|
/**
|
|
664
701
|
* Build the browser for a provider. Called at startup AND on a live
|
|
665
702
|
* `/provider` switch, so both paths construct the class the same way. The
|
|
@@ -756,8 +793,9 @@ async function printRestoredHistory(browser, ui, chatId = null) {
|
|
|
756
793
|
out(theme.dim(t('chats.history_tokens', { v: String(restoredTokens) })));
|
|
757
794
|
// Warn when the restored chat is near the context limit, and suggest
|
|
758
795
|
// /compact. The threshold mirrors the auto-compact one so the operator
|
|
759
|
-
// sees the same signal whether or not auto-compact is enabled.
|
|
760
|
-
|
|
796
|
+
// sees the same signal whether or not auto-compact is enabled. The limit
|
|
797
|
+
// is the ACTIVE provider's window (Qwen's is per-model).
|
|
798
|
+
const limit = effectiveContextLimit();
|
|
761
799
|
if (limit > 0) {
|
|
762
800
|
const pct = (restoredTokens / limit) * 100;
|
|
763
801
|
if (pct >= 80) {
|
|
@@ -1292,8 +1330,10 @@ async function main() {
|
|
|
1292
1330
|
// Task-list summary ("tasks: 2/5") before the context counter. Pulled on
|
|
1293
1331
|
// every render, like the queue badge.
|
|
1294
1332
|
ed.onTasksQuery = () => todosSummary();
|
|
1295
|
-
// Use the configured context window for the fill percentage/color.
|
|
1296
|
-
|
|
1333
|
+
// Use the configured context window for the fill percentage/color. The
|
|
1334
|
+
// window is PROVIDER-dependent: Qwen's is per-model (258K/1M, see
|
|
1335
|
+
// qwenContextWindow), DeepSeek's is the ui.contextLimit.
|
|
1336
|
+
ed.setContextLimit(effectiveContextLimit());
|
|
1297
1337
|
// Toggle icons (🧠 deep thinking, 🌐 web search) before the context
|
|
1298
1338
|
// counter. The editor pulls the live state on every render.
|
|
1299
1339
|
ed.onToggleQuery = () => browser.getToggleStatesSync();
|
|
@@ -1380,7 +1420,7 @@ async function main() {
|
|
|
1380
1420
|
chatId: currentChatId,
|
|
1381
1421
|
queueLength: pendingQueue.length,
|
|
1382
1422
|
tokens: browser.getLastTokenUsage(),
|
|
1383
|
-
contextLimit:
|
|
1423
|
+
contextLimit: effectiveContextLimit(),
|
|
1384
1424
|
tasks: todosSummary(),
|
|
1385
1425
|
locale: currentLocale,
|
|
1386
1426
|
});
|
|
@@ -1690,10 +1730,13 @@ async function main() {
|
|
|
1690
1730
|
}
|
|
1691
1731
|
// The context window size feeds the status-bar percentage and its color.
|
|
1692
1732
|
// Apply it to the live editor immediately (the value is a plain number,
|
|
1693
|
-
// no restart needed).
|
|
1694
|
-
|
|
1733
|
+
// no restart needed). A change to EITHER provider's limit matters: the
|
|
1734
|
+
// Qwen one is what a /model pick rewrites, and the active provider is
|
|
1735
|
+
// what decides which value the editor shows.
|
|
1736
|
+
if ((path === 'ui.contextLimit' || path === 'qwen.contextLimit') &&
|
|
1737
|
+
typeof value === 'number') {
|
|
1695
1738
|
if (editor) {
|
|
1696
|
-
editor.setContextLimit(
|
|
1739
|
+
editor.setContextLimit(effectiveContextLimit());
|
|
1697
1740
|
editor.refreshStatus();
|
|
1698
1741
|
}
|
|
1699
1742
|
}
|
|
@@ -2556,6 +2599,16 @@ async function main() {
|
|
|
2556
2599
|
config.qwen.model = picked;
|
|
2557
2600
|
if (typeof qb.model === 'string')
|
|
2558
2601
|
qb.model = picked;
|
|
2602
|
+
// Derive the context window from the picked model: Qwen windows
|
|
2603
|
+
// are per-model (258K vs 1M), and this number is the denominator
|
|
2604
|
+
// of the auto-compact threshold. The operator's manual
|
|
2605
|
+
// `/config set qwen.contextLimit` still wins afterwards (this
|
|
2606
|
+
// only runs on an explicit pick).
|
|
2607
|
+
applyModelContextLimit(picked);
|
|
2608
|
+
if (editor) {
|
|
2609
|
+
editor.setContextLimit(effectiveContextLimit());
|
|
2610
|
+
editor.refreshStatus();
|
|
2611
|
+
}
|
|
2559
2612
|
try {
|
|
2560
2613
|
writeConfigValue('home', 'qwen.model', picked);
|
|
2561
2614
|
}
|
|
@@ -2571,6 +2624,11 @@ async function main() {
|
|
|
2571
2624
|
config.qwen.model = arg;
|
|
2572
2625
|
if (typeof qb.model === 'string')
|
|
2573
2626
|
qb.model = arg;
|
|
2627
|
+
applyModelContextLimit(arg);
|
|
2628
|
+
if (editor) {
|
|
2629
|
+
editor.setContextLimit(effectiveContextLimit());
|
|
2630
|
+
editor.refreshStatus();
|
|
2631
|
+
}
|
|
2574
2632
|
try {
|
|
2575
2633
|
writeConfigValue('home', 'qwen.model', arg);
|
|
2576
2634
|
}
|
|
@@ -3257,7 +3315,7 @@ async function main() {
|
|
|
3257
3315
|
saveLastChat(chatId, currentWorkdir);
|
|
3258
3316
|
}
|
|
3259
3317
|
},
|
|
3260
|
-
contextLimit:
|
|
3318
|
+
contextLimit: effectiveContextLimit(),
|
|
3261
3319
|
getTokenUsage: () => browser.getLastTokenUsage(),
|
|
3262
3320
|
goal: sessionGoal,
|
|
3263
3321
|
});
|
|
@@ -3372,7 +3430,7 @@ async function main() {
|
|
|
3372
3430
|
mcpTools: mcpStatus.toolCount,
|
|
3373
3431
|
transcriptOk: !!transcript.file,
|
|
3374
3432
|
authSaved: authMarkerExists(),
|
|
3375
|
-
contextLimit:
|
|
3433
|
+
contextLimit: effectiveContextLimit(),
|
|
3376
3434
|
hasOrigin: gitOk
|
|
3377
3435
|
? (await runGitArgs(['remote'], currentWorkdir, 5000)).includes('origin')
|
|
3378
3436
|
: undefined,
|
|
@@ -3890,7 +3948,7 @@ async function main() {
|
|
|
3890
3948
|
}
|
|
3891
3949
|
: null,
|
|
3892
3950
|
autoCompactPct: config.browser.autoCompactPct,
|
|
3893
|
-
contextLimit:
|
|
3951
|
+
contextLimit: effectiveContextLimit(),
|
|
3894
3952
|
getTokenUsage: () => browser.getLastTokenUsage(),
|
|
3895
3953
|
goal: sessionGoal,
|
|
3896
3954
|
// Completion-goal mode (Claude Code parity): the goal is a CONDITION,
|
package/dist/markdown.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { render } from 'markdansi';
|
|
2
2
|
import { highlight } from 'cli-highlight';
|
|
3
|
+
import supportsHyperlinks from 'supports-hyperlinks';
|
|
3
4
|
const theme = {
|
|
4
5
|
heading: { color: '#9fb8d8', bold: true },
|
|
5
6
|
strong: { color: '#e6e0cc', bold: true },
|
|
@@ -37,16 +38,31 @@ export function setAnswerWidth(width) {
|
|
|
37
38
|
export function renderMarkdown(text) {
|
|
38
39
|
if (!text)
|
|
39
40
|
return '';
|
|
41
|
+
// NO_COLOR (https://no-color.org) is the ONE color opt-out. markdansi
|
|
42
|
+
// builds its OWN Chalk instance, so the `chalk.level = 0` that theme.ts
|
|
43
|
+
// sets for NO_COLOR never reaches it; without this guard the answers would
|
|
44
|
+
// stay colored while everything else went plain.
|
|
45
|
+
if (process.env.NO_COLOR)
|
|
46
|
+
return String(text);
|
|
40
47
|
try {
|
|
41
48
|
const width = contentWidth();
|
|
42
49
|
return render(String(text), {
|
|
43
50
|
width,
|
|
51
|
+
// markdansi infers color support itself (`process.stdout.isTTY`), so
|
|
52
|
+
// under a non-TTY stdout — a piped answer, `--json`, a test — it emitted
|
|
53
|
+
// PLAIN text and the whole theme below was silently unused (answers
|
|
54
|
+
// looked unformatted). `color: true` makes it always emit SGR; the ONE
|
|
55
|
+
// opt-out is NO_COLOR, handled above.
|
|
56
|
+
color: true,
|
|
44
57
|
theme: theme,
|
|
45
58
|
highlighter,
|
|
46
59
|
codeBox: true,
|
|
47
60
|
tableBorder: 'unicode',
|
|
48
61
|
listIndent: 2,
|
|
49
|
-
hyperlinks:
|
|
62
|
+
// OSC-8 hyperlinks: the URL disappears into a clickable label. Only
|
|
63
|
+
// when the terminal advertises support — otherwise the raw URL is
|
|
64
|
+
// printed, which is the safer default for a log that may be piped.
|
|
65
|
+
hyperlinks: supportsHyperlinks.stdout,
|
|
50
66
|
});
|
|
51
67
|
}
|
|
52
68
|
catch {
|
package/dist/qwen-ui.js
CHANGED
|
@@ -76,6 +76,47 @@ export const NEW_CHAT_SELECTOR = '.new-chat, button[aria-label="New Chat" i]';
|
|
|
76
76
|
// model itself is chosen via the config (sent with each completion request)
|
|
77
77
|
// unless a per-session override is set.
|
|
78
78
|
export const MODEL_TRIGGER_SELECTOR = '.wms-trigger';
|
|
79
|
+
// ---------- context windows ----------
|
|
80
|
+
// Qwen's per-model context window (tokens). The window is a property of the
|
|
81
|
+
// MODEL, not the site: Qwen3-Max is 258K while the 3.7/3.8-Max and Coder
|
|
82
|
+
// builds are 1M. zames uses the number ONLY as the denominator for the
|
|
83
|
+
// status-bar fill percentage and the auto-compact threshold, so a wrong value
|
|
84
|
+
// either fires compaction far too late (window overstated) or too early.
|
|
85
|
+
//
|
|
86
|
+
// Matching is done on a NORMALIZED id: lowercased, non-alphanumerics dropped,
|
|
87
|
+
// so `qwen3-max`, `Qwen3-Max` and `qwen3_max` all hit the same row. The list is
|
|
88
|
+
// ordered MOST SPECIFIC first: a `qwen3-coder-plus` id must not stop at a
|
|
89
|
+
// generic `qwen3` rule. Values verified against the public model cards
|
|
90
|
+
// (contextwindows.dev / haimaker.ai) in October 2026.
|
|
91
|
+
const CONTEXT_WINDOWS = [
|
|
92
|
+
// The 1M builds MUST be checked before the 258K `qwen3-max` family, or
|
|
93
|
+
// `qwen3.7-max` would match the shorter `max` rule first.
|
|
94
|
+
[/qwen3\.?7.?max/, 1_000_000],
|
|
95
|
+
[/qwen3\.?8.?max/, 1_000_000],
|
|
96
|
+
[/qwen3coder/, 1_000_000],
|
|
97
|
+
[/qwenlong/, 10_000_000],
|
|
98
|
+
[/qwen3max/, 258_048],
|
|
99
|
+
// Generic fallback: the OTHER qwen3 builds (Plus/Turbo/Flash) advertised 1M
|
|
100
|
+
// on their model cards, so an unknown qwen3 id gets 1M rather than the min.
|
|
101
|
+
[/qwen3/, 1_000_000],
|
|
102
|
+
];
|
|
103
|
+
/** Lowercase and drop every non-alphanumeric so `qwen3.7-max` -> `qwen37max`. */
|
|
104
|
+
export function normalizeModelId(model) {
|
|
105
|
+
return String(model || '')
|
|
106
|
+
.toLowerCase()
|
|
107
|
+
.replace(/[^a-z0-9]/g, '');
|
|
108
|
+
}
|
|
109
|
+
/** The context window (tokens) for a Qwen model id, or null when unknown. */
|
|
110
|
+
export function qwenContextWindow(model) {
|
|
111
|
+
const id = normalizeModelId(model);
|
|
112
|
+
if (!id)
|
|
113
|
+
return null;
|
|
114
|
+
for (const [re, window] of CONTEXT_WINDOWS) {
|
|
115
|
+
if (re.test(id))
|
|
116
|
+
return window;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
79
120
|
// ---------- age gate / cookies ----------
|
|
80
121
|
// A first-time visitor must confirm their birth year before chatting. The
|
|
81
122
|
// dialog is an ant-modal; the Continue button is the black footer button.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.75.
|
|
3
|
+
"version": "2.75.4",
|
|
4
4
|
"description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen and other web LLMs — no API key required. Reads and edits files, runs commands, commits to git, like Claude Code / Codex CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"README.md",
|
|
13
13
|
"CHANGELOG.md",
|
|
14
14
|
"LICENSE",
|
|
15
|
-
"logo
|
|
15
|
+
"logo.jpg"
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
18
|
"start": "node ./dist/index.js",
|
|
File without changes
|