zames_pro 2.95.1 → 2.95.3
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 +15 -0
- package/dist/browser.js +42 -7
- package/dist/net-capture.js +10 -7
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.95.3] - 2026-11-04
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **deepseek:** an appended answer is not a dead turn
|
|
15
|
+
- slow start
|
|
16
|
+
|
|
17
|
+
## [2.95.2] - 2026-11-04
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- fix-deepseek-head-only
|
|
22
|
+
- test: pin the streamed-answer head snapshot (the resend loop)
|
|
23
|
+
- docs: theme gallery, mobile menu and code copy buttons
|
|
24
|
+
|
|
10
25
|
## [2.95.1] - 2026-11-03
|
|
11
26
|
|
|
12
27
|
### Fixed
|
package/dist/browser.js
CHANGED
|
@@ -510,6 +510,12 @@ export class DeepSeekBrowser {
|
|
|
510
510
|
this._lastSentAt = Date.now();
|
|
511
511
|
}
|
|
512
512
|
this._netBody = body;
|
|
513
|
+
// The answer clock drives the "is this body FRESH?" gate in both
|
|
514
|
+
// ask loops. A body that carries a (head) fragment but no decodable
|
|
515
|
+
// answer text left the clock at 0, so every detector below saw a
|
|
516
|
+
// stale body and stayed silent. Give it a fresh timestamp.
|
|
517
|
+
if (!this._netCaptureAt)
|
|
518
|
+
this._netCaptureAt = Date.now();
|
|
513
519
|
const cid = url.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
|
|
514
520
|
if (cid)
|
|
515
521
|
this._netChatId = cid[0];
|
|
@@ -2291,11 +2297,15 @@ export class DeepSeekBrowser {
|
|
|
2291
2297
|
// Wait for the start: either Stop appeared, or the answer text changed,
|
|
2292
2298
|
// or the page grew (a new answer node / longer last answer). In parallel we
|
|
2293
2299
|
// catch the rate-limit toast (only toasts, not the whole body).
|
|
2294
|
-
//
|
|
2295
|
-
//
|
|
2296
|
-
//
|
|
2297
|
-
//
|
|
2298
|
-
|
|
2300
|
+
//
|
|
2301
|
+
// MEASURED on 90 live turns: the server needs a MEDIAN of ~16s to deliver a
|
|
2302
|
+
// completion body (min 11.7s, max 25.8s), so the old 12s window was shorter
|
|
2303
|
+
// than a NORMAL turn. The loop then declared "the send did not go out" and
|
|
2304
|
+
// pressed Enter AGAIN — a DUPLICATE turn that ended in "Messages too
|
|
2305
|
+
// frequent". The window only has to cover the SEND (the input clearing, the
|
|
2306
|
+
// Stop button appearing), not the answer, so 30s is generous without
|
|
2307
|
+
// delaying a real miss by minutes.
|
|
2308
|
+
const startDeadline = Date.now() + 30_000;
|
|
2299
2309
|
// A CHEAP growth signal (message-node count + last-answer length)
|
|
2300
2310
|
// instead of document.body.innerText.length, which walked the whole page.
|
|
2301
2311
|
const startSignal = await this._chatSignal();
|
|
@@ -2308,6 +2318,7 @@ export class DeepSeekBrowser {
|
|
|
2308
2318
|
// the text has been stable for a couple of ticks.
|
|
2309
2319
|
let settledTicks = 0;
|
|
2310
2320
|
let lastStartCur = '';
|
|
2321
|
+
let stopTicks = 0;
|
|
2311
2322
|
while (Date.now() < startDeadline) {
|
|
2312
2323
|
if (this._abort || this._askCancelled)
|
|
2313
2324
|
return '(прервано пользователем)';
|
|
@@ -2435,7 +2446,20 @@ export class DeepSeekBrowser {
|
|
|
2435
2446
|
// 'changed' can stay false for a while — without this check ask() used
|
|
2436
2447
|
// to hang until the full timeout and the agent appeared to "stop".
|
|
2437
2448
|
const netStarted = !!this._netCapture && this._netCaptureAt >= this._lastSentAt;
|
|
2438
|
-
|
|
2449
|
+
// The Stop button is the SERVER's own "a turn is running" signal, and it
|
|
2450
|
+
// appears long before the first token reaches the DOM or the completion
|
|
2451
|
+
// body is complete (measured: the body lands a median 16s after the
|
|
2452
|
+
// send, while Stop shows up within a second). Without it a SLOW network
|
|
2453
|
+
// looked like a failed send: the loop hit the start deadline and pressed
|
|
2454
|
+
// Enter again, producing a duplicate turn. A lingering Stop from the
|
|
2455
|
+
// previous generation is ruled out by requiring TWO consecutive ticks
|
|
2456
|
+
// with the button visible, so a single stray paint does not start us.
|
|
2457
|
+
const stopVisible = await this._isGenerating();
|
|
2458
|
+
if (stopVisible)
|
|
2459
|
+
stopTicks++;
|
|
2460
|
+
else
|
|
2461
|
+
stopTicks = 0;
|
|
2462
|
+
if (changed || netStarted || grew || stopTicks >= 2) {
|
|
2439
2463
|
started = true;
|
|
2440
2464
|
this._askDebug('STARTED changed=' +
|
|
2441
2465
|
changed +
|
|
@@ -2631,6 +2655,15 @@ export class DeepSeekBrowser {
|
|
|
2631
2655
|
const noAnswer = !doneAnswer &&
|
|
2632
2656
|
this._netNoAnswer &&
|
|
2633
2657
|
this._netCaptureAt >= this._lastSentAt;
|
|
2658
|
+
// A HEAD-ONLY snapshot is NOT a dead turn: DeepSeek opens a turn
|
|
2659
|
+
// with a RESPONSE fragment that holds only newlines, and the text
|
|
2660
|
+
// arrives in the following bare `data: {"v":...}` chunks.
|
|
2661
|
+
// `_netBody` is the body of the LAST network event, so a poll that
|
|
2662
|
+
// lands between the snapshot and the first chunk sees FINISHED with
|
|
2663
|
+
// no answer, and the old code threw at once (the operator saw
|
|
2664
|
+
// "DeepSeek оборвал ответ") while the chat was still streaming.
|
|
2665
|
+
const headOnly = this._netCaptureAt >= this._lastSentAt &&
|
|
2666
|
+
isFinishedAwaitingAnswer(this._netBody);
|
|
2634
2667
|
if ((truncated || noAnswer) && this.autoContinue) {
|
|
2635
2668
|
// MEASURED: a COMPLETE DSML tool call ends with the event `close`
|
|
2636
2669
|
// and `{click_behavior:'none', auto_resume:false}`, yet the DOM may
|
|
@@ -2660,7 +2693,9 @@ export class DeepSeekBrowser {
|
|
|
2660
2693
|
// autoContinue is off. Do NOT wait out the whole timeout — the turn is
|
|
2661
2694
|
// dead (truncated / finished with no answer), so tell ask() to RESEND
|
|
2662
2695
|
// the prompt right away (that is the only remaining way to continue).
|
|
2663
|
-
|
|
2696
|
+
// EXCEPT a head-only snapshot: the rest of the answer is still on
|
|
2697
|
+
// its way (see `headOnly`), so keep waiting for the append chunks.
|
|
2698
|
+
if ((truncated || noAnswer) && !headOnly) {
|
|
2664
2699
|
throw new GenerationIncompleteError((this._netBody || 'finished without answer').slice(-300));
|
|
2665
2700
|
}
|
|
2666
2701
|
// Reasoning-mode pause: DeepSeek caps the THINK phase and shows a
|
package/dist/net-capture.js
CHANGED
|
@@ -257,13 +257,16 @@ export function isFinishedWithoutAnswer(body) {
|
|
|
257
257
|
const finished = FINISHED_STATUS_RE.test(b);
|
|
258
258
|
if (!finished)
|
|
259
259
|
return false;
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
|
|
260
|
+
// MEASURED live: `responseContentLength` only sees the SNAPSHOT fragment
|
|
261
|
+
// (`v.response.fragments[]`), while DeepSeek appends the REAL answer through
|
|
262
|
+
// bare `data: {"v":...}` chunks. A live body with a `\n\n` snapshot and
|
|
263
|
+
// 1121 chars of appended text measured 2 chars, so 18 of 71 healthy bodies
|
|
264
|
+
// were classified as "finished WITHOUT an answer" — the finish loop then
|
|
265
|
+
// threw `GenerationIncompleteError` (no Continue button), ask() resent the
|
|
266
|
+
// task and the operator saw "DeepSeek оборвал ответ" on a perfectly good
|
|
267
|
+
// turn. The DECODED answer is the truth: use the real extractor.
|
|
268
|
+
const text = extractAnswer(b).trim();
|
|
269
|
+
return text === '' || TRUNCATED_HEAD_RE.test(text);
|
|
267
270
|
}
|
|
268
271
|
/**
|
|
269
272
|
* True when a FINISHED body still carries NO complete answer — its only
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.95.
|
|
3
|
+
"version": "2.95.3",
|
|
4
4
|
"description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen or GLM — 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": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"start": "node ./dist/index.js",
|
|
23
|
-
"dev": "ZAMES_NET_DEBUG=1 tsx ./src/index.ts --dev --headed",
|
|
23
|
+
"dev": "ZAMES_NET_DEBUG=1 ZAMES_ASK_DEBUG=1 tsx ./src/index.ts --dev --headed",
|
|
24
24
|
"clean": "tsx scripts/clean-dist.mts",
|
|
25
25
|
"build": "tsx scripts/clean-dist.mts && tsc -p tsconfig.build.json",
|
|
26
26
|
"coverage:gate": "tsx scripts/coverage-gate.mts",
|