zames_pro 2.82.54 → 2.82.56
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/deepseek-ui.js +1 -1
- package/dist/input/layout.js +67 -18
- package/dist/tool-summary.js +5 -0
- package/dist/tools.js +24 -1
- package/dist/xml-toolcall.js +4 -1
- package/package.json +1 -1
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.82.56] - 2026-10-23
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **tool-summary:** mark an MCP failure as an error, not a success
|
|
15
|
+
- **tools:** answer Read failures in the tool's own words
|
|
16
|
+
- **paste:** never swap the bodies of two same-size pastes
|
|
17
|
+
|
|
18
|
+
## [2.82.55] - 2026-10-23
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **deepseek:** require a boundary around HTTP status codes in the busy matcher
|
|
23
|
+
- **toolcall:** require an attribute boundary before the tool name
|
|
24
|
+
|
|
10
25
|
## [2.82.54] - 2026-10-23
|
|
11
26
|
|
|
12
27
|
### Fixed
|
package/dist/deepseek-ui.js
CHANGED
|
@@ -242,7 +242,7 @@ export const RATE_LIMIT_RE = /(messages? too frequent|too many requests|rate lim
|
|
|
242
242
|
// Transient server-side hiccups (DeepSeek overloaded / hiccup). Unlike the
|
|
243
243
|
// rate limit, these usually clear in seconds, so we retry quickly instead of
|
|
244
244
|
// waiting minutes.
|
|
245
|
-
export const SERVER_BUSY_RE = /(server (is )?busy|server error|service (is )?unavailable|temporarily unavailable|internal server error|
|
|
245
|
+
export const SERVER_BUSY_RE = /(server (is )?busy|server error|service (is )?unavailable|temporarily unavailable|internal server error|\b50[234]\b|server overloaded|сервер занят|сервер перегружен|сервис недоступен|внутренняя ошибка|попробуйте позже)/i;
|
|
246
246
|
// The chat's CONTEXT WINDOW is exhausted. DeepSeek refuses to continue the
|
|
247
247
|
// conversation and answers with a non-retryable notice instead of a model
|
|
248
248
|
// turn: "Length limit reached. Please start a new chat." (the Russian UI:
|
package/dist/input/layout.js
CHANGED
|
@@ -64,28 +64,77 @@ export function pasteReplacement(raw) {
|
|
|
64
64
|
return null;
|
|
65
65
|
return { marker: formatPasteMarker(lines), text };
|
|
66
66
|
}
|
|
67
|
-
// Replace "[Pasted lines#N]" markers with the original pasted text.
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
|
|
67
|
+
// Replace "[Pasted lines#N]" markers with the original pasted text.
|
|
68
|
+
//
|
|
69
|
+
// The marker carries ONLY the line count, so two pastes of the same length are
|
|
70
|
+
// indistinguishable in the buffer. The old "last occurrence for the last paste"
|
|
71
|
+
// walk handed the SURVIVING marker the wrong body whenever the operator deleted
|
|
72
|
+
// the other one. The caller can also name the paste it deleted
|
|
73
|
+
// (`deletedIndex`), which removes the ambiguity completely; without it, an
|
|
74
|
+
// ambiguous case (two same-size pastes, exactly one marker left) cannot be
|
|
75
|
+
// resolved from the text, so the marker is left AS IS — sending the literal
|
|
76
|
+
// "[Pasted lines#3]" is visible to the operator, while the wrong body under a
|
|
77
|
+
// plausible marker silently sends the model someone else's text.
|
|
78
|
+
export function expandPastes(pastes, text, deletedIndex) {
|
|
76
79
|
if (!pastes.length)
|
|
77
80
|
return text;
|
|
78
|
-
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
const src = String(text);
|
|
82
|
+
// Without a deletion hint, expansion is only unambiguous when the number of
|
|
83
|
+
// surviving markers matches the number of pastes (nothing was deleted) or
|
|
84
|
+
// the markers differ (different line counts).
|
|
85
|
+
const occurrences = new Map();
|
|
86
|
+
for (const p of pastes) {
|
|
87
|
+
occurrences.set(p.marker, (occurrences.get(p.marker) || 0) + 1);
|
|
88
|
+
}
|
|
89
|
+
let ambiguous = false;
|
|
90
|
+
for (const [marker, count] of occurrences) {
|
|
91
|
+
let seen = 0;
|
|
92
|
+
let from = 0;
|
|
93
|
+
while (true) {
|
|
94
|
+
const idx = src.indexOf(marker, from);
|
|
95
|
+
if (idx === -1)
|
|
96
|
+
break;
|
|
97
|
+
seen++;
|
|
98
|
+
from = idx + marker.length;
|
|
99
|
+
}
|
|
100
|
+
if (seen === count)
|
|
101
|
+
continue;
|
|
102
|
+
// Fewer markers than pastes means one was deleted; more than one survivor
|
|
103
|
+
// of the same text cannot be attributed.
|
|
104
|
+
if (seen > 1 || count > 1)
|
|
105
|
+
ambiguous = true;
|
|
106
|
+
}
|
|
107
|
+
if (deletedIndex !== undefined)
|
|
108
|
+
ambiguous = false;
|
|
109
|
+
// With a KNOWN deletion index the mapping is exact: the remaining pastes in
|
|
110
|
+
// order own the remaining markers in order.
|
|
111
|
+
const pool = deletedIndex !== undefined &&
|
|
112
|
+
deletedIndex >= 0 &&
|
|
113
|
+
deletedIndex < pastes.length
|
|
114
|
+
? pastes.filter((_p, i) => i !== deletedIndex)
|
|
115
|
+
: pastes;
|
|
116
|
+
let out = src;
|
|
117
|
+
let scanFrom = 0;
|
|
118
|
+
const positions = [];
|
|
119
|
+
for (const p of pool) {
|
|
120
|
+
const idx = out.indexOf(p.marker, scanFrom);
|
|
87
121
|
if (idx === -1)
|
|
88
122
|
continue;
|
|
123
|
+
positions.push({ p, idx });
|
|
124
|
+
scanFrom = idx + p.marker.length;
|
|
125
|
+
}
|
|
126
|
+
const survivors = pool.length;
|
|
127
|
+
const total = pastes.length;
|
|
128
|
+
const lost = total - pool.length;
|
|
129
|
+
const found = positions.length;
|
|
130
|
+
// Every marker left in the text must be accounted for, and every unmatched
|
|
131
|
+
// paste must have actually lost its marker. Anything else is ambiguous.
|
|
132
|
+
if (!ambiguous && (found !== survivors || lost < 0))
|
|
133
|
+
ambiguous = true;
|
|
134
|
+
if (ambiguous)
|
|
135
|
+
return src;
|
|
136
|
+
for (let i = positions.length - 1; i >= 0; i--) {
|
|
137
|
+
const { p, idx } = positions[i];
|
|
89
138
|
out = out.slice(0, idx) + p.text + out.slice(idx + p.marker.length);
|
|
90
139
|
}
|
|
91
140
|
return out;
|
package/dist/tool-summary.js
CHANGED
|
@@ -97,6 +97,11 @@ export function looksLikeToolError(result) {
|
|
|
97
97
|
head.startsWith('blocked by') ||
|
|
98
98
|
head.startsWith('denied by') ||
|
|
99
99
|
head.startsWith('unknown tool') ||
|
|
100
|
+
// An MCP failure is returned AS the result (see renderMcpResult):
|
|
101
|
+
// `MCP error: …` for an isError answer, `MCP error (server/tool): …`
|
|
102
|
+
// when the call throws. Neither was recognised, so a failed MCP call was
|
|
103
|
+
// drawn with a green check.
|
|
104
|
+
head.startsWith('mcp error') ||
|
|
100
105
|
head.startsWith('subagents are not available') ||
|
|
101
106
|
head.startsWith('subagent failed') ||
|
|
102
107
|
head.startsWith('subagent error') ||
|
package/dist/tools.js
CHANGED
|
@@ -107,7 +107,30 @@ export function createTools(workdir, { undo, todos, readOnly = false, subagents
|
|
|
107
107
|
// Binary guard (N35): reading an image/archive as utf-8 produced
|
|
108
108
|
// mojibake the model tried to reason about. Detect a binary buffer
|
|
109
109
|
// BEFORE decoding and answer with useful metadata instead.
|
|
110
|
-
|
|
110
|
+
// A read FAILURE (a missing file, a directory, EACCES) is answered in
|
|
111
|
+
// the tool's own words: the raw Node error carries the ABSOLUTE server
|
|
112
|
+
// path and a POSIX-only message, and the loop would wrap it as
|
|
113
|
+
// `Error: ENOENT: …`, which tells the model nothing actionable.
|
|
114
|
+
let buf;
|
|
115
|
+
try {
|
|
116
|
+
buf = await fs.readFile(file);
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
const code = e.code;
|
|
120
|
+
if (code === 'ENOENT') {
|
|
121
|
+
return ('File not found: ' +
|
|
122
|
+
p +
|
|
123
|
+
' (check the path; it must be inside the working directory)');
|
|
124
|
+
}
|
|
125
|
+
if (code === 'EISDIR') {
|
|
126
|
+
return (p +
|
|
127
|
+
' is a directory, not a file. Use LS or Glob to list its contents.');
|
|
128
|
+
}
|
|
129
|
+
if (code === 'EACCES' || code === 'EPERM') {
|
|
130
|
+
return 'Permission denied reading ' + p;
|
|
131
|
+
}
|
|
132
|
+
return 'Error reading ' + p + ': ' + e.message;
|
|
133
|
+
}
|
|
111
134
|
if (isImageName(file)) {
|
|
112
135
|
return ('[image: ' +
|
|
113
136
|
p +
|
package/dist/xml-toolcall.js
CHANGED
|
@@ -14,7 +14,10 @@ function unescapeXml(s) {
|
|
|
14
14
|
}
|
|
15
15
|
function readAttr(attrs, name) {
|
|
16
16
|
const Q = String.fromCharCode(34);
|
|
17
|
-
|
|
17
|
+
// A boundary BEFORE the name: without it `name` matched the TAIL of
|
|
18
|
+
// `tool_name="Bash"`, so an invoke tag that restated the tool was dispatched
|
|
19
|
+
// to `Bash` and the real `name="Write"` was ignored.
|
|
20
|
+
const re = new RegExp('(?:^|[\\s])' + name + '[ ]*=[ ]*([' + Q + ']([^' + Q + ']*)[' + Q + '])');
|
|
18
21
|
const m = attrs.match(re);
|
|
19
22
|
return m ? m[2] : null;
|
|
20
23
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.82.
|
|
3
|
+
"version": "2.82.56",
|
|
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": {
|