zames_pro 2.82.141 → 2.83.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/CHANGELOG.md +12 -0
- package/README.md +19 -0
- package/dist/attachments.js +4 -7
- package/dist/backlog.js +2 -2
- package/dist/browser-pure.js +2 -2
- package/dist/commands.js +2 -2
- package/dist/compact.js +1 -1
- package/dist/config.js +10 -1
- package/dist/glm-browser.js +1 -1
- package/dist/glm-net-capture.js +1 -1
- package/dist/glm-ui.js +0 -20
- package/dist/hooks.js +1 -1
- package/dist/i18n/config.js +4 -0
- package/dist/index.js +24 -1
- package/dist/mcp.js +2 -2
- package/dist/qwen-browser.js +1 -1
- package/dist/qwen-ui.js +0 -2
- package/dist/run-task.js +1 -1
- package/dist/scheduler.js +1 -1
- package/dist/sessions.js +1 -1
- package/dist/spinner.js +1 -1
- package/dist/statusline.js +2 -2
- package/dist/subagent.js +3 -3
- package/dist/system-prompt.js +1 -1
- package/dist/theme.js +239 -37
- package/dist/tui/AskQuestion.js +2 -2
- package/dist/tui/ConfigMenu.js +7 -1
- package/dist/tui/FullScreenApp.js +11 -18
- package/dist/tui/InputLine.js +5 -4
- package/dist/tui/LogView.js +1 -1
- package/dist/tui/SelectList.js +1 -1
- package/dist/tui/StatusBar.js +10 -9
- package/dist/tui/TuiEditor.js +9 -0
- package/dist/tui/edit.js +1 -1
- package/dist/tui/layout.js +1 -1
- package/dist/tui/store.js +1 -0
- package/dist/tui/ui-colors.js +37 -0
- package/dist/tui/widgets.js +6 -41
- package/dist/web.js +1 -1
- package/dist/width.js +1 -1
- package/llms-full.txt +9 -0
- package/package.json +1 -1
- package/dist/code-blocks.js +0 -54
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.83.0] - 2026-10-27
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **ui:** selectable terminal color themes
|
|
15
|
+
|
|
16
|
+
## [2.82.142] - 2026-10-27
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **release:** bump package-lock.json root versions too
|
|
21
|
+
|
|
10
22
|
## [2.82.141] - 2026-10-26
|
|
11
23
|
|
|
12
24
|
### Fixed
|
package/README.md
CHANGED
|
@@ -620,6 +620,15 @@ right away (where possible without a restart).
|
|
|
620
620
|
(help, messages, spinner) and the language the agent answers you in. The
|
|
621
621
|
locale lives in `ui.locale` in the config file.
|
|
622
622
|
|
|
623
|
+
### Themes
|
|
624
|
+
|
|
625
|
+
`/config set ui.theme <id>` (or the `/config` menu, row «Terminal color theme»)
|
|
626
|
+
repaints the whole terminal in a different palette, live — no restart. `zames`
|
|
627
|
+
is the default; the bundled palettes are ports of well-known editor schemes:
|
|
628
|
+
|
|
629
|
+
`zames` (default), `one-dark`, `dracula`, `gruvbox`, `nord`, `monokai`,
|
|
630
|
+
`solarized-dark`, `tokyo-night`.
|
|
631
|
+
|
|
623
632
|
Agent data is stored in `~/.zames`: browser profile, logs, undo history, sessions.
|
|
624
633
|
|
|
625
634
|
## FAQ
|
|
@@ -1330,6 +1339,16 @@ enum переключаются на месте; числа и строки от
|
|
|
1330
1339
|
(справка, сообщения, спиннер), и язык, на котором агент вам отвечает. Локаль
|
|
1331
1340
|
хранится в `ui.locale` в файле конфига.
|
|
1332
1341
|
|
|
1342
|
+
### Темы
|
|
1343
|
+
|
|
1344
|
+
`/config set ui.theme <id>` (или меню `/config`, строка «Цветовая тема
|
|
1345
|
+
терминала») перекрашивает весь терминал в другую палитру, сразу — без
|
|
1346
|
+
перезапуска. Тема по умолчанию — `zames`; в комплекте порты известных схем
|
|
1347
|
+
редакторов:
|
|
1348
|
+
|
|
1349
|
+
`zames` (по умолчанию), `one-dark`, `dracula`, `gruvbox`, `nord`, `monokai`,
|
|
1350
|
+
`solarized-dark`, `tokyo-night`.
|
|
1351
|
+
|
|
1333
1352
|
Данные агента хранятся в `~/.zames`: профиль браузера, логи, история undo,
|
|
1334
1353
|
сессии.
|
|
1335
1354
|
|
package/dist/attachments.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createHash } from 'crypto';
|
|
|
6
6
|
import { execFileSync } from 'child_process';
|
|
7
7
|
import { extractPathToken } from './path-token.js';
|
|
8
8
|
export { extractPathToken };
|
|
9
|
-
|
|
9
|
+
const IMAGE_EXTS = [
|
|
10
10
|
'.png',
|
|
11
11
|
'.jpg',
|
|
12
12
|
'.jpeg',
|
|
@@ -16,7 +16,7 @@ export const IMAGE_EXTS = [
|
|
|
16
16
|
'.svg',
|
|
17
17
|
'.ico',
|
|
18
18
|
];
|
|
19
|
-
|
|
19
|
+
const MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
|
|
20
20
|
// Stable content hash for de-duplicating identical attachments (a pasted image
|
|
21
21
|
// is saved to a NEW temp file every time, so its path is not a stable identity).
|
|
22
22
|
export function contentHash(data) {
|
|
@@ -286,7 +286,7 @@ export function hasClipboardTool() {
|
|
|
286
286
|
}
|
|
287
287
|
// True when the Windows filesystem is visible from here, i.e. a
|
|
288
288
|
// powershell.exe call can reach the Windows clipboard.
|
|
289
|
-
|
|
289
|
+
function windowsReachable() {
|
|
290
290
|
if (process.platform === 'win32')
|
|
291
291
|
return true;
|
|
292
292
|
if (process.platform !== 'linux')
|
|
@@ -410,9 +410,6 @@ export async function readClipboardImageDetailed() {
|
|
|
410
410
|
}
|
|
411
411
|
return { data: null, via: 'unsupported-platform' };
|
|
412
412
|
}
|
|
413
|
-
export async function readClipboardImage() {
|
|
414
|
-
return (await readClipboardImageDetailed()).data;
|
|
415
|
-
}
|
|
416
413
|
function tryCommand(bin, args) {
|
|
417
414
|
try {
|
|
418
415
|
const out = execFileSync(bin, args, {
|
|
@@ -501,7 +498,7 @@ export function winPathToWsl(winPath) {
|
|
|
501
498
|
return '/mnt/' + drive + '/' + rest;
|
|
502
499
|
}
|
|
503
500
|
// Run the embedded PowerShell script and return the clipboard image bytes.
|
|
504
|
-
|
|
501
|
+
async function readWindowsClipboardImage() {
|
|
505
502
|
if (!isWsl())
|
|
506
503
|
return null;
|
|
507
504
|
const out = tryCommand('powershell.exe', [
|
package/dist/backlog.js
CHANGED
|
@@ -13,8 +13,8 @@ const NL = String.fromCharCode(10);
|
|
|
13
13
|
const NEW_ITEM_PREFIX = 'N';
|
|
14
14
|
// Past these thresholds BACKLOG.md costs more tokens (on /improve) than it is
|
|
15
15
|
// worth, so the operator is nudged to collapse it.
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const BACKLOG_WARN_LINES = 500;
|
|
17
|
+
const BACKLOG_WARN_CHARS = 60_000;
|
|
18
18
|
// Parse a `/backlog <text>` note into the shape appendBacklogItem() wants. An
|
|
19
19
|
// optional leading `P0..P3` token sets the priority; the first line becomes the
|
|
20
20
|
// title and the rest the body. PURE so the operator's note is placed exactly as
|
package/dist/browser-pure.js
CHANGED
|
@@ -214,7 +214,7 @@ export function sanitizeHeadlessUA(ua) {
|
|
|
214
214
|
// reported "no session" for whichever provider had signed in second. A legacy
|
|
215
215
|
// record without `providers` still reads: `{provider:'qwen'}` means Qwen and a
|
|
216
216
|
// bare `{at}` means DeepSeek (the only writer before the second backend).
|
|
217
|
-
|
|
217
|
+
const AUTH_MARKER_FILE = path.join(os.homedir(), '.zames', 'auth.json');
|
|
218
218
|
/**
|
|
219
219
|
* PURE: does the marker text say `provider` has a stored session? Legacy
|
|
220
220
|
* shapes are honored (see the comment above AUTH_MARKER_FILE).
|
|
@@ -269,7 +269,7 @@ export function mergeAuthMarker(raw, provider, at) {
|
|
|
269
269
|
return JSON.stringify({ providers }, null, 2);
|
|
270
270
|
}
|
|
271
271
|
// Best-effort: read the marker text from disk ('' when absent/unreadable).
|
|
272
|
-
|
|
272
|
+
async function readAuthMarker() {
|
|
273
273
|
try {
|
|
274
274
|
return await fs.readFile(AUTH_MARKER_FILE, 'utf-8');
|
|
275
275
|
}
|
package/dist/commands.js
CHANGED
|
@@ -161,7 +161,7 @@ export function parseTranscript(body) {
|
|
|
161
161
|
}
|
|
162
162
|
return out;
|
|
163
163
|
}
|
|
164
|
-
|
|
164
|
+
const EN_DURATION_UNITS = { h: 'h', m: 'm', s: 's' };
|
|
165
165
|
// Human-readable duration: "8s", "5m 12s", "1h 01m 01s". Seconds are padded
|
|
166
166
|
// only when a larger unit is present, so a lone "45s" is not "45s" padded.
|
|
167
167
|
export function formatDuration(ms, units = EN_DURATION_UNITS) {
|
|
@@ -421,7 +421,7 @@ export const RESTORED_HISTORY_LIMIT = 20;
|
|
|
421
421
|
* history) is far bigger and is filtered by the noise checks below; a real
|
|
422
422
|
* answer above this size is shortened instead of being dropped.
|
|
423
423
|
*/
|
|
424
|
-
|
|
424
|
+
const RESTORED_MESSAGE_MAX = 100_000;
|
|
425
425
|
/**
|
|
426
426
|
* DeepSeek stores the assistant turn VERBATIM: either a raw tool-call JSON
|
|
427
427
|
* ({"tool":"Read","args":{...}}, frequently dirty/truncated) or a `respond`
|
package/dist/compact.js
CHANGED
|
@@ -3,7 +3,7 @@ import { buildCompactPrompt, buildCompactCarryover, isUsableCompactSummary, trim
|
|
|
3
3
|
import { loadHooks, runLifecycleHooks } from './hooks.js';
|
|
4
4
|
const NL = String.fromCharCode(10);
|
|
5
5
|
/** How many times the summary call is retried before the local fallback. */
|
|
6
|
-
|
|
6
|
+
const COMPACT_SUMMARY_ATTEMPTS = 3;
|
|
7
7
|
/** How many restored messages the local fallback keeps. */
|
|
8
8
|
export const COMPACT_FALLBACK_LIMIT = 40;
|
|
9
9
|
/**
|
package/dist/config.js
CHANGED
|
@@ -5,6 +5,7 @@ import { DEFAULT_DIAGNOSTICS } from './diagnostics.js';
|
|
|
5
5
|
import { DEFAULT_LOCALE } from './i18n.js';
|
|
6
6
|
import { writeJsonAtomic } from './fsutil.js';
|
|
7
7
|
import { PROVIDER_IDS } from './providers.js';
|
|
8
|
+
import { DEFAULT_THEME, themeNames } from './theme.js';
|
|
8
9
|
const ZAMES_HOME = path.join(os.homedir(), '.zames');
|
|
9
10
|
const HOME_CONFIG = path.join(ZAMES_HOME, 'config.json');
|
|
10
11
|
// The PROJECT config path is resolved LAZILY: `/cd` changes process.cwd() at
|
|
@@ -132,6 +133,7 @@ export const DEFAULTS = {
|
|
|
132
133
|
},
|
|
133
134
|
ui: {
|
|
134
135
|
locale: DEFAULT_LOCALE,
|
|
136
|
+
theme: DEFAULT_THEME,
|
|
135
137
|
contextLimit: 1_000_000,
|
|
136
138
|
// 0 = auto: terminal width, capped at 100 columns (the historical width).
|
|
137
139
|
answerWidth: 0,
|
|
@@ -289,6 +291,13 @@ export const CONFIG_SCHEMA = [
|
|
|
289
291
|
labelKey: 'cfg.f.maxIterations',
|
|
290
292
|
groupKey: 'cfg.group.agent',
|
|
291
293
|
},
|
|
294
|
+
{
|
|
295
|
+
path: 'ui.theme',
|
|
296
|
+
type: 'enum',
|
|
297
|
+
values: themeNames(),
|
|
298
|
+
labelKey: 'cfg.f.ui_theme',
|
|
299
|
+
groupKey: 'cfg.group.ui',
|
|
300
|
+
},
|
|
292
301
|
{
|
|
293
302
|
path: 'ui.answerWidth',
|
|
294
303
|
type: 'number',
|
|
@@ -748,7 +757,7 @@ function parseConfigValue(field, raw) {
|
|
|
748
757
|
export function validateConfigValue(field, raw) {
|
|
749
758
|
return parseConfigValue(field, raw);
|
|
750
759
|
}
|
|
751
|
-
|
|
760
|
+
function configPathFor(scope) {
|
|
752
761
|
return scope === 'project' ? projectConfigPath() : HOME_CONFIG;
|
|
753
762
|
}
|
|
754
763
|
/**
|
package/dist/glm-browser.js
CHANGED
|
@@ -33,7 +33,7 @@ import { extractAnswer, extractTokenUsage, isFinishedWithoutAnswer, isRateLimitT
|
|
|
33
33
|
import { SendRuntime } from './send-runtime.js';
|
|
34
34
|
import { readGlmBodyFeatures, unsafeGlmModes, glmFlagsMismatch, } from './glm-flags.js';
|
|
35
35
|
import { interactiveBrowserLogin, loginIsInteractive, canShowWindow, } from './browser-login.js';
|
|
36
|
-
|
|
36
|
+
const GLM_USER_DATA_DIR = path.join(os.homedir(), '.zames', 'glm-profile');
|
|
37
37
|
const MAX_NET_BODY = 64 * 1024;
|
|
38
38
|
function capNetBody(body) {
|
|
39
39
|
if (body.length <= MAX_NET_BODY)
|
package/dist/glm-net-capture.js
CHANGED
|
@@ -224,7 +224,7 @@ export function extractTokenUsage(body) {
|
|
|
224
224
|
return null;
|
|
225
225
|
}
|
|
226
226
|
/** True when a chunk marks the end of the stream (`done: true`). */
|
|
227
|
-
|
|
227
|
+
function isDoneChunk(obj) {
|
|
228
228
|
const d = completionData(obj);
|
|
229
229
|
if (!d)
|
|
230
230
|
return false;
|
package/dist/glm-ui.js
CHANGED
|
@@ -49,8 +49,6 @@ export function chatHistoryUrl(chatId) {
|
|
|
49
49
|
}
|
|
50
50
|
/** GET - the current user (or guest) record. */
|
|
51
51
|
export const AUTH_URL = 'https://chat.z.ai/api/v1/auths/';
|
|
52
|
-
/** POST - creates a chat and returns { id }. */
|
|
53
|
-
export const NEW_CHAT_URL = 'https://chat.z.ai/api/v1/chats/new';
|
|
54
52
|
/** POST - the SSE completion endpoint (the answer stream). */
|
|
55
53
|
export const ANSWER_URL_RE = /\/api\/v2\/chat\/completions/i;
|
|
56
54
|
// ---------- input ----------
|
|
@@ -142,30 +140,12 @@ export function glmContextWindow(model) {
|
|
|
142
140
|
return null;
|
|
143
141
|
}
|
|
144
142
|
// ---------- service statuses / errors ----------
|
|
145
|
-
// A placeholder that must never be taken for an answer.
|
|
146
|
-
export const STATUS_RE = /^(thinking|generating|thinking\s+process|thought\s+process)[\s.…]*$/i;
|
|
147
|
-
// z.ai's captcha gate (an Aliyun slider). Visible text on the modal.
|
|
148
|
-
export const CAPTCHA_RE = /security verification|drag the slider/i;
|
|
149
|
-
export const CAPTCHA_SELECTORS = [
|
|
150
|
-
'.aliyun-captcha',
|
|
151
|
-
'[class*="aliyunCaptcha"]',
|
|
152
|
-
'[id*="aliyunCaptcha"]',
|
|
153
|
-
];
|
|
154
143
|
// Rate limit / busy notices. Kept NARROW: the classifier additionally requires
|
|
155
144
|
// an error payload or an answer-less body (same rule as qwen-net-capture.ts),
|
|
156
145
|
// so a normal answer that merely mentions "try again" is not mistaken for a
|
|
157
146
|
// limit.
|
|
158
147
|
export const RATE_LIMIT_RE = /too many requests|rate limit|too frequent|please try again later|слишком часто/i;
|
|
159
148
|
export const SERVER_BUSY_RE = /server is busy|service unavailable|try again later|high demand/i;
|
|
160
|
-
export const GUEST_LIMIT_RE = /guest.*limit|log ?in to continue|sign ?in to continue|гостевой лимит/i;
|
|
161
|
-
// ---------- mode flags ----------
|
|
162
|
-
// The completion body carries the mode flags. `preview_mode` and the
|
|
163
|
-
// `general_agent` flag change the ANSWER FORMAT (they enable z.ai's own agent
|
|
164
|
-
// runtime, which emits its own tool calls), and that would break the zames
|
|
165
|
-
// tool-call protocol. The request body is rewritten before the send and then
|
|
166
|
-
// verified (glm-browser's _installRuntimeRewrite/_verifyModes + glm-flags).
|
|
167
|
-
export const SAFE_PREVIEW_MODE = false;
|
|
168
|
-
export const SAFE_ENABLE_THINKING = true;
|
|
169
149
|
// ---------- attachments ----------
|
|
170
150
|
// The observed upload control (a real filechooser on click).
|
|
171
151
|
export const UPLOAD_BUTTON_SELECTOR = '#upload-file-button, button[class*="upload"]';
|
package/dist/hooks.js
CHANGED
package/dist/i18n/config.js
CHANGED
|
@@ -55,6 +55,10 @@ export const configMessages = {
|
|
|
55
55
|
ru: 'Язык интерфейса и ответов агента',
|
|
56
56
|
en: 'Interface and agent reply language',
|
|
57
57
|
},
|
|
58
|
+
'cfg.f.ui_theme': {
|
|
59
|
+
ru: 'Цветовая тема терминала',
|
|
60
|
+
en: 'Terminal color theme',
|
|
61
|
+
},
|
|
58
62
|
'cfg.f.ui_answerWidth': {
|
|
59
63
|
ru: 'Ширина контента в колонках: ответы, ввод, инструменты (0 — авто, до 100)',
|
|
60
64
|
en: 'Content width in columns: answers, input, tools (0 = auto, capped at 100)',
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import fs from 'fs/promises';
|
|
5
5
|
import { readFileSync, statSync } from 'fs';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
|
-
import { theme } from './theme.js';
|
|
7
|
+
import { theme, applyTheme, themeColors } from './theme.js';
|
|
8
8
|
import { DeepSeekBrowser, headlessUACacheReady } from './browser.js';
|
|
9
9
|
import { QwenBrowser } from './qwen-browser.js';
|
|
10
10
|
import { GlmBrowser } from './glm-browser.js';
|
|
@@ -119,6 +119,10 @@ if (providerArg !== null) {
|
|
|
119
119
|
}
|
|
120
120
|
// Apply the configured answer width to the markdown renderer (0 = auto).
|
|
121
121
|
setAnswerWidth(config.ui?.answerWidth ?? 0);
|
|
122
|
+
// The color palette must be selected BEFORE the first line is rendered (the
|
|
123
|
+
// banner, startup notices): theme.ts starts on the default and every call site
|
|
124
|
+
// reads the live object, so one apply here repaints the whole session.
|
|
125
|
+
applyTheme(config.ui?.theme);
|
|
122
126
|
// Current interface/agent language. Changed by the /config lang <ru|en> command.
|
|
123
127
|
let currentLocale = isLocale(config.ui?.locale)
|
|
124
128
|
? config.ui.locale
|
|
@@ -1934,6 +1938,18 @@ async function main() {
|
|
|
1934
1938
|
editor.setPrompt(buildPrompt());
|
|
1935
1939
|
}
|
|
1936
1940
|
}
|
|
1941
|
+
// Repaint the WHOLE terminal in the new palette immediately. The theme
|
|
1942
|
+
// object is shared and mutated in place, so every later render uses it;
|
|
1943
|
+
// the editor is asked for a full redraw because the rows already on screen
|
|
1944
|
+
// (status, prompt, hints) were painted with the OLD colors.
|
|
1945
|
+
if (path === 'ui.theme') {
|
|
1946
|
+
const applied = applyTheme(value);
|
|
1947
|
+
config.ui.theme = applied;
|
|
1948
|
+
if (editor) {
|
|
1949
|
+
editor.setTheme?.(themeColors());
|
|
1950
|
+
editor.refreshStatus();
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1937
1953
|
// Apply a live change to the answer width immediately, so the next
|
|
1938
1954
|
// rendered answer uses the new width without a restart.
|
|
1939
1955
|
if (path === 'ui.answerWidth') {
|
|
@@ -2155,6 +2171,13 @@ async function main() {
|
|
|
2155
2171
|
getDefault: (path) => getByPath(DEFAULTS, path),
|
|
2156
2172
|
set: (field, raw) => configSetRaw(field, raw),
|
|
2157
2173
|
reset: (field) => configResetField(field),
|
|
2174
|
+
// The menu itself must repaint when the THEME row changes: its widgets
|
|
2175
|
+
// read the shared UI map, and without a bump the operator would cycle
|
|
2176
|
+
// palette ids while the colors stayed those of the previous one.
|
|
2177
|
+
onAfterChange: (field) => {
|
|
2178
|
+
if (field.path === 'ui.theme')
|
|
2179
|
+
editor?.setTheme?.(themeColors());
|
|
2180
|
+
},
|
|
2158
2181
|
locale: currentLocale,
|
|
2159
2182
|
};
|
|
2160
2183
|
try {
|
package/dist/mcp.js
CHANGED
|
@@ -110,7 +110,7 @@ export function normalizeServerConfig(raw) {
|
|
|
110
110
|
return out;
|
|
111
111
|
}
|
|
112
112
|
/** Candidate config paths, ordered from global to project-local. */
|
|
113
|
-
|
|
113
|
+
async function discoverConfigFiles(workdir) {
|
|
114
114
|
const path = await import('path');
|
|
115
115
|
const os = await import('os');
|
|
116
116
|
const fsMod = await import('fs/promises');
|
|
@@ -127,7 +127,7 @@ export async function discoverConfigFiles(workdir) {
|
|
|
127
127
|
return out;
|
|
128
128
|
}
|
|
129
129
|
/** Read and merge every config file. Later files override earlier ones. */
|
|
130
|
-
|
|
130
|
+
async function loadConfigFiles(files) {
|
|
131
131
|
const merged = {};
|
|
132
132
|
const fsMod = await import('fs/promises');
|
|
133
133
|
for (const f of files) {
|
package/dist/qwen-browser.js
CHANGED
|
@@ -35,7 +35,7 @@ import { extractAnswer, extractTokenUsage, isFinishedWithoutAnswer, isRateLimitT
|
|
|
35
35
|
// contexts on ONE --user-data-dir cannot coexist (SingletonLock), and keeping
|
|
36
36
|
// Qwen's cookies apart also means switching providers never logs the other
|
|
37
37
|
// one out.
|
|
38
|
-
|
|
38
|
+
const QWEN_USER_DATA_DIR = path.join(os.homedir(), '.zames', 'qwen-profile');
|
|
39
39
|
// Escape a string for use inside a RegExp (the model id may carry dots).
|
|
40
40
|
function escapeRegExp(s) {
|
|
41
41
|
return String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
package/dist/qwen-ui.js
CHANGED
|
@@ -169,8 +169,6 @@ export const LOGIN_SELECTORS = [
|
|
|
169
169
|
'input[autocomplete="username"]',
|
|
170
170
|
];
|
|
171
171
|
// ---------- service statuses / errors ----------
|
|
172
|
-
// A generic "generating" label that must never be taken for an answer.
|
|
173
|
-
export const STATUS_RE = /^(thinking|searching|generating|thinking completed|остановить|stop)[\s.…]*$/i;
|
|
174
172
|
// Qwen's rate-limit / busy notices (best-effort; the toast may be short-lived).
|
|
175
173
|
export const RATE_LIMIT_RE = /too many requests|rate limit|too frequent|please try again later|слишком часто/i;
|
|
176
174
|
export const SERVER_BUSY_RE = /server is busy|service unavailable|try again later/i;
|
package/dist/run-task.js
CHANGED
|
@@ -123,7 +123,7 @@ async function promptOnce(question) {
|
|
|
123
123
|
// Enter sends the buffer to onQueue; an empty Enter is ignored. Backspace,
|
|
124
124
|
// Ctrl+U, Esc sequences (arrows/Home/End/Delete are ignored) and bracketed
|
|
125
125
|
// paste are supported. Returns a cleanup function.
|
|
126
|
-
|
|
126
|
+
function watchInput({ onEscape, onChange, onQueue, } = {}) {
|
|
127
127
|
const stdin = process.stdin;
|
|
128
128
|
if (!stdin.isTTY || !stdin.setRawMode)
|
|
129
129
|
return () => { };
|
package/dist/scheduler.js
CHANGED
|
@@ -26,7 +26,7 @@ export function parseInterval(text) {
|
|
|
26
26
|
return ms;
|
|
27
27
|
}
|
|
28
28
|
/** Largest delay setInterval accepts (32-bit signed milliseconds). */
|
|
29
|
-
|
|
29
|
+
const MAX_TIMER_MS = 2_147_483_647;
|
|
30
30
|
// Render an interval back to the shortest human form (used in listings).
|
|
31
31
|
// Non-finite input (a cron job whose next fire could not be computed sets
|
|
32
32
|
// nextAt to Infinity) renders as "-" instead of "Infinityh".
|
package/dist/sessions.js
CHANGED
|
@@ -68,7 +68,7 @@ function safeName(id) {
|
|
|
68
68
|
// Save/update a session. workdir is needed so that when launched from the same
|
|
69
69
|
// project we restore exactly its last chat.
|
|
70
70
|
/** Hard cap on the todo checklist stored with a session. */
|
|
71
|
-
|
|
71
|
+
const SESSION_TODOS_CAP = 1000;
|
|
72
72
|
export function saveSession({ id, title = '', workdir = '', todos, provider, }) {
|
|
73
73
|
if (!id)
|
|
74
74
|
return null;
|
package/dist/spinner.js
CHANGED
|
@@ -22,7 +22,7 @@ export function stripEllipsis(phrase) {
|
|
|
22
22
|
// (3 dots) so the trailing hint does not "jump" when the phase changes. Kept in
|
|
23
23
|
// one place so the two status lines animate identically.
|
|
24
24
|
export const DOTS = ['', '.', '..', '...'];
|
|
25
|
-
|
|
25
|
+
const DOTS_PAD = ' ';
|
|
26
26
|
// The colored dot string for a phase (brown, matching the base text).
|
|
27
27
|
export function renderDots(n) {
|
|
28
28
|
const d = DOTS[n] ?? '';
|
package/dist/statusline.js
CHANGED
|
@@ -9,10 +9,10 @@ import { spawn } from 'node:child_process';
|
|
|
9
9
|
// hidden), never an error in the UI.
|
|
10
10
|
// Longest status text that still fits a typical terminal row together with
|
|
11
11
|
// the rest of the status bar.
|
|
12
|
-
|
|
12
|
+
const STATUS_BADGE_MAX = 80;
|
|
13
13
|
/** Clip a status badge to STATUS_BADGE_MAX with an ellipsis (never splits a
|
|
14
14
|
* surrogate pair). */
|
|
15
|
-
|
|
15
|
+
function clipBadge(text, max = STATUS_BADGE_MAX) {
|
|
16
16
|
const s = String(text ?? '');
|
|
17
17
|
if (s.length <= max)
|
|
18
18
|
return s;
|
package/dist/subagent.js
CHANGED
|
@@ -14,11 +14,11 @@ import { markInternalChat } from './sessions.js';
|
|
|
14
14
|
// subagents would need a second page and a shared send scheduler (a change to
|
|
15
15
|
// browser.ts, which owns the live Playwright context) — out of scope here.
|
|
16
16
|
/** Hard cap on iterations for one subagent run. */
|
|
17
|
-
|
|
17
|
+
const SUBAGENT_MAX_ITER = 30;
|
|
18
18
|
// The report is fed back into the PARENT chat as a tool result, so a runaway
|
|
19
19
|
// subagent must not be able to blow up the parent's context with one answer.
|
|
20
20
|
// The tail is trimmed with a marker, not silently dropped.
|
|
21
|
-
|
|
21
|
+
const SUBAGENT_REPORT_CAP = 20_000;
|
|
22
22
|
/** Cap a subagent report before it becomes a parent-context tool result. */
|
|
23
23
|
export function capReport(text, cap = SUBAGENT_REPORT_CAP) {
|
|
24
24
|
if (text.length <= cap)
|
|
@@ -41,7 +41,7 @@ export function capReport(text, cap = SUBAGENT_REPORT_CAP) {
|
|
|
41
41
|
* standard system prompt itself (tool list + call format), and the model needs
|
|
42
42
|
* exactly that to work. The persona only adds role + output contract.
|
|
43
43
|
*/
|
|
44
|
-
|
|
44
|
+
function subagentPersona(type) {
|
|
45
45
|
const base = 'You are a SUBAGENT launched by a main coding agent to complete ONE ' +
|
|
46
46
|
'self-contained sub-task in an ISOLATED chat. You do NOT see the main ' +
|
|
47
47
|
"conversation's history, so rely only on the instructions below and on " +
|
package/dist/system-prompt.js
CHANGED
|
@@ -199,7 +199,7 @@ export function renderContextSection(context) {
|
|
|
199
199
|
// of truth for `### <Tool>` blocks; the prose that cannot be derived
|
|
200
200
|
// ("Choosing the right tool", the call formats) stays hard-coded below.
|
|
201
201
|
// Guarded by test/system-prompt-tools.test.ts.
|
|
202
|
-
|
|
202
|
+
function renderToolSection(tools) {
|
|
203
203
|
return tools
|
|
204
204
|
.map((t2) => `### ${t2.name}\n${t2.description}\nParameters: ${JSON.stringify(t2.parameters)}`)
|
|
205
205
|
.join('\n\n');
|