zames_pro 2.82.137 → 2.82.139
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 +153 -0
- package/README.md +86 -63
- package/dist/agent-loop.js +3 -6
- package/dist/answer-parse.js +10 -16
- package/dist/attach-refs.js +3 -3
- package/dist/attachments.js +7 -8
- package/dist/backlog.js +2 -5
- package/dist/banner.js +4 -5
- package/dist/browser-chats.js +4 -4
- package/dist/browser-dom.js +16 -15
- package/dist/browser-login.js +9 -5
- package/dist/browser-pure.js +5 -5
- package/dist/browser.js +10 -20
- package/dist/changelog.js +14 -20
- package/dist/checkpoint.js +6 -10
- package/dist/cli-args.js +4 -6
- package/dist/code-blocks.js +3 -6
- package/dist/commands.js +5 -9
- package/dist/config-command.js +6 -4
- package/dist/config-commands.js +2 -3
- package/dist/config-live.js +7 -13
- package/dist/config-menu.js +5 -4
- package/dist/config.js +5 -8
- package/dist/deepseek-ui.js +7 -7
- package/dist/diagnostics.js +5 -7
- package/dist/diff.js +8 -10
- package/dist/extraTools.js +3 -3
- package/dist/fsutil.js +0 -4
- package/dist/gitTools.js +5 -4
- package/dist/glm-browser.js +2 -10
- package/dist/glm-chats.js +0 -1
- package/dist/glm-net-capture.js +1 -1
- package/dist/glm-ui.js +2 -2
- package/dist/goal.js +1 -4
- package/dist/i18n/common.js +2 -2
- package/dist/i18n.js +1 -2
- package/dist/index.js +0 -11
- package/dist/input/suggest.js +4 -7
- package/dist/input.js +3 -8
- package/dist/markdown.js +0 -1
- package/dist/mcp.js +2 -5
- package/dist/net-capture.js +1 -1
- package/dist/path-token.js +7 -14
- package/dist/prompt.js +4 -3
- package/dist/qwen-browser.js +1 -8
- package/dist/qwen-chats.js +0 -1
- package/dist/qwen-net-capture.js +4 -7
- package/dist/qwen-ui.js +0 -3
- package/dist/run-task.js +4 -4
- package/dist/self-review.js +0 -5
- package/dist/send-runtime.js +6 -10
- package/dist/send-telemetry.js +5 -7
- package/dist/send-throttle.js +4 -5
- package/dist/sessions.js +6 -7
- package/dist/spinner.js +3 -4
- package/dist/statusline.js +5 -9
- package/dist/system-prompt.js +1 -1
- package/dist/tui/widgets.js +0 -1
- package/llms-full.txt +44 -32
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +5 -0
package/dist/qwen-browser.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// and the chat-id scheme. DeepSeekBrowser is already large; folding a second
|
|
7
7
|
// site into it would make both harder to audit. The shared surface is the
|
|
8
8
|
// ChatProvider interface (types.ts); the agent loop only needs BrowserLike and
|
|
9
|
-
// never sees
|
|
9
|
+
// never sees a concrete class.
|
|
10
10
|
//
|
|
11
11
|
// Verified against a live session (headless Chromium, guest + logged in):
|
|
12
12
|
// input -> textarea.message-input-textarea
|
|
@@ -233,7 +233,6 @@ export class QwenBrowser {
|
|
|
233
233
|
sendIntervalMs() {
|
|
234
234
|
return this.minSendIntervalMs;
|
|
235
235
|
}
|
|
236
|
-
// ---------- lifecycle ----------
|
|
237
236
|
async launch() {
|
|
238
237
|
if (await profileLooksLocked(QWEN_USER_DATA_DIR)) {
|
|
239
238
|
if (this.debug)
|
|
@@ -487,7 +486,6 @@ export class QwenBrowser {
|
|
|
487
486
|
}
|
|
488
487
|
catch { }
|
|
489
488
|
}
|
|
490
|
-
// ---------- login ----------
|
|
491
489
|
// Are we signed in? Detected from the PAGE, the same way a human sees it: a
|
|
492
490
|
// signed-out visitor has the header "Log in" / "Sign up" buttons, a signed-in
|
|
493
491
|
// one shows the account menu instead. The `auths/refresh` API was the first
|
|
@@ -1005,7 +1003,6 @@ export class QwenBrowser {
|
|
|
1005
1003
|
}
|
|
1006
1004
|
return false;
|
|
1007
1005
|
}
|
|
1008
|
-
// ---------- chat management ----------
|
|
1009
1006
|
async newChat() {
|
|
1010
1007
|
const before = await this.getCurrentChatId();
|
|
1011
1008
|
const btn = this.page.locator(NEW_CHAT_SELECTOR).first();
|
|
@@ -1272,7 +1269,6 @@ export class QwenBrowser {
|
|
|
1272
1269
|
getLastTokenUsage() {
|
|
1273
1270
|
return this._lastTokenUsage;
|
|
1274
1271
|
}
|
|
1275
|
-
// ---------- answer reading ----------
|
|
1276
1272
|
async _readLastAnswerText() {
|
|
1277
1273
|
if (this._netCapture && this._netCaptureAt >= this._lastSentAt) {
|
|
1278
1274
|
return this._netCapture;
|
|
@@ -1346,7 +1342,6 @@ export class QwenBrowser {
|
|
|
1346
1342
|
catch { }
|
|
1347
1343
|
return false;
|
|
1348
1344
|
}
|
|
1349
|
-
// ---------- toggles (model thinking / search) ----------
|
|
1350
1345
|
getToggleStatesSync() {
|
|
1351
1346
|
return this._toggles;
|
|
1352
1347
|
}
|
|
@@ -1523,7 +1518,6 @@ export class QwenBrowser {
|
|
|
1523
1518
|
}
|
|
1524
1519
|
catch { }
|
|
1525
1520
|
}
|
|
1526
|
-
// ---------- diagnostics ----------
|
|
1527
1521
|
async dumpDom(filePath) {
|
|
1528
1522
|
if (!this.page)
|
|
1529
1523
|
throw new Error('qwen: browser not launched');
|
|
@@ -1566,7 +1560,6 @@ export class QwenBrowser {
|
|
|
1566
1560
|
chatUrl(id) {
|
|
1567
1561
|
return chatUrl(id);
|
|
1568
1562
|
}
|
|
1569
|
-
// ---------- ask ----------
|
|
1570
1563
|
async _waitForSendSlot(agent) {
|
|
1571
1564
|
if (!agent)
|
|
1572
1565
|
return;
|
package/dist/qwen-chats.js
CHANGED
|
@@ -84,7 +84,6 @@ export function scrapeQwenChatMessages(opts) {
|
|
|
84
84
|
}
|
|
85
85
|
return out;
|
|
86
86
|
}
|
|
87
|
-
// ---------- API payload parsers (pure, unit-tested) ----------
|
|
88
87
|
/**
|
|
89
88
|
* Parse the chat list from `GET /api/v2/chats/?page=1&exclude_project=true`.
|
|
90
89
|
* The shape has drifted between builds, so accept several:
|
package/dist/qwen-net-capture.js
CHANGED
|
@@ -223,13 +223,10 @@ export function isFinishedWithoutAnswer(body) {
|
|
|
223
223
|
return extractFromSse(b) === '';
|
|
224
224
|
}
|
|
225
225
|
// True when the body carries Qwen's rate-limit / busy notice, so ask() can
|
|
226
|
-
// back off instead of waiting out the deadline.
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
// ERROR payload (`event: error` / `"type":"error"`) or as a code; answer text
|
|
231
|
-
// lives in `choices[].delta.content`. We therefore only inspect the body when
|
|
232
|
-
// it is NOT a plain answer stream, or when it carries no answer content.
|
|
226
|
+
// back off instead of waiting out the deadline. The classifier runs against the
|
|
227
|
+
// RAW body, where a phrase like `please try again later` also matches a NORMAL
|
|
228
|
+
// answer (the model writing about retry logic) and made ask() back off on a
|
|
229
|
+
// good turn. So it only counts an ERROR payload or a body with NO answer text.
|
|
233
230
|
export function isRateLimitText(body) {
|
|
234
231
|
const b = String(body || '');
|
|
235
232
|
if (!b)
|
package/dist/qwen-ui.js
CHANGED
|
@@ -174,9 +174,6 @@ export const STATUS_RE = /^(thinking|searching|generating|thinking completed|о
|
|
|
174
174
|
// Qwen's rate-limit / busy notices (best-effort; the toast may be short-lived).
|
|
175
175
|
export const RATE_LIMIT_RE = /too many requests|rate limit|too frequent|please try again later|слишком часто/i;
|
|
176
176
|
export const SERVER_BUSY_RE = /server is busy|service unavailable|try again later/i;
|
|
177
|
-
// ---------- toggles ----------
|
|
178
|
-
// The "Auto" thinking/search toggle and the mode selector. Read-only probing
|
|
179
|
-
// support; the model's thinking mode is set via config when supported.
|
|
180
177
|
// ---------- toggles (thinking mode / web search) ----------
|
|
181
178
|
// Qwen exposes thinking as a MODE dropdown (`.qwen-thinking-selector`, label
|
|
182
179
|
// "Auto" by default) with options Auto / Thinking / Fast — NOT a boolean
|
package/dist/run-task.js
CHANGED
|
@@ -330,7 +330,7 @@ export async function runTask(deps, browser, tools, taskText, workdir, opts, att
|
|
|
330
330
|
});
|
|
331
331
|
try {
|
|
332
332
|
// Set when the loop ends without a real answer (iteration limit / watchdog)
|
|
333
|
-
// so the one-shot mode can exit non-zero.
|
|
333
|
+
// so the one-shot mode can exit non-zero.
|
|
334
334
|
let failure = null;
|
|
335
335
|
// The machine-readable reason for the last task's outcome.
|
|
336
336
|
let outcomeKind = 'ok';
|
|
@@ -351,7 +351,7 @@ export async function runTask(deps, browser, tools, taskText, workdir, opts, att
|
|
|
351
351
|
// Calling it here made the spinner run for the whole pre-send phase (chat
|
|
352
352
|
// creation, throttle wait) with no generation in flight.
|
|
353
353
|
while (true) {
|
|
354
|
-
//
|
|
354
|
+
// Per-task summary: duration, number of tool calls and tokens spent.
|
|
355
355
|
const taskStart = Date.now();
|
|
356
356
|
// Token delta for THIS task: accumulated_token_usage is cumulative for
|
|
357
357
|
// the whole chat, so the per-task spend is after - before. Null when the
|
|
@@ -461,8 +461,8 @@ export async function runTask(deps, browser, tools, taskText, workdir, opts, att
|
|
|
461
461
|
else {
|
|
462
462
|
outcomeKind = 'ok';
|
|
463
463
|
}
|
|
464
|
-
//
|
|
465
|
-
//
|
|
464
|
+
// One summary line per task, printed even on an abort, so the operator
|
|
465
|
+
// sees what happened.
|
|
466
466
|
const tokensAfter = getTokenUsage ? getTokenUsage() : null;
|
|
467
467
|
const tokenDelta = typeof tokensBefore === 'number' && typeof tokensAfter === 'number'
|
|
468
468
|
? Math.max(0, tokensAfter - tokensBefore)
|
package/dist/self-review.js
CHANGED
|
@@ -122,7 +122,6 @@ async function copyContextFiles(srcDir, snapDir) {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
-
// ---------- /self-review ----------
|
|
126
125
|
export async function selfReview({ browser, focus, transcript, locale, }) {
|
|
127
126
|
const t = translator(locale);
|
|
128
127
|
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
@@ -190,7 +189,6 @@ export async function selfReview({ browser, focus, transcript, locale, }) {
|
|
|
190
189
|
console.log();
|
|
191
190
|
return { snapDir, reportPath, changed };
|
|
192
191
|
}
|
|
193
|
-
// ---------- /self-diff ----------
|
|
194
192
|
export async function selfDiff({ name, locale, }) {
|
|
195
193
|
const t = translator(locale);
|
|
196
194
|
const snapDir = path.join(SNAP_ROOT, name);
|
|
@@ -218,7 +216,6 @@ export async function selfDiff({ name, locale, }) {
|
|
|
218
216
|
console.log(theme.system(t('self.no_diff')));
|
|
219
217
|
}
|
|
220
218
|
}
|
|
221
|
-
// ---------- /self-apply ----------
|
|
222
219
|
export async function selfApply({ name, locale, }) {
|
|
223
220
|
const t = translator(locale);
|
|
224
221
|
const snapDir = path.join(SNAP_ROOT, name);
|
|
@@ -247,7 +244,6 @@ export async function selfApply({ name, locale, }) {
|
|
|
247
244
|
console.log(theme.system(t('self.backup', { dir: backupDir })));
|
|
248
245
|
console.log(theme.warn('\n' + t('self.restart_hint') + '\n'));
|
|
249
246
|
}
|
|
250
|
-
// ---------- /self-list ----------
|
|
251
247
|
export async function selfList({ locale, } = {}) {
|
|
252
248
|
const t = translator(locale);
|
|
253
249
|
const snapRoot = path.join(SNAP_ROOT);
|
|
@@ -285,7 +281,6 @@ export async function selfList({ locale, } = {}) {
|
|
|
285
281
|
console.log();
|
|
286
282
|
console.log(theme.system(t('self.list_commands') + '\n'));
|
|
287
283
|
}
|
|
288
|
-
// ---------- helpers ----------
|
|
289
284
|
async function diffFiles(origDir, newDir) {
|
|
290
285
|
const files = await listTsFiles(newDir).catch(() => []);
|
|
291
286
|
const changed = [];
|
package/dist/send-runtime.js
CHANGED
|
@@ -78,11 +78,9 @@ export class SendRuntime {
|
|
|
78
78
|
if (typeof extra.retryAfterMs === 'number') {
|
|
79
79
|
e.retryAfterMs = extra.retryAfterMs;
|
|
80
80
|
}
|
|
81
|
-
// A SUCCESS after a rate limit IS the lift of that block
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
// fold below clears `blockedAt` first. The measured duration is therefore
|
|
85
|
-
// taken HERE, before the state is folded; it is attached to the SAME event
|
|
81
|
+
// A SUCCESS after a rate limit IS the lift of that block: the retry happens
|
|
82
|
+
// IN PLACE and simply succeeds. The duration is therefore measured HERE,
|
|
83
|
+
// before the fold below clears `blockedAt`, and attached to the SAME event
|
|
86
84
|
// line, so one block still yields exactly one sample.
|
|
87
85
|
let blockMs;
|
|
88
86
|
if (outcome === 'ok' &&
|
|
@@ -101,11 +99,9 @@ export class SendRuntime {
|
|
|
101
99
|
this.state = { ...this.state, blocked: true, blockedAt: this.now() };
|
|
102
100
|
}
|
|
103
101
|
if (outcome === 'rate_limit') {
|
|
104
|
-
// The
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
// lower bound that would then be averaged together with the real
|
|
108
|
-
// duration written by onBlockLifted — the same block counted TWICE.
|
|
102
|
+
// The event carries NO duration: a rate-limit send is not the END of the
|
|
103
|
+
// block, so a value measured here would be averaged together with the
|
|
104
|
+
// real duration measured at the lift — the same block counted TWICE.
|
|
109
105
|
if (!this.state.blockedAt) {
|
|
110
106
|
this.state = { ...this.state, blocked: true, blockedAt: this.now() };
|
|
111
107
|
}
|
package/dist/send-telemetry.js
CHANGED
|
@@ -51,14 +51,12 @@ export function summarizeSends(events, now = Date.now()) {
|
|
|
51
51
|
const age = now - t;
|
|
52
52
|
return age >= 0 && age <= ms;
|
|
53
53
|
}).length;
|
|
54
|
-
// `blockMs` may arrive as a NUMBER (the writer) or a string (an older
|
|
55
|
-
// build, a hand-edited file): a numeric string is accepted so the average
|
|
56
|
-
// is not silently 0 for a file that records the block.
|
|
57
54
|
const blocks = events
|
|
58
|
-
// `
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
// block
|
|
55
|
+
// `blockMs` may arrive as a NUMBER (the writer) or a string (an older
|
|
56
|
+
// build, a hand-edited file), so it is coerced. A missing value must NOT
|
|
57
|
+
// become 0: 0 is a finite non-negative number, and an event without a
|
|
58
|
+
// block duration (the rate-limit event only records `blockedAt`) was
|
|
59
|
+
// averaged as a ZERO-length block, halving the reported average.
|
|
62
60
|
.map((e) => e.blockMs === null || e.blockMs === undefined ? NaN : Number(e.blockMs))
|
|
63
61
|
// A finite value is not enough: the SUM of many large durations overflows
|
|
64
62
|
// a JS number (two `Number.MAX_VALUE` entries add up to Infinity), so the
|
package/dist/send-throttle.js
CHANGED
|
@@ -39,11 +39,10 @@ function prune(recent, now, windowMs) {
|
|
|
39
39
|
*/
|
|
40
40
|
export function nextWaitMs(state, lastSentAt, now) {
|
|
41
41
|
if (state.forcedWaitMs > 0) {
|
|
42
|
-
// A forced wait must EXPIRE:
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// the current call, and expires on the next.
|
|
42
|
+
// A forced wait must EXPIRE: with only `forcedWaitMs > 0` a long
|
|
43
|
+
// Retry-After blocked every send until another event cleared it. The
|
|
44
|
+
// request stamp makes the remaining time exact; without one (a caller-built
|
|
45
|
+
// or restored state) the wait is reported ONCE, from the current call.
|
|
47
46
|
if (state.forcedWaitAt) {
|
|
48
47
|
const left = state.forcedWaitMs - (now - state.forcedWaitAt);
|
|
49
48
|
return left > 0 ? left : 0;
|
package/dist/sessions.js
CHANGED
|
@@ -133,13 +133,12 @@ function writeIndex({ id, workdir }) {
|
|
|
133
133
|
}
|
|
134
134
|
catch { }
|
|
135
135
|
}
|
|
136
|
-
// The last session for the working directory
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
// global last only within the same provider).
|
|
136
|
+
// The last session for the working directory (the last one overall when the
|
|
137
|
+
// directory has none — useful when launched from a new place), restricted to
|
|
138
|
+
// `provider` when given. A chat id only exists on ITS backend, so restoring a
|
|
139
|
+
// DeepSeek chat while the active provider is Qwen (or vice versa) would fail to
|
|
140
|
+
// open; the filter makes auto-restore and /last pick the right one, and fall
|
|
141
|
+
// back to the global last only within the same provider.
|
|
143
142
|
export function loadLastSession(workdir = '', provider) {
|
|
144
143
|
try {
|
|
145
144
|
const index = JSON.parse(fs.readFileSync(INDEX_FILE, 'utf-8'));
|
package/dist/spinner.js
CHANGED
|
@@ -152,10 +152,9 @@ export function createSpinner(locale = 'ru') {
|
|
|
152
152
|
}
|
|
153
153
|
startThinking();
|
|
154
154
|
},
|
|
155
|
-
// The agent is waiting out the send-interval pause before a real send
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
// was printed and the dots stayed frozen).
|
|
155
|
+
// The agent is waiting out the send-interval pause before a real send: an
|
|
156
|
+
// ANIMATED status with the remaining seconds, so the spinner keeps moving
|
|
157
|
+
// (a static line made the pause look frozen).
|
|
159
158
|
sendPause: (seconds) => {
|
|
160
159
|
if (pending)
|
|
161
160
|
return;
|
package/dist/statusline.js
CHANGED
|
@@ -58,10 +58,6 @@ export function runStatusLineCommand(workdir, command, info, timeoutMs = 4000) {
|
|
|
58
58
|
// (the row overflows and the cursor arithmetic drifts).
|
|
59
59
|
resolve(clipBadge(first));
|
|
60
60
|
};
|
|
61
|
-
// Only the FIRST LINE is shown, so there is no reason to keep collecting a
|
|
62
|
-
// big blob: a chatty command (a JSON dump, `ls -R`) would grow `out`
|
|
63
|
-
// unboundedly and the status row then repainted megabytes. Stop appending
|
|
64
|
-
// once a short line is buffered (the rest of that line does not matter).
|
|
65
61
|
const timer = setTimeout(() => {
|
|
66
62
|
try {
|
|
67
63
|
if (process.platform !== 'win32' && child.pid) {
|
|
@@ -74,15 +70,15 @@ export function runStatusLineCommand(workdir, command, info, timeoutMs = 4000) {
|
|
|
74
70
|
catch { }
|
|
75
71
|
finish();
|
|
76
72
|
}, timeoutMs);
|
|
77
|
-
//
|
|
78
|
-
//
|
|
73
|
+
// Only the FIRST line is shown and the status line is a short badge, so the
|
|
74
|
+
// buffer is capped: a chatty command (a JSON dump, `ls -R`) would otherwise
|
|
75
|
+
// grow `out` and make the status row repaint megabytes on every tick.
|
|
79
76
|
const MAX_OUT = 400;
|
|
80
77
|
child.stdout?.on('data', (c) => {
|
|
81
78
|
if (out.length >= MAX_OUT)
|
|
82
79
|
return;
|
|
83
|
-
// A chunk can be huge (one 64 KB read)
|
|
84
|
-
//
|
|
85
|
-
// status row repaint megabytes on every tick.
|
|
80
|
+
// A chunk can be huge (one 64 KB read): clip it to the remaining space so
|
|
81
|
+
// the buffer never exceeds MAX_OUT.
|
|
86
82
|
out += c.toString().slice(0, MAX_OUT - out.length);
|
|
87
83
|
});
|
|
88
84
|
child.on('error', () => {
|
package/dist/system-prompt.js
CHANGED
|
@@ -124,7 +124,7 @@ export function renderContextSection(context) {
|
|
|
124
124
|
f.content +
|
|
125
125
|
'\n';
|
|
126
126
|
}
|
|
127
|
-
//
|
|
127
|
+
// Nested AGENTS.md/MEMORY.md from the directories this task touches.
|
|
128
128
|
// Deliberately a SEPARATE section: these apply only to part of the tree and
|
|
129
129
|
// must not be mistaken for project-wide rules.
|
|
130
130
|
for (const f of context.scopedAgents || []) {
|
package/dist/tui/widgets.js
CHANGED
package/llms-full.txt
CHANGED
|
@@ -80,8 +80,8 @@ Key pieces:
|
|
|
80
80
|
- **A persistent per-provider profile** (`~/.zames/profile` for DeepSeek,
|
|
81
81
|
`~/.zames/qwen-profile` for Qwen) keeps you signed in; the headless
|
|
82
82
|
User-Agent is patched so the site's CDN does not 403 the login.
|
|
83
|
-
- **A send throttle** (15 s on DeepSeek,
|
|
84
|
-
|
|
83
|
+
- **A send throttle** (15 s on DeepSeek, 4 s on Qwen, which tolerates a faster
|
|
84
|
+
pace; GLM uses an adaptive pause that starts at 0) keeps the web chat's rate
|
|
85
85
|
limit happy during long tool-heavy runs.
|
|
86
86
|
- **The agent is sandboxed** to the directory it was started in — no tool can
|
|
87
87
|
read or write above it.
|
|
@@ -99,7 +99,8 @@ Key pieces:
|
|
|
99
99
|
|
|
100
100
|
- **Tools like Claude Code / Codex** — `Read`, `Write`, `Edit`, `Bash`,
|
|
101
101
|
`Glob`, `Grep`, plus `MultiEdit`, `ApplyPatch`, `LS`, `TodoWrite`, git and web
|
|
102
|
-
tools. Every edit is backed by `/undo
|
|
102
|
+
tools. Every edit is backed by `/undo`, and `/rewind` restores the whole
|
|
103
|
+
working tree from a checkpoint.
|
|
103
104
|
- **Runs while you keep typing** — queue messages during a task; they run right
|
|
104
105
|
after it, in the same chat (like typing during generation on the web).
|
|
105
106
|
- **Plan mode** — `/plan` (or `--plan`) drops all mutating tools, so the agent
|
|
@@ -132,7 +133,7 @@ Key pieces:
|
|
|
132
133
|
|
|
133
134
|
## Requirements
|
|
134
135
|
|
|
135
|
-
- Node.js >=
|
|
136
|
+
- Node.js >= 22
|
|
136
137
|
- An account on the chat you want to use — **DeepSeek** (`chat.deepseek.com`,
|
|
137
138
|
the default), **Qwen** (`chat.qwen.ai`) or **GLM** (`chat.z.ai`). Qwen also
|
|
138
139
|
works in guest mode for a few messages. On first launch zames asks for your login in the terminal
|
|
@@ -178,8 +179,8 @@ Credentials and toggles can also be edited from `/config`
|
|
|
178
179
|
|
|
179
180
|
## Switching providers (DeepSeek / Qwen / GLM)
|
|
180
181
|
|
|
181
|
-
zames can drive
|
|
182
|
-
**
|
|
182
|
+
zames can drive **DeepSeek** (`chat.deepseek.com`, the default), **Qwen**
|
|
183
|
+
(`chat.qwen.ai`) or **GLM** (`chat.z.ai`):
|
|
183
184
|
|
|
184
185
|
```bash
|
|
185
186
|
/provider qwen # switch to Qwen (persisted) — applied live, no restart
|
|
@@ -193,9 +194,10 @@ zames can drive either **DeepSeek** (`chat.deepseek.com`, the default) or
|
|
|
193
194
|
Notes:
|
|
194
195
|
|
|
195
196
|
- The switch is **live**: zames closes the current backend and opens the other
|
|
196
|
-
one in place, so no restart is needed. The
|
|
197
|
+
one in place, so no restart is needed. The backends use **separate
|
|
197
198
|
persistent profiles** (`~/.zames/profile` for DeepSeek, `~/.zames/qwen-profile`
|
|
198
|
-
for Qwen), so switching never signs the other
|
|
199
|
+
for Qwen, `~/.zames/glm-profile` for GLM), so switching never signs the other
|
|
200
|
+
one out.
|
|
199
201
|
- A chat id belongs to ONE backend, so the new provider opens a **fresh chat**.
|
|
200
202
|
If the previous chat had messages they are summarized and posted as a
|
|
201
203
|
handoff with the next task; an empty chat is switched silently.
|
|
@@ -214,10 +216,10 @@ Notes:
|
|
|
214
216
|
solved programmatically). zames therefore launches GLM headed by default and
|
|
215
217
|
prints a note about it; `--headless` still forces headless (sends will hit
|
|
216
218
|
the captcha). `--headed` is accepted everywhere.
|
|
217
|
-
- **Send telemetry
|
|
219
|
+
- **Send telemetry** (off by default): when enabled, every send is recorded in
|
|
218
220
|
`~/.zames/logs/sends-<provider>.jsonl` (timing, mode flags, the outcome and
|
|
219
|
-
the server error — never the prompt or the answer). Turn it
|
|
220
|
-
`/config set browser.sendTelemetry
|
|
221
|
+
the server error — never the prompt or the answer). Turn it on with
|
|
222
|
+
`/config set browser.sendTelemetry true`. `/limits` shows the recent sends
|
|
221
223
|
and the current pause. GLM additionally uses an **adaptive pause**: it starts
|
|
222
224
|
at 0 and backs off from a REAL rate limit.
|
|
223
225
|
- **Warning**: automating a web chat may violate the service's terms of use and
|
|
@@ -279,11 +281,11 @@ The prompt is a small line editor with persistent history:
|
|
|
279
281
|
Full-screen mode adds dialog navigation, so a long answer can be read and
|
|
280
282
|
selected without leaving the terminal:
|
|
281
283
|
|
|
282
|
-
- `ui.mouseScroll=
|
|
283
|
-
|
|
284
|
-
history is arrow keys only
|
|
285
|
-
- `ui.mouseScroll=
|
|
286
|
-
|
|
284
|
+
- `ui.mouseScroll=true` (default) — the app consumes the wheel and scrolls the
|
|
285
|
+
dialog; selecting text then needs `Shift`+click (the TUI convention). The
|
|
286
|
+
wheel never scrolls the input history — history is arrow keys only.
|
|
287
|
+
- `ui.mouseScroll=false` — the terminal owns the mouse again: normal text
|
|
288
|
+
selection works and the wheel is inert.
|
|
287
289
|
- `PgUp` / `PgDn` — move up/down the dialog half a screen.
|
|
288
290
|
- `Home` / `End` — jump to the very top / bottom of the dialog (only while the
|
|
289
291
|
input line is empty; with text typed they move the caret as usual).
|
|
@@ -320,8 +322,11 @@ instead of staying silent.
|
|
|
320
322
|
zames --task <task text>
|
|
321
323
|
zames --chat <id>
|
|
322
324
|
zames --new-chat
|
|
325
|
+
zames --resume-last
|
|
323
326
|
zames --resend-prompt
|
|
324
327
|
zames --dir <path>
|
|
328
|
+
zames --provider <deepseek|qwen|glm>
|
|
329
|
+
zames --max-iter <n>
|
|
325
330
|
zames --headless
|
|
326
331
|
zames --headed
|
|
327
332
|
zames --plan
|
|
@@ -352,15 +357,19 @@ zames --task "..." --output-format jsonl \
|
|
|
352
357
|
|
|
353
358
|
The agent has the same style of tools as Claude Code / Codex CLI:
|
|
354
359
|
|
|
355
|
-
- **File tools** — `Read`, `Write`, `Edit`, `Bash`, `
|
|
356
|
-
returns raw content by default; pass `numbered=true` to get
|
|
357
|
-
line numbers (for reference only — do not paste them into
|
|
360
|
+
- **File tools** — `Read`, `Write`, `Edit`, `Bash`, `BashOutput`, `Glob`,
|
|
361
|
+
`Grep`. `Read` returns raw content by default; pass `numbered=true` to get
|
|
362
|
+
`cat -n`-style line numbers (for reference only — do not paste them into
|
|
363
|
+
`Edit`). `Bash` takes `run_in_background`; `BashOutput` then polls or kills
|
|
364
|
+
that process.
|
|
358
365
|
- **Extra tools** — `LS` (list a directory), `MultiEdit` (several edits to one
|
|
359
366
|
file applied atomically), `TodoWrite` (session task checklist), `ApplyPatch`
|
|
360
367
|
(multi-file patch in Codex's V4A format: `*** Begin Patch` … `*** End Patch`).
|
|
361
368
|
- **Git** — `GitStatus`, `GitDiff`, `GitLog`, `GitShow`, `GitBranchList`,
|
|
362
369
|
`GitAdd`, `GitCommit`, `GitPush`.
|
|
363
370
|
- **Web** — `WebFetch`, `WebSearch`.
|
|
371
|
+
- **Operator** — `AskUser` (a modal question with options; skipped in plan
|
|
372
|
+
mode so a plan is never interrupted).
|
|
364
373
|
- **Service** — `respond` (final answer to the operator, ends the task).
|
|
365
374
|
|
|
366
375
|
All file tools stay inside the working directory (sandbox). `Write`/`Edit` and
|
|
@@ -368,11 +377,12 @@ All file tools stay inside the working directory (sandbox). `Write`/`Edit` and
|
|
|
368
377
|
|
|
369
378
|
## Slash commands
|
|
370
379
|
|
|
371
|
-
Type / in the prompt for hints (Tab completes)
|
|
372
|
-
|
|
373
|
-
/
|
|
374
|
-
/
|
|
375
|
-
|
|
380
|
+
Type / in the prompt for hints (Tab completes) — that list is always complete,
|
|
381
|
+
`/help <command>` describes any single one. Besides the session and config
|
|
382
|
+
commands (/new or /clear, /sessions, /chats, /resume-id, /last, /chat, /cd,
|
|
383
|
+
/pwd, /status, /config, /undo, /undo-list, /rewind, /rewind-list, /transcript,
|
|
384
|
+
/tasks, /mcp, /skills, /memory, /remember, /init, /reload, /debug-dom, /help,
|
|
385
|
+
/exit) there are a few that mirror Claude Code / Codex CLI:
|
|
376
386
|
|
|
377
387
|
- /diff [--staged] — show the working-tree git diff (--staged for the index).
|
|
378
388
|
- /diffstat — a one-line change summary (`git diff --stat`).
|
|
@@ -434,11 +444,12 @@ tests`). /cron "<min> <hour> <dom> <month> <dow>" <task> — run on a schedule.
|
|
|
434
444
|
current task.
|
|
435
445
|
|
|
436
446
|
The token context is also shown live: the status line above the input has the
|
|
437
|
-
spinner/text on the left and the context on the right (e.g. `125k · 13
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
447
|
+
spinner/text on the left and the context on the right (e.g. `125k · 13%`). The
|
|
448
|
+
percentage is of the ACTIVE provider's window — 1M for DeepSeek, a per-model
|
|
449
|
+
value for Qwen and GLM (`qwen.contextLimit` / `glm.contextLimit`) — and it is
|
|
450
|
+
COLORED by fill level: green below 50%, yellow 50-80%, red above 80%.
|
|
451
|
+
It comes from the provider's own token counter (DeepSeek's
|
|
452
|
+
`accumulated_token_usage`, Qwen's `input_tokens`, GLM's `prompt_tokens`) and is
|
|
442
453
|
hidden until the first answer delivers it.
|
|
443
454
|
|
|
444
455
|
## Project context, skills and memory
|
|
@@ -571,8 +582,9 @@ Examples:
|
|
|
571
582
|
/config lang en
|
|
572
583
|
```
|
|
573
584
|
|
|
574
|
-
Changes are written to the
|
|
575
|
-
|
|
585
|
+
Changes are written to the HOME config (`~/.zames/config.json`), NOT to the
|
|
586
|
+
project `.zamesrc.json` — a personal toggle must not leak into git — and applied
|
|
587
|
+
right away (where possible without a restart).
|
|
576
588
|
|
|
577
589
|
### Language
|
|
578
590
|
|
|
@@ -629,7 +641,7 @@ signed-in browser profile.
|
|
|
629
641
|
**How do I install a terminal AI coding agent?**
|
|
630
642
|
|
|
631
643
|
```bash
|
|
632
|
-
npm install -g zames_pro # Node.js >=
|
|
644
|
+
npm install -g zames_pro # Node.js >= 22; Chromium is fetched automatically
|
|
633
645
|
cd your-project
|
|
634
646
|
zames
|
|
635
647
|
```
|
package/llms.txt
CHANGED
|
@@ -20,7 +20,7 @@ LLM-friendly map of the project. Everything below is plain Markdown.
|
|
|
20
20
|
- **Supported backends:** DeepSeek (`chat.deepseek.com`, default), Qwen
|
|
21
21
|
(`chat.qwen.ai`), GLM (`chat.z.ai`). Live switch with
|
|
22
22
|
`/provider <deepseek|qwen|glm>`; each backend keeps its own browser profile.
|
|
23
|
-
- **Language / stack:** TypeScript (strict), Node.js >=
|
|
23
|
+
- **Language / stack:** TypeScript (strict), Node.js >= 22, Playwright,
|
|
24
24
|
Ink/React TUI, MCP client.
|
|
25
25
|
- **License:** MIT. **Not affiliated** with DeepSeek, Alibaba or Zhipu AI.
|
|
26
26
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.82.
|
|
3
|
+
"version": "2.82.139",
|
|
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": {
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// Post-install step: fetch the Chromium build Playwright drives and, on a
|
|
2
|
+
// Linux box that needs it, its system libraries. It runs BEFORE tsx is
|
|
3
|
+
// guaranteed to be installed, so it stays a plain .mjs script. Every failure
|
|
4
|
+
// is a WARNING, never a non-zero exit: a missing browser must not break
|
|
5
|
+
// `npm install`, and the warning names the manual command to run instead.
|
|
1
6
|
import { spawnSync } from 'node:child_process'
|
|
2
7
|
|
|
3
8
|
const NL = String.fromCharCode(10)
|