zames_pro 2.82.49 → 2.82.51
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 +60 -0
- package/README.md +159 -26
- package/SECURITY.md +50 -0
- package/dist/agent-loop.js +35 -5
- package/dist/answer-parse.js +1 -1
- package/dist/attach-flow.js +7 -3
- package/dist/attach-refs.js +30 -6
- package/dist/attachments.js +9 -0
- package/dist/browser-chats.js +9 -2
- package/dist/browser-dom.js +1 -1
- package/dist/browser-pure.js +9 -3
- package/dist/browser.js +1 -1
- package/dist/changelog.js +1 -1
- package/dist/checkpoint.js +1 -1
- package/dist/cli-args.js +1 -1
- package/dist/config-command.js +1 -1
- package/dist/config-commands.js +5 -2
- package/dist/config-live.js +10 -0
- package/dist/config.js +13 -1
- package/dist/context-command.js +6 -0
- package/dist/context.js +27 -4
- package/dist/diff.js +52 -9
- package/dist/extraTools.js +7 -0
- package/dist/glm-chats.js +18 -1
- package/dist/glm-net-capture.js +23 -5
- package/dist/goal.js +6 -1
- package/dist/i18n/deepseek.js +3 -3
- package/dist/i18n/self-review.js +4 -0
- package/dist/i18n.js +8 -2
- package/dist/index.js +90 -101
- package/dist/input/layout.js +33 -8
- package/dist/input/suggest.js +1 -1
- package/dist/input.js +1 -1
- package/dist/limits-command.js +42 -0
- package/dist/mcp.js +23 -4
- package/dist/model-command.js +21 -0
- package/dist/path-token.js +36 -9
- package/dist/permissions.js +32 -3
- package/dist/provider-command.js +29 -0
- package/dist/provider-handoff.js +18 -1
- package/dist/queue-command.js +15 -0
- package/dist/qwen-chats.js +13 -6
- package/dist/qwen-net-capture.js +13 -6
- package/dist/resume-command.js +38 -0
- package/dist/send-telemetry.js +15 -3
- package/dist/send-throttle.js +3 -1
- package/dist/status-command.js +68 -0
- package/dist/subagent.js +5 -12
- package/dist/tool-summary.js +8 -1
- package/dist/tools.js +13 -1
- package/dist/tui/StatusBar.js +1 -1
- package/dist/tui/log-window.js +41 -4
- package/dist/undo.js +11 -1
- package/dist/web.js +16 -6
- package/llms-full.txt +679 -0
- package/llms.txt +102 -0
- package/package.json +84 -23
package/dist/browser-pure.js
CHANGED
|
@@ -14,7 +14,7 @@ export function cleanAnswer(raw) {
|
|
|
14
14
|
return '';
|
|
15
15
|
return raw;
|
|
16
16
|
}
|
|
17
|
-
// Pure, state-free helpers extracted from browser.ts
|
|
17
|
+
// Pure, state-free helpers extracted from browser.ts . Nothing
|
|
18
18
|
// here touches the DeepSeekBrowser instance state, so this module is safe to
|
|
19
19
|
// import anywhere (including hot-reloaded modules) without pulling in the
|
|
20
20
|
// live Playwright context. browser.ts re-exports the public names, so the
|
|
@@ -245,8 +245,14 @@ export function mergeAuthMarker(raw, provider, at) {
|
|
|
245
245
|
providers[k] = v || {};
|
|
246
246
|
}
|
|
247
247
|
else if (j && typeof j === 'object') {
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
// A legacy record carries ONE provider name. Only Qwen and GLM ever wrote
|
|
249
|
+
// one (`{provider:'qwen'|'glm', at}`); a BARE `{at}` was DeepSeek. Treating
|
|
250
|
+
// every non-qwen name as DeepSeek filed a legacy GLM session under
|
|
251
|
+
// `deepseek`, so /doctor reported a DeepSeek session and the GLM one
|
|
252
|
+
// vanished.
|
|
253
|
+
const p = typeof j.provider === 'string' ? j.provider : '';
|
|
254
|
+
if (p === 'qwen' || p === 'glm')
|
|
255
|
+
providers[p] = { at: j.at };
|
|
250
256
|
else if (typeof j.at === 'string')
|
|
251
257
|
providers.deepseek = { at: j.at };
|
|
252
258
|
}
|
package/dist/browser.js
CHANGED
|
@@ -18,7 +18,7 @@ import { translate, DEFAULT_LOCALE } from './i18n.js';
|
|
|
18
18
|
// here preserves the existing public API (test/*.test.ts import them from
|
|
19
19
|
// browser.ts).
|
|
20
20
|
export { STOP_NAME_RE, CONTINUE_NAME_RE };
|
|
21
|
-
// The pure, state-free helpers live in browser-pure.ts
|
|
21
|
+
// The pure, state-free helpers live in browser-pure.ts : they are
|
|
22
22
|
// imported for the class below and RE-EXPORTED here, so the public API of this
|
|
23
23
|
// module is unchanged (tests and other modules import these names from
|
|
24
24
|
// './browser.js').
|
package/dist/changelog.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Conventional-Commits -> CHANGELOG draft
|
|
1
|
+
// Conventional-Commits -> CHANGELOG draft. PURE and
|
|
2
2
|
// unit-tested: the draft is built from git log OUTSIDE the release flow
|
|
3
3
|
// (scripts/changelog-draft.mts), so a misparse never touches CHANGELOG.md.
|
|
4
4
|
// We do NOT auto-write the file: a human (or the agent) reviews the draft
|
package/dist/checkpoint.js
CHANGED
|
@@ -4,7 +4,7 @@ import os from 'os';
|
|
|
4
4
|
import { execFile } from 'child_process';
|
|
5
5
|
import { randomBytes } from 'crypto';
|
|
6
6
|
import { writeJsonAtomic } from './fsutil.js';
|
|
7
|
-
// Checkpoints / rewind
|
|
7
|
+
// Checkpoints / rewind .
|
|
8
8
|
//
|
|
9
9
|
// Undo (src/undo.ts) reverts ONE file write. A checkpoint snapshots the WHOLE
|
|
10
10
|
// working tree at the start of a task, so a risky task can be rolled back with
|
package/dist/cli-args.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// PURE CLI argument parsing
|
|
1
|
+
// PURE CLI argument parsing . index.ts used to hold `args` as a
|
|
2
2
|
// module-level constant plus three helpers closing over it, which made the
|
|
3
3
|
// parsing impossible to unit-test in isolation (index.ts is the entry point and
|
|
4
4
|
// is not covered). `createCliArgs(argv)` binds the argv ONCE and returns the
|
package/dist/config-command.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The /config subcommand DISPATCH
|
|
1
|
+
// The /config subcommand DISPATCH . The PURE text rendering lives
|
|
2
2
|
// in config-commands.ts; this module owns the parsing/routing of the text
|
|
3
3
|
// subcommands (list/get/set/reset/lang/path) and prints through injected
|
|
4
4
|
// callbacks. Everything that touches live state (the runtime config object,
|
package/dist/config-commands.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// PURE rendering for the /config command's TEXT output
|
|
1
|
+
// PURE rendering for the /config command's TEXT output . These
|
|
2
2
|
// helpers format the schema into lines; they touch no browser/terminal state,
|
|
3
3
|
// so they are unit-tested without a live agent (the /config dispatch in
|
|
4
4
|
// index.ts only prints what these return). The interactive menu lives in
|
|
@@ -14,7 +14,10 @@ export function configListValue(field, value, t) {
|
|
|
14
14
|
return t('cfg.menu.default');
|
|
15
15
|
if (typeof value === 'boolean')
|
|
16
16
|
return value ? t('common.on') : t('common.off');
|
|
17
|
-
|
|
17
|
+
// Mask a real secret, but NOT a whitespace-only one — the same rule as
|
|
18
|
+
// configGetValue(): a blank password cannot log in, and masking it makes
|
|
19
|
+
// the list claim a password is configured while auto-login silently fails.
|
|
20
|
+
if (/password/i.test(field.path) && String(value).trim().length > 0)
|
|
18
21
|
return '********';
|
|
19
22
|
return JSON.stringify(value);
|
|
20
23
|
}
|
package/dist/config-live.js
CHANGED
|
@@ -31,6 +31,16 @@ export function applyBrowserLiveConfig(path, value, browser) {
|
|
|
31
31
|
if (!field || !(field in browser))
|
|
32
32
|
return false;
|
|
33
33
|
browser[field] = value;
|
|
34
|
+
// `answerTimeoutMs` is DERIVED into the watchdog (`askDeadlineMs` = the
|
|
35
|
+
// answer timeout + 60s) by the Qwen/GLM constructors. Pushing only the field
|
|
36
|
+
// left the deadline at its old value, so a LARGER timeout made the agent
|
|
37
|
+
// give up before the browser's own timeout ever fired. Re-derive it the same
|
|
38
|
+
// way the constructor does.
|
|
39
|
+
if (field === 'answerTimeoutMs' && 'askDeadlineMs' in browser) {
|
|
40
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
41
|
+
browser.askDeadlineMs = value + 60_000;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
34
44
|
return true;
|
|
35
45
|
}
|
|
36
46
|
/** Push undo.* / checkpoint.* changes onto the live stores. The stores copy
|
package/dist/config.js
CHANGED
|
@@ -70,7 +70,7 @@ export const DEFAULTS = {
|
|
|
70
70
|
enabled: true,
|
|
71
71
|
maxBackups: 200,
|
|
72
72
|
},
|
|
73
|
-
//
|
|
73
|
+
// Snapshot the whole working tree at the start of each task so
|
|
74
74
|
// /rewind can roll a risky task back in one step.
|
|
75
75
|
checkpoint: {
|
|
76
76
|
enabled: true,
|
|
@@ -601,11 +601,23 @@ export function getConfigField(path) {
|
|
|
601
601
|
}
|
|
602
602
|
export function getByPath(obj, path) {
|
|
603
603
|
const parts = path.split('.');
|
|
604
|
+
// Same guard as setByPath: a dotted path must never reach the PROTOTYPE
|
|
605
|
+
// chain. `__proto__` used to return Object.prototype (an object), and
|
|
606
|
+
// `constructor` / `toString` the corresponding FUNCTION — the /config list
|
|
607
|
+
// then rendered `{}` for a field whose path did not exist instead of the
|
|
608
|
+
// default marker. Reading those keys is never meaningful for a config path.
|
|
609
|
+
// `toString`, `valueOf`, `hasOwnProperty`, ... are inherited METHODS, not
|
|
610
|
+
// config values: reading them through a dotted path returned a FUNCTION.
|
|
611
|
+
// Only an OWN property counts.
|
|
612
|
+
if (parts.some((p) => UNSAFE_PATH_KEYS.has(p)))
|
|
613
|
+
return undefined;
|
|
604
614
|
let cur = obj;
|
|
605
615
|
for (const p of parts) {
|
|
606
616
|
if (cur === null || cur === undefined || typeof cur !== 'object') {
|
|
607
617
|
return undefined;
|
|
608
618
|
}
|
|
619
|
+
if (!Object.prototype.hasOwnProperty.call(cur, p))
|
|
620
|
+
return undefined;
|
|
609
621
|
cur = cur[p];
|
|
610
622
|
}
|
|
611
623
|
return cur;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { formatContextSources } from './commands.js';
|
|
2
|
+
import { translate } from './i18n.js';
|
|
3
|
+
/** Render the `/context` report for already-loaded sources. */
|
|
4
|
+
export function renderContextReport(src, opts = {}, t = translate('en')) {
|
|
5
|
+
return formatContextSources(src.agents || [], src.memory || [], src.skills || [], src.commands || [], opts, t);
|
|
6
|
+
}
|
package/dist/context.js
CHANGED
|
@@ -186,9 +186,25 @@ function clipIf(name, content, budget, fromTail = false) {
|
|
|
186
186
|
// MEMORY.md is APPENDED to over time, so its newest (most relevant) notes
|
|
187
187
|
// live at the TAIL — a head clip would drop exactly those. Keep the tail
|
|
188
188
|
// for memory files, the head for AGENTS.md (whose top is the summary).
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
//
|
|
190
|
+
// The cut is in UTF-16 units, so it can land BETWEEN the halves of a
|
|
191
|
+
// surrogate PAIR (an emoji / astral CJK char): the head would then end with
|
|
192
|
+
// a lone HIGH half and the tail would START with a lone LOW half. Both reach
|
|
193
|
+
// the model through the system prompt as broken code points (the same rule
|
|
194
|
+
// as truncateToolResult / capReport / cutHookText). Step one unit back for
|
|
195
|
+
// the head, one unit forward for the tail, so the pair is dropped whole.
|
|
196
|
+
if (!fromTail) {
|
|
197
|
+
let end = keep;
|
|
198
|
+
const last = cleaned.charCodeAt(end - 1);
|
|
199
|
+
if (last >= 0xd800 && last <= 0xdbff)
|
|
200
|
+
end--;
|
|
201
|
+
return cleaned.slice(0, end) + NL + NL + marker;
|
|
202
|
+
}
|
|
203
|
+
let start = cleaned.length - keep;
|
|
204
|
+
const first = cleaned.charCodeAt(start);
|
|
205
|
+
if (first >= 0xdc00 && first <= 0xdfff)
|
|
206
|
+
start++;
|
|
207
|
+
let slice = cleaned.slice(start);
|
|
192
208
|
// Cut at a NOTE boundary, not mid-note: start the kept tail at the next line
|
|
193
209
|
// that begins a bullet or a heading, so a half-sentence note is not left.
|
|
194
210
|
const m = slice.search(/\n(?=[-*] |#{1,6} )/);
|
|
@@ -245,7 +261,14 @@ const PROJECT_SKILL_DIRS = [
|
|
|
245
261
|
'skills',
|
|
246
262
|
];
|
|
247
263
|
function parseFrontmatter(raw) {
|
|
248
|
-
|
|
264
|
+
// A leading BOM must be dropped BEFORE the `---` check: Windows editors and
|
|
265
|
+
// generators write one, and without this the frontmatter was not recognised
|
|
266
|
+
// at all (description / argument-hint / arguments silently lost).
|
|
267
|
+
// `skillBody` already strips it; the two must agree.
|
|
268
|
+
const bom = String.fromCharCode(0xfeff);
|
|
269
|
+
const norm = String(raw ?? '')
|
|
270
|
+
.replace(new RegExp('^' + bom), '')
|
|
271
|
+
.replace(CRLF_RE, NL);
|
|
249
272
|
if (!norm.startsWith('---'))
|
|
250
273
|
return {};
|
|
251
274
|
const end = norm.indexOf(NL + '---', 3);
|
package/dist/diff.js
CHANGED
|
@@ -2,22 +2,65 @@ import { theme } from './theme.js';
|
|
|
2
2
|
export function unifiedDiff(a, b, { label = '' } = {}) {
|
|
3
3
|
const la = a.split(String.fromCharCode(10));
|
|
4
4
|
const lb = b.split(String.fromCharCode(10));
|
|
5
|
-
const n = Math.max(la.length, lb.length);
|
|
6
5
|
const out = [];
|
|
7
6
|
if (label)
|
|
8
7
|
out.push('... ' + label);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
// A LINE diff, not a positional compare: an insertion at the top of a
|
|
9
|
+
// file shifts every following line, and comparing line i with line i
|
|
10
|
+
// reported the WHOLE file as removed AND added (a 20-line file with one
|
|
11
|
+
// new first line produced 41 diff lines). The undo/`/diff` preview became
|
|
12
|
+
// unreadable and the model was told everything changed. A longest-common-
|
|
13
|
+
// subsequence walk emits only the lines that really differ.
|
|
14
|
+
for (const op of diffOps(la, lb)) {
|
|
15
|
+
if (op.kind === 'same')
|
|
13
16
|
continue;
|
|
14
|
-
|
|
15
|
-
out.push('-' + x);
|
|
16
|
-
if (y !== undefined)
|
|
17
|
-
out.push('+' + y);
|
|
17
|
+
out.push((op.kind === 'del' ? '-' : '+') + op.line);
|
|
18
18
|
}
|
|
19
19
|
return out.join(String.fromCharCode(10));
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Longest-common-subsequence line diff (Hunt–Szymanski style table, O(n*m)
|
|
23
|
+
* memory). Files here are a handful of lines (a single edit preview), so the
|
|
24
|
+
* simple dynamic-programming table is fine and keeps the module dependency-
|
|
25
|
+
* free. A change pair is emitted as `-old` then `+new`, like a real diff.
|
|
26
|
+
*/
|
|
27
|
+
function diffOps(a, b) {
|
|
28
|
+
const n = a.length;
|
|
29
|
+
const m = b.length;
|
|
30
|
+
// lcs[i][j] = length of the LCS of a[i..] and b[j..].
|
|
31
|
+
const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
32
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
33
|
+
for (let j = m - 1; j >= 0; j--) {
|
|
34
|
+
lcs[i][j] =
|
|
35
|
+
a[i] === b[j]
|
|
36
|
+
? lcs[i + 1][j + 1] + 1
|
|
37
|
+
: Math.max(lcs[i + 1][j], lcs[i][j + 1]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const ops = [];
|
|
41
|
+
let i = 0;
|
|
42
|
+
let j = 0;
|
|
43
|
+
while (i < n && j < m) {
|
|
44
|
+
if (a[i] === b[j]) {
|
|
45
|
+
ops.push({ kind: 'same', line: a[i] });
|
|
46
|
+
i++;
|
|
47
|
+
j++;
|
|
48
|
+
}
|
|
49
|
+
else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
|
|
50
|
+
ops.push({ kind: 'del', line: a[i] });
|
|
51
|
+
i++;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
ops.push({ kind: 'add', line: b[j] });
|
|
55
|
+
j++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
while (i < n)
|
|
59
|
+
ops.push({ kind: 'del', line: a[i++] });
|
|
60
|
+
while (j < m)
|
|
61
|
+
ops.push({ kind: 'add', line: b[j++] });
|
|
62
|
+
return ops;
|
|
63
|
+
}
|
|
21
64
|
export function colorDiff(diffText) {
|
|
22
65
|
return diffText
|
|
23
66
|
.split(String.fromCharCode(10))
|
package/dist/extraTools.js
CHANGED
|
@@ -226,6 +226,13 @@ export function createExtraTools(workdir, { undo, todos } = {}) {
|
|
|
226
226
|
if (v === undefined || v === null) {
|
|
227
227
|
throw new Error(`Missing required argument: ${name}. Add "${name}" to the tool call arguments and retry.`);
|
|
228
228
|
}
|
|
229
|
+
// `path`/`pattern` are strings by contract; an OBJECT used to become
|
|
230
|
+
// "[object Object]" and reach the fs layer as a literal file name.
|
|
231
|
+
if (typeof v === 'object' || typeof v === 'function') {
|
|
232
|
+
throw new Error(`Invalid required argument ${name}: expected a string, got ` +
|
|
233
|
+
JSON.stringify(v) +
|
|
234
|
+
'. Re-send the call with a plain string value.');
|
|
235
|
+
}
|
|
229
236
|
const s = String(v);
|
|
230
237
|
if (s === '' || s === 'undefined' || s === 'null') {
|
|
231
238
|
throw new Error(`Invalid required argument ${name}: ${JSON.stringify(v)}. Provide a non-empty value for "${name}" and retry.`);
|
package/dist/glm-chats.js
CHANGED
|
@@ -157,9 +157,26 @@ export function parseGlmHistory(json) {
|
|
|
157
157
|
: null;
|
|
158
158
|
if (!arr)
|
|
159
159
|
return [];
|
|
160
|
+
// The map is UNORDERED and `timestamp` restores the dialogue order. The
|
|
161
|
+
// value is a NUMBER or a numeric string in the builds seen so far, but an
|
|
162
|
+
// ISO stamp (`2026-10-23T10:00:00Z`) makes `Number(...)` NaN, and a NaN
|
|
163
|
+
// comparator keeps the INSERTION order — a scrambled dialogue. Fall back to
|
|
164
|
+
// Date.parse for a non-numeric string.
|
|
165
|
+
const stampOf = (m) => {
|
|
166
|
+
const raw = m.timestamp;
|
|
167
|
+
const n = Number(raw);
|
|
168
|
+
if (Number.isFinite(n) && n !== 0)
|
|
169
|
+
return n;
|
|
170
|
+
if (typeof raw === 'string') {
|
|
171
|
+
const t = Date.parse(raw);
|
|
172
|
+
if (Number.isFinite(t))
|
|
173
|
+
return t;
|
|
174
|
+
}
|
|
175
|
+
return 0;
|
|
176
|
+
};
|
|
160
177
|
const sorted = arr
|
|
161
178
|
.filter((x) => !!x && typeof x === 'object')
|
|
162
|
-
.sort((a, b) => (
|
|
179
|
+
.sort((a, b) => stampOf(a) - stampOf(b));
|
|
163
180
|
const out = [];
|
|
164
181
|
for (const m of sorted) {
|
|
165
182
|
// The role comes from `role` (confirmed by the live fixture). Only the
|
package/dist/glm-net-capture.js
CHANGED
|
@@ -172,6 +172,10 @@ export function extractAnswer(body) {
|
|
|
172
172
|
return sse;
|
|
173
173
|
return extractFromJson(body);
|
|
174
174
|
}
|
|
175
|
+
/** A usable token count: a FINITE NON-NEGATIVE number. */
|
|
176
|
+
function isUsageCount(v) {
|
|
177
|
+
return typeof v === 'number' && Number.isFinite(v) && v >= 0;
|
|
178
|
+
}
|
|
175
179
|
/**
|
|
176
180
|
* The CONTEXT size (tokens) from `usage.prompt_tokens` - the prompt size for
|
|
177
181
|
* the turn, the same idea as DeepSeek's accumulated_token_usage. Falls back to
|
|
@@ -186,9 +190,14 @@ export function extractTokenUsage(body) {
|
|
|
186
190
|
if (!usage || typeof usage !== 'object')
|
|
187
191
|
continue;
|
|
188
192
|
const u = usage;
|
|
189
|
-
|
|
193
|
+
// A token count is a FINITE NON-NEGATIVE number. A negative payload (a
|
|
194
|
+
// nonsense reply, or a wrapped `int` in a proxy build) used to become
|
|
195
|
+
// `browser._lastTokenUsage` as-is: the context badge disappeared
|
|
196
|
+
// (tokenStatusLevel rejects it) while the auto-compact percentage went
|
|
197
|
+
// negative and never fired. Treat it as "no usage", like a non-number.
|
|
198
|
+
if (isUsageCount(u.prompt_tokens))
|
|
190
199
|
lastPrompt = u.prompt_tokens;
|
|
191
|
-
if (
|
|
200
|
+
if (isUsageCount(u.total_tokens))
|
|
192
201
|
lastTotal = u.total_tokens;
|
|
193
202
|
}
|
|
194
203
|
if (lastPrompt !== null)
|
|
@@ -205,9 +214,9 @@ export function extractTokenUsage(body) {
|
|
|
205
214
|
: null;
|
|
206
215
|
const usage = ((envelope ? envelope.usage : undefined) ?? obj?.usage);
|
|
207
216
|
if (usage) {
|
|
208
|
-
if (
|
|
217
|
+
if (isUsageCount(usage.prompt_tokens))
|
|
209
218
|
return usage.prompt_tokens;
|
|
210
|
-
if (
|
|
219
|
+
if (isUsageCount(usage.total_tokens))
|
|
211
220
|
return usage.total_tokens;
|
|
212
221
|
}
|
|
213
222
|
}
|
|
@@ -279,7 +288,16 @@ const ERROR_PAYLOAD_RE = /"type"\s*:\s*"error"|"error"\s*:|\{\s*"code"\s*:/i;
|
|
|
279
288
|
* message instead of retrying for the whole budget.
|
|
280
289
|
*/
|
|
281
290
|
export function isQuotaExhaustedText(body) {
|
|
282
|
-
|
|
291
|
+
const b = String(body || '');
|
|
292
|
+
if (!b)
|
|
293
|
+
return false;
|
|
294
|
+
// Same guard as every other z.ai classifier: a normal ANSWER may legitimately
|
|
295
|
+
// discuss `insufficient_quota` / "exceeded your current quota" (the operator
|
|
296
|
+
// asked how to handle that API error). Without this the answer was taken for
|
|
297
|
+
// a refusal and a good turn was abandoned with `glm.quota_exhausted`.
|
|
298
|
+
if (!ERROR_PAYLOAD_RE.test(b) && hasAnswerContent(b))
|
|
299
|
+
return false;
|
|
300
|
+
return /insufficient_quota|quota (?:exceeded|exhausted)|exceeded your current quota/i.test(b);
|
|
283
301
|
}
|
|
284
302
|
export function isRateLimitText(body) {
|
|
285
303
|
const b = String(body || '');
|
package/dist/goal.js
CHANGED
|
@@ -96,7 +96,12 @@ export function parseGoalVerdict(text) {
|
|
|
96
96
|
// separator is checked on the character FOLLOWING the matched value (a
|
|
97
97
|
// lookahead INSIDE the value pattern was useless: the value alternatives
|
|
98
98
|
// always end on a word boundary of their own).
|
|
99
|
-
|
|
99
|
+
// A right boundary is REQUIRED: without it `VERDICT: METS` /
|
|
100
|
+
// `METADATA` parsed as MET, and MET CLEARS the goal — an unfinished task
|
|
101
|
+
// looked done. `\b` alone is not enough: in `MET-ish` the hyphen is a word
|
|
102
|
+
// boundary, so a hyphen/underscore directly after the value is rejected
|
|
103
|
+
// explicitly (a hyphen is only legal INSIDE `NOT-MET`).
|
|
104
|
+
const label = /VERDICT\s*[*_`]*\s*:\s*[*_`]*\s*(IMPOSSIBLE|NOT[_ -]?MET|MET)(?![\w-])/gi;
|
|
100
105
|
let m;
|
|
101
106
|
let status = null;
|
|
102
107
|
while ((m = label.exec(t)) !== null) {
|
package/dist/i18n/deepseek.js
CHANGED
|
@@ -96,9 +96,9 @@ export const deepseekMessages = {
|
|
|
96
96
|
ru: '✖ {provider} перестал отвечать, лимит повторов исчерпан. Модель не дала ответа — проверь чат вручную.',
|
|
97
97
|
en: '✖ {provider} stopped responding, retry limit exhausted. The model gave no answer — check the chat manually.',
|
|
98
98
|
},
|
|
99
|
-
'ds.
|
|
100
|
-
ru: '⏳
|
|
101
|
-
en: '⏳ The
|
|
99
|
+
'ds.text_no_tool': {
|
|
100
|
+
ru: '⏳ Модель ответила текстом без вызова инструмента ({attempt}/{max}) — прошу вызвать инструмент...',
|
|
101
|
+
en: '⏳ The model answered with plain text and no tool call ({attempt}/{max}) — asking for a tool call...',
|
|
102
102
|
},
|
|
103
103
|
'ds.not_launched': {
|
|
104
104
|
ru: '✖ Браузер не запущен.',
|
package/dist/i18n/self-review.js
CHANGED
|
@@ -183,4 +183,8 @@ export const selfReviewMessages = {
|
|
|
183
183
|
ru: 'Использование: /resume-id <chat id>',
|
|
184
184
|
en: 'Usage: /resume-id <chat id>',
|
|
185
185
|
},
|
|
186
|
+
'sessions.provider_mismatch': {
|
|
187
|
+
ru: 'Сессия принадлежит провайдеру {owner}, а активен {active}: чат не откроется. Переключись командой /provider {owner} и повтори.',
|
|
188
|
+
en: 'This session belongs to {owner}, but {active} is active: the chat will not open. Switch with /provider {owner} and retry.',
|
|
189
|
+
},
|
|
186
190
|
};
|
package/dist/i18n.js
CHANGED
|
@@ -37,7 +37,7 @@ export function normalizeLocale(v) {
|
|
|
37
37
|
return DEFAULT_LOCALE;
|
|
38
38
|
}
|
|
39
39
|
// String catalog. Key -> { ru, en }. If a key is missing we return the key itself.
|
|
40
|
-
// Split into section files
|
|
40
|
+
// Split into section files; merged here so callers keep importing
|
|
41
41
|
// CATALOG from a single place.
|
|
42
42
|
export const CATALOG = {
|
|
43
43
|
...commonMessages,
|
|
@@ -55,7 +55,13 @@ export const CATALOG = {
|
|
|
55
55
|
export function translate(locale) {
|
|
56
56
|
const loc = isLocale(locale) ? locale : DEFAULT_LOCALE;
|
|
57
57
|
return (key, params) => {
|
|
58
|
-
|
|
58
|
+
// A plain-object lookup ALSO resolves Object.prototype members, so a key
|
|
59
|
+
// like 'toString' returned the inherited FUNCTION: the caller got
|
|
60
|
+
// `undefined` (no params) or a TypeError (`.replace is not a function`).
|
|
61
|
+
// Only an OWN entry is a translation.
|
|
62
|
+
const entry = Object.prototype.hasOwnProperty.call(CATALOG, key)
|
|
63
|
+
? CATALOG[key]
|
|
64
|
+
: undefined;
|
|
59
65
|
const template = entry ? entry[loc] || entry[DEFAULT_LOCALE] : key;
|
|
60
66
|
if (!params)
|
|
61
67
|
return template;
|