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/config.js
CHANGED
|
@@ -611,14 +611,11 @@ export function getConfigField(path) {
|
|
|
611
611
|
}
|
|
612
612
|
export function getByPath(obj, path) {
|
|
613
613
|
const parts = path.split('.');
|
|
614
|
-
//
|
|
615
|
-
// chain
|
|
616
|
-
//
|
|
617
|
-
//
|
|
618
|
-
//
|
|
619
|
-
// `toString`, `valueOf`, `hasOwnProperty`, ... are inherited METHODS, not
|
|
620
|
-
// config values: reading them through a dotted path returned a FUNCTION.
|
|
621
|
-
// Only an OWN property counts.
|
|
614
|
+
// Two guards, both load-bearing. UNSAFE_PATH_KEYS keeps a dotted path off
|
|
615
|
+
// the PROTOTYPE chain (`__proto__` answered Object.prototype, `constructor`
|
|
616
|
+
// a function, and the /config list rendered `{}` instead of the default
|
|
617
|
+
// marker). The hasOwnProperty check keeps inherited METHODS out: `toString`
|
|
618
|
+
// / `valueOf` are not config values, and only an OWN property counts.
|
|
622
619
|
if (parts.some((p) => UNSAFE_PATH_KEYS.has(p)))
|
|
623
620
|
return undefined;
|
|
624
621
|
let cur = obj;
|
package/dist/deepseek-ui.js
CHANGED
|
@@ -45,9 +45,9 @@ export const SEND_SELECTORS = [
|
|
|
45
45
|
'button[aria-label*="отправ" i]',
|
|
46
46
|
];
|
|
47
47
|
// DeepSeek's own button classes. Used as a coarse "is this a DeepSeek button?"
|
|
48
|
-
// filter in the raw-DOM fallbacks for Stop and
|
|
49
|
-
//
|
|
50
|
-
//
|
|
48
|
+
// filter in the raw-DOM fallbacks for Stop and the send/stop icon detection.
|
|
49
|
+
// Passed to page.evaluate as a source string (`.source`), because the in-page
|
|
50
|
+
// callback cannot receive a RegExp object.
|
|
51
51
|
export const BUTTON_CLASS_RE = /ds-button--(circle|primary|filled)/i;
|
|
52
52
|
// ---------- stop ----------
|
|
53
53
|
export const STOP_SELECTORS = [
|
|
@@ -108,10 +108,10 @@ export const MESSAGE_CONTAINER_SELECTORS = [
|
|
|
108
108
|
export const ASSISTANT_CLASS_RE = /ds-assistant-message|assistant-message/i;
|
|
109
109
|
export const ASSISTANT_SELECTOR = '[class*="ds-assistant-message"]';
|
|
110
110
|
export const ANSWER_MARKDOWN_SELECTOR = '[class*="ds-markdown"]';
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
// document.body.innerText.length
|
|
114
|
-
// (sidebar, history, menus) and forces a layout
|
|
111
|
+
// The node/markdown selectors for the cheap "did the page grow?" signal
|
|
112
|
+
// (chatSignalInDom): counting message nodes and measuring the LAST answer's
|
|
113
|
+
// length instead of document.body.innerText.length, which serializes the WHOLE
|
|
114
|
+
// page (sidebar, history, menus) and forces a layout on every tick.
|
|
115
115
|
export const CHAT_SIGNAL_NODES_SELECTOR = '[class*="ds-message"], [class*="chat-message"], .ds-markdown';
|
|
116
116
|
export const MARKDOWN_SELECTOR = '.ds-markdown';
|
|
117
117
|
// ---------- new chat ----------
|
package/dist/diagnostics.js
CHANGED
|
@@ -24,13 +24,11 @@ export function summarizeDiagnostics(raw, maxChars) {
|
|
|
24
24
|
if (!text)
|
|
25
25
|
return null;
|
|
26
26
|
// tsc prints nothing on success, but with `--watch` it prints a banner
|
|
27
|
-
// ("Found 0 errors. Watching for file changes.")
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
// the clean checks: a watch-mode banner is usually coloured and/or
|
|
33
|
-
// prefixed, and the anchored regexes missed it — every edit then
|
|
27
|
+
// ("Found 0 errors. Watching for file changes."). Treat a ZERO-count banner
|
|
28
|
+
// as clean however long it is; a non-zero count still falls through to the
|
|
29
|
+
// lines. The banner is usually coloured and/or prefixed with a
|
|
30
|
+
// `[12:00:00]`-style timestamp, so both are stripped before the clean
|
|
31
|
+
// checks — with them in place the anchored regexes missed it and every edit
|
|
34
32
|
// appended a pointless diagnostics block.
|
|
35
33
|
const plain = text.replace(/\x1b\[[0-9;?]*[A-Za-z]/g, '');
|
|
36
34
|
const cleanCheck = plain.replace(/^\[[^\]]*\]\s*/, '');
|
package/dist/diff.js
CHANGED
|
@@ -5,12 +5,11 @@ export function unifiedDiff(a, b, { label = '' } = {}) {
|
|
|
5
5
|
const out = [];
|
|
6
6
|
if (label)
|
|
7
7
|
out.push('... ' + label);
|
|
8
|
-
// A LINE diff, not a positional compare: an insertion at the top
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// subsequence walk emits only the lines that really differ.
|
|
8
|
+
// A LINE diff, not a positional compare: an insertion at the top shifts
|
|
9
|
+
// every following line, and comparing line i with line i reported the WHOLE
|
|
10
|
+
// file as removed AND added (a 20-line file with one new first line produced
|
|
11
|
+
// 41 diff lines), which made the undo preview unreadable and told the model
|
|
12
|
+
// that everything changed.
|
|
14
13
|
for (const op of diffOps(la, lb)) {
|
|
15
14
|
if (op.kind === 'same')
|
|
16
15
|
continue;
|
|
@@ -19,10 +18,9 @@ export function unifiedDiff(a, b, { label = '' } = {}) {
|
|
|
19
18
|
return out.join(String.fromCharCode(10));
|
|
20
19
|
}
|
|
21
20
|
/**
|
|
22
|
-
* Longest-common-subsequence line diff (
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* free. A change pair is emitted as `-old` then `+new`, like a real diff.
|
|
21
|
+
* Longest-common-subsequence line diff (an O(n*m) dynamic-programming table,
|
|
22
|
+
* which keeps the module dependency-free). A change pair is emitted as
|
|
23
|
+
* `-old` then `+new`, like a real diff.
|
|
26
24
|
*/
|
|
27
25
|
function diffOps(a, b) {
|
|
28
26
|
const n = a.length;
|
package/dist/extraTools.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { safePath } from './sandbox.js';
|
|
4
|
-
// Extra tools
|
|
4
|
+
// Extra tools borrowed from Claude Code / Codex CLI:
|
|
5
5
|
// * LS - list a directory (Claude Code has LS; Codex has list_dir)
|
|
6
6
|
// * MultiEdit - several edits to ONE file applied atomically (Claude Code)
|
|
7
7
|
// * ApplyPatch - multi-file V4A-style patch (Codex apply_patch)
|
|
8
8
|
// * TodoWrite - session task checklist (Claude Code TodoWrite)
|
|
9
9
|
//
|
|
10
|
-
// The core file tools (Read/Write/Edit/Bash/Glob/Grep) live in tools.ts
|
|
11
|
-
//
|
|
10
|
+
// The core file tools (Read/Write/Edit/Bash/Glob/Grep) live in tools.ts; these
|
|
11
|
+
// are additive and do not change their behaviour.
|
|
12
12
|
const NL = String.fromCharCode(10);
|
|
13
13
|
function decodeContent(content, contentBase64) {
|
|
14
14
|
if (typeof contentBase64 === 'string' && contentBase64.length) {
|
package/dist/fsutil.js
CHANGED
|
@@ -6,10 +6,6 @@ import path from 'path';
|
|
|
6
6
|
// truncated/broken file behind. The temp name embeds the pid so two
|
|
7
7
|
// concurrent processes never collide on it.
|
|
8
8
|
//
|
|
9
|
-
// Kept in its own module because the pattern used to be copy-pasted in at
|
|
10
|
-
// least three places; drift between the copies is exactly how one of them
|
|
11
|
-
// silently loses its atomicity.
|
|
12
|
-
//
|
|
13
9
|
// The file is created 0o600: ~/.zames/config.json holds the
|
|
14
10
|
// browser password in clear text alongside sessions/undo history, and the
|
|
15
11
|
// default umask (022) would make them readable by other users on a
|
package/dist/gitTools.js
CHANGED
|
@@ -89,9 +89,9 @@ export async function getGitContext(workdir) {
|
|
|
89
89
|
const remoteR = await runGitArgs(['remote'], workdir, 5000);
|
|
90
90
|
const aheadR = await runGitArgs(['rev-list', '--left-right', '--count', '@{upstream}...HEAD'], workdir, 5000);
|
|
91
91
|
const branch = branchR.trim() || '(detached HEAD)';
|
|
92
|
-
// The
|
|
93
|
-
//
|
|
94
|
-
//
|
|
92
|
+
// The sentinel is an internal string, not a path: kept verbatim it told the
|
|
93
|
+
// model the tree had a modified "file" by that name, so every task started
|
|
94
|
+
// with a false dirty-tree premise.
|
|
95
95
|
const statusText = statusR.trim() === NO_OUTPUT_SENTINEL ? '' : statusR;
|
|
96
96
|
const statusLines = statusText
|
|
97
97
|
.split(String.fromCharCode(10))
|
|
@@ -286,7 +286,8 @@ export function createGitTools(workdir) {
|
|
|
286
286
|
if (!msg.trim()) {
|
|
287
287
|
return 'Error: message is empty.';
|
|
288
288
|
}
|
|
289
|
-
//
|
|
289
|
+
// Stage everything only when the index is empty, which is what the tool
|
|
290
|
+
// description promises the model.
|
|
290
291
|
const staged = await runGitArgs(['diff', '--cached', '--name-only'], workdir, 10_000);
|
|
291
292
|
const stagedTrimmed = staged.trim();
|
|
292
293
|
const hasStaged = stagedTrimmed !== '' && stagedTrimmed !== NO_OUTPUT_SENTINEL;
|
package/dist/glm-browser.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// WHY a separate class (the same reason QwenBrowser exists): the backends
|
|
4
4
|
// differ in EVERY layer we touch — selectors, the send flow, the SSE format and
|
|
5
5
|
// the chat-id scheme. The shared surface is the ChatProvider interface
|
|
6
|
-
// (types.ts); the agent loop only needs BrowserLike and never sees
|
|
7
|
-
//
|
|
6
|
+
// (types.ts); the agent loop only needs BrowserLike and never sees a concrete
|
|
7
|
+
// class.
|
|
8
8
|
//
|
|
9
9
|
// Verified against a LIVE signed-in session (headless + headed Chromium):
|
|
10
10
|
// input -> textarea#chat-input
|
|
@@ -233,7 +233,6 @@ export class GlmBrowser {
|
|
|
233
233
|
sendIntervalMs() {
|
|
234
234
|
return this.sendRuntime.intervalMs();
|
|
235
235
|
}
|
|
236
|
-
// ---------- lifecycle ----------
|
|
237
236
|
async launch() {
|
|
238
237
|
if (await profileLooksLocked(GLM_USER_DATA_DIR)) {
|
|
239
238
|
if (this.debug)
|
|
@@ -398,7 +397,6 @@ export class GlmBrowser {
|
|
|
398
397
|
}
|
|
399
398
|
throw new Error('glm: the browser profile is still locked');
|
|
400
399
|
}
|
|
401
|
-
// ---------- login ----------
|
|
402
400
|
/**
|
|
403
401
|
* A FAST login probe with NO long waits (the old isLoggedIn polled up to
|
|
404
402
|
* 20 s, which is a false "logged out" on a slow network). The signal is the
|
|
@@ -754,7 +752,6 @@ export class GlmBrowser {
|
|
|
754
752
|
}
|
|
755
753
|
catch { }
|
|
756
754
|
}
|
|
757
|
-
// ---------- network capture ----------
|
|
758
755
|
_installNetHook() {
|
|
759
756
|
const pg = this.page;
|
|
760
757
|
if (!pg)
|
|
@@ -857,7 +854,6 @@ export class GlmBrowser {
|
|
|
857
854
|
}
|
|
858
855
|
catch { }
|
|
859
856
|
}
|
|
860
|
-
// ---------- chat management ----------
|
|
861
857
|
async newChat() {
|
|
862
858
|
const before = await this.getCurrentChatId();
|
|
863
859
|
const btn = this.page.locator(NEW_CHAT_SELECTOR).first();
|
|
@@ -1081,7 +1077,6 @@ export class GlmBrowser {
|
|
|
1081
1077
|
getLastTokenUsage() {
|
|
1082
1078
|
return this._lastTokenUsage;
|
|
1083
1079
|
}
|
|
1084
|
-
// ---------- answer reading ----------
|
|
1085
1080
|
async _readLastAnswerText() {
|
|
1086
1081
|
if (this._netCapture && this._netCaptureAt >= this._lastSentAt) {
|
|
1087
1082
|
return this._netCapture;
|
|
@@ -1239,7 +1234,6 @@ export class GlmBrowser {
|
|
|
1239
1234
|
}
|
|
1240
1235
|
catch { }
|
|
1241
1236
|
}
|
|
1242
|
-
// ---------- toggles ----------
|
|
1243
1237
|
getToggleStatesSync() {
|
|
1244
1238
|
return this._toggles;
|
|
1245
1239
|
}
|
|
@@ -1295,7 +1289,6 @@ export class GlmBrowser {
|
|
|
1295
1289
|
this._notice(this._t('glm.mode_mismatch', { v: mismatch.join(', ') }));
|
|
1296
1290
|
}
|
|
1297
1291
|
}
|
|
1298
|
-
// ---------- diagnostics ----------
|
|
1299
1292
|
async dumpDom(filePath) {
|
|
1300
1293
|
if (!this.page)
|
|
1301
1294
|
throw new Error('glm: browser not launched');
|
|
@@ -1332,7 +1325,6 @@ export class GlmBrowser {
|
|
|
1332
1325
|
chatUrl(id) {
|
|
1333
1326
|
return chatUrl(id);
|
|
1334
1327
|
}
|
|
1335
|
-
// ---------- ask ----------
|
|
1336
1328
|
async _waitForSendSlot(agent) {
|
|
1337
1329
|
if (!agent)
|
|
1338
1330
|
return;
|
package/dist/glm-chats.js
CHANGED
|
@@ -78,7 +78,6 @@ export function scrapeGlmChatMessages(opts) {
|
|
|
78
78
|
}
|
|
79
79
|
return out;
|
|
80
80
|
}
|
|
81
|
-
// ---------- API payload parsers (pure, unit-tested) ----------
|
|
82
81
|
/**
|
|
83
82
|
* Parse `GET /api/v1/chats/?page=1`. The live shape is a BARE ARRAY; older /
|
|
84
83
|
* other builds wrap it in `{data:[...]}` or key it by id. Returns [] for
|
package/dist/glm-net-capture.js
CHANGED
|
@@ -280,7 +280,6 @@ function hasAnswerContent(body) {
|
|
|
280
280
|
// ERROR payload (`type: "error"` / a code) or as a bare notice with NO answer
|
|
281
281
|
// content; answer text lives in `phase: "answer"` chunks.
|
|
282
282
|
const ERROR_PAYLOAD_RE = /"type"\s*:\s*"error"|"error"\s*:|\{\s*"code"\s*:/i;
|
|
283
|
-
/** True when the RAW body is z.ai's rate-limit notice. */
|
|
284
283
|
/**
|
|
285
284
|
* True when z.ai answers that the ACCOUNT quota is exhausted
|
|
286
285
|
* (`insufficient_quota`, HTTP 429). Distinct from a transient rate limit:
|
|
@@ -299,6 +298,7 @@ export function isQuotaExhaustedText(body) {
|
|
|
299
298
|
return false;
|
|
300
299
|
return /insufficient_quota|quota (?:exceeded|exhausted)|exceeded your current quota/i.test(b);
|
|
301
300
|
}
|
|
301
|
+
/** True when the RAW body is z.ai's rate-limit notice. */
|
|
302
302
|
export function isRateLimitText(body) {
|
|
303
303
|
const b = String(body || '');
|
|
304
304
|
if (!b)
|
package/dist/glm-ui.js
CHANGED
|
@@ -162,8 +162,8 @@ export const GUEST_LIMIT_RE = /guest.*limit|log ?in to continue|sign ?in to cont
|
|
|
162
162
|
// The completion body carries the mode flags. `preview_mode` and the
|
|
163
163
|
// `general_agent` flag change the ANSWER FORMAT (they enable z.ai's own agent
|
|
164
164
|
// runtime, which emits its own tool calls), and that would break the zames
|
|
165
|
-
// tool-call protocol. The
|
|
166
|
-
//
|
|
165
|
+
// tool-call protocol. The request body is rewritten before the send and then
|
|
166
|
+
// verified (glm-browser's _installRuntimeRewrite/_verifyModes + glm-flags).
|
|
167
167
|
export const SAFE_PREVIEW_MODE = false;
|
|
168
168
|
export const SAFE_ENABLE_THINKING = true;
|
|
169
169
|
// ---------- attachments ----------
|
package/dist/goal.js
CHANGED
|
@@ -69,13 +69,10 @@ export function buildContinuationDirective(reason) {
|
|
|
69
69
|
// Collapse whitespace in the model-written reason: a NEWLINE started a new
|
|
70
70
|
// line of the directive, letting the evaluator's text inject an extra
|
|
71
71
|
// instruction into the next turn.
|
|
72
|
-
(
|
|
72
|
+
(String(reason ?? '').trim()
|
|
73
73
|
? 'Evaluator note: ' + String(reason).trim().replace(/\s+/g, ' ')
|
|
74
74
|
: ''));
|
|
75
75
|
}
|
|
76
|
-
function argumentReason(reason) {
|
|
77
|
-
return String(reason ?? '').trim().length > 0;
|
|
78
|
-
}
|
|
79
76
|
/**
|
|
80
77
|
* The text a verdict may be read from. A plain answer is returned as-is; a
|
|
81
78
|
* TOOL CALL (JSON or DSML/XML) yields only its `respond` message, because the
|
package/dist/i18n/common.js
CHANGED
|
@@ -199,7 +199,7 @@ export const commonMessages = {
|
|
|
199
199
|
ru: '📎 Вложен файл {marker} {name} ({size}) — {path}',
|
|
200
200
|
en: '📎 Attached file {marker} {name} ({size}) — {path}',
|
|
201
201
|
},
|
|
202
|
-
//
|
|
202
|
+
// Shown when `@path` references in a task were inlined as file contents.
|
|
203
203
|
'msg.at_refs': {
|
|
204
204
|
ru: '📄 Встроено файлов по @-ссылкам: {n} —',
|
|
205
205
|
en: '📄 Inlined @-referenced files: {n} —',
|
|
@@ -303,7 +303,7 @@ export const commonMessages = {
|
|
|
303
303
|
},
|
|
304
304
|
'cost.auto_compacts': { ru: ' авто-сжатий: {n}', en: ' auto-compacts: {n}' },
|
|
305
305
|
'cost.by_tool': { ru: ' по инструментам:', en: ' by tool:' },
|
|
306
|
-
//
|
|
306
|
+
// Total time spent inside tools, then a top-N per-tool breakdown.
|
|
307
307
|
'cost.by_time': {
|
|
308
308
|
ru: ' время в инструментах: {dur}; топ по времени:',
|
|
309
309
|
en: ' time in tools: {dur}; top by time:',
|
package/dist/i18n.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Goals:
|
|
4
4
|
// * change the interface language (help, service messages);
|
|
5
|
-
// * the agent's answer language — via the system-prompt
|
|
6
|
-
// * the agent's answer language for a plain language.
|
|
5
|
+
// * the agent's answer language — via the system-prompt.
|
|
7
6
|
//
|
|
8
7
|
// Strategy: a single key->translation map. New keys are easy to add.
|
|
9
8
|
// Russian texts (ru) are the default, English (en) is the alternative.
|
package/dist/index.js
CHANGED
|
@@ -76,7 +76,6 @@ function authMarkerExists(provider = config.provider) {
|
|
|
76
76
|
return false;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
// ---------- CLI parsing ----------
|
|
80
79
|
// The pure helpers live in src/cli-args.ts and are bound to this process's
|
|
81
80
|
// argv ONCE here. Call sites keep using getArg/hasFlag/getPositional.
|
|
82
81
|
const { getArg, hasFlag, getPositional } = createCliArgs(process.argv.slice(2));
|
|
@@ -182,7 +181,6 @@ if (jsonMode) {
|
|
|
182
181
|
console.error(...a);
|
|
183
182
|
});
|
|
184
183
|
}
|
|
185
|
-
// ---------- session persistence ----------
|
|
186
184
|
// We store sessions (DeepSeek chats) in ~/.zames/.sessions so they survive
|
|
187
185
|
// a restart and are available for explicit restore (--resume-last,
|
|
188
186
|
// --chat <id>, /resume-id <id>). They are NOT brought up automatically on launch.
|
|
@@ -231,7 +229,6 @@ async function saveGoal(workdir, goal) {
|
|
|
231
229
|
// best-effort: a failed write must not break the command
|
|
232
230
|
}
|
|
233
231
|
}
|
|
234
|
-
// ---------- hot reload ----------
|
|
235
232
|
// We keep references to the logic modules in the mod object. The /reload
|
|
236
233
|
// command re-reads them via a dynamic import with a timestamp query — Node
|
|
237
234
|
// caches ESM by URL, so such an import returns a fresh version of the module.
|
|
@@ -406,7 +403,6 @@ function warnIfBrowserChanged() {
|
|
|
406
403
|
}
|
|
407
404
|
catch { }
|
|
408
405
|
}
|
|
409
|
-
// ---------- helpers ----------
|
|
410
406
|
function printHelp() {
|
|
411
407
|
// Self-development commands (backlog / self-review) are shown ONLY in dev
|
|
412
408
|
// mode. A regular package install must not advertise them to the user — and
|
|
@@ -689,7 +685,6 @@ async function cleanTmpDir() {
|
|
|
689
685
|
console.error('tmp: cleanup failed:', e.message);
|
|
690
686
|
}
|
|
691
687
|
}
|
|
692
|
-
// ---------- workdir resolution ----------
|
|
693
688
|
// The agent works in the directory it was launched from (process.cwd()).
|
|
694
689
|
// This is the sandbox root: tools cannot go above it.
|
|
695
690
|
async function resolveWorkdir() {
|
|
@@ -701,7 +696,6 @@ async function resolveWorkdir() {
|
|
|
701
696
|
}
|
|
702
697
|
return dir;
|
|
703
698
|
}
|
|
704
|
-
// ---------- restored dialogue ----------
|
|
705
699
|
/**
|
|
706
700
|
* The answer timeout for a one-off provider call (compact summary, etc.).
|
|
707
701
|
* Qwen's cold guest turns are far slower than DeepSeek's, so using
|
|
@@ -1403,7 +1397,6 @@ async function main() {
|
|
|
1403
1397
|
// Timestamp of the last Ctrl+C while the agent was busy. Two presses
|
|
1404
1398
|
// within 2s escalate from "abort the running tool" to "stop the whole run".
|
|
1405
1399
|
let lastCtrlCAt = 0;
|
|
1406
|
-
// ---------- review mode state ----------
|
|
1407
1400
|
// null — normal mode.
|
|
1408
1401
|
// { snapDir, snapName, originalWorkdir } — we're inside a snapshot, the chat
|
|
1409
1402
|
// is already initialized with the review prompt, the user can just type "fix...".
|
|
@@ -1458,7 +1451,6 @@ async function main() {
|
|
|
1458
1451
|
console.error(theme.error(t('msg.open_chat_error', { v: e.message })));
|
|
1459
1452
|
}
|
|
1460
1453
|
}
|
|
1461
|
-
// ---------- input: permanent line below + status above ----------
|
|
1462
1454
|
// In TTY we use LineEditor: it owns the input all the time, shows the
|
|
1463
1455
|
// status above the input line and prints the agent's answers ABOVE it, so
|
|
1464
1456
|
// the typed text is never overwritten by output. In non-TTY (pipe) —
|
|
@@ -1878,7 +1870,6 @@ async function main() {
|
|
|
1878
1870
|
edAny._origLog = origLog;
|
|
1879
1871
|
edAny._origErr = origErr;
|
|
1880
1872
|
}
|
|
1881
|
-
// ---------- /config ----------
|
|
1882
1873
|
// View and edit settings without a restart. Values are validated against
|
|
1883
1874
|
// CONFIG_SCHEMA (src/config.ts) and written to the project .zamesrc.json.
|
|
1884
1875
|
// The language (ui.locale) is applied immediately: we change currentLocale,
|
|
@@ -2407,7 +2398,6 @@ async function main() {
|
|
|
2407
2398
|
}
|
|
2408
2399
|
continue;
|
|
2409
2400
|
}
|
|
2410
|
-
// ---------- Self-review ----------
|
|
2411
2401
|
if (lower === '/self-review' || lower.startsWith('/self-review ')) {
|
|
2412
2402
|
const focus = trimmed.slice('/self-review'.length).trim();
|
|
2413
2403
|
// Remember where to return
|
|
@@ -2571,7 +2561,6 @@ async function main() {
|
|
|
2571
2561
|
}
|
|
2572
2562
|
continue;
|
|
2573
2563
|
}
|
|
2574
|
-
// ---------- Regular commands ----------
|
|
2575
2564
|
if (lower === '/chats' || lower.startsWith('/chats ')) {
|
|
2576
2565
|
const filter = trimmed.slice('/chats'.length).trim().toLowerCase();
|
|
2577
2566
|
const spin = editor || mod.createSpinner(currentLocale);
|
package/dist/input/suggest.js
CHANGED
|
@@ -107,13 +107,10 @@ export function completeSlashCommand(buf, sugg, selected) {
|
|
|
107
107
|
prefix = prefix.slice(0, -1);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
//
|
|
111
|
-
// everything
|
|
112
|
-
// common first letter like "/h")
|
|
113
|
-
// never typed.
|
|
114
|
-
// With an EMPTY buffer everything matches and the shared prefix collapses
|
|
115
|
-
// to a bare "/" (or a common first letter like "/h"): Tab must not guess a
|
|
116
|
-
// command the operator has not started typing.
|
|
110
|
+
// A completion must EXTEND what is already typed: with an empty buffer the
|
|
111
|
+
// list matches everything and the shared prefix used to collapse to a bare
|
|
112
|
+
// "/" (or a common first letter like "/h"), so Tab inserted something the
|
|
113
|
+
// operator never typed.
|
|
117
114
|
if (typed.length < 2)
|
|
118
115
|
return null;
|
|
119
116
|
if (!prefix.toLowerCase().startsWith(typed))
|
package/dist/input.js
CHANGED
|
@@ -96,7 +96,7 @@ export class LineEditor {
|
|
|
96
96
|
// is stored so it can be removed in dispose().
|
|
97
97
|
_onResize;
|
|
98
98
|
_resizeTimer;
|
|
99
|
-
// ESC disambiguation
|
|
99
|
+
// ESC disambiguation: a read may end on a bare ESC that is really the
|
|
100
100
|
// head of a control sequence split across two reads. We hold it briefly and
|
|
101
101
|
// fire onEscape only if no tail arrives.
|
|
102
102
|
_escPending;
|
|
@@ -644,7 +644,6 @@ export class LineEditor {
|
|
|
644
644
|
this.pastes = [];
|
|
645
645
|
this._render();
|
|
646
646
|
}
|
|
647
|
-
// ---------- rendering ----------
|
|
648
647
|
_eraseBlock() {
|
|
649
648
|
if (!this.rendered)
|
|
650
649
|
return;
|
|
@@ -936,7 +935,6 @@ export class LineEditor {
|
|
|
936
935
|
process.stdout.write(String(text) + NL);
|
|
937
936
|
this._writeBlock();
|
|
938
937
|
}
|
|
939
|
-
// ---------- spinner-compatible interface ----------
|
|
940
938
|
setStatus(text) {
|
|
941
939
|
this.statusText = text || '';
|
|
942
940
|
this._render();
|
|
@@ -1124,7 +1122,6 @@ export class LineEditor {
|
|
|
1124
1122
|
const wrapped = wrapToWidth('⚠ ' + msg, maxW);
|
|
1125
1123
|
this.printAbove(NL + theme.warn(wrapped));
|
|
1126
1124
|
}
|
|
1127
|
-
// ---------- input ----------
|
|
1128
1125
|
_submit() {
|
|
1129
1126
|
if (this.locked) {
|
|
1130
1127
|
this._render();
|
|
@@ -1367,7 +1364,7 @@ export class LineEditor {
|
|
|
1367
1364
|
}
|
|
1368
1365
|
// Bare ESC: wait a moment for a possible control-sequence tail before firing
|
|
1369
1366
|
// onEscape. Without this a sequence split across two reads (ESC, then "[A")
|
|
1370
|
-
// looked like a lone Escape and aborted the current generation
|
|
1367
|
+
// looked like a lone Escape and aborted the current generation.
|
|
1371
1368
|
_scheduleEscape() {
|
|
1372
1369
|
this._escPending = true;
|
|
1373
1370
|
if (this._escTimer)
|
|
@@ -1460,7 +1457,6 @@ export class LineEditor {
|
|
|
1460
1457
|
chars.splice(this.cursor, i - this.cursor);
|
|
1461
1458
|
this.buf = chars.join('');
|
|
1462
1459
|
}
|
|
1463
|
-
// ---------- history ----------
|
|
1464
1460
|
_historyUp() {
|
|
1465
1461
|
if (!this.history.length)
|
|
1466
1462
|
return;
|
|
@@ -1485,7 +1481,6 @@ export class LineEditor {
|
|
|
1485
1481
|
}
|
|
1486
1482
|
this.cursor = Array.from(this.buf).length;
|
|
1487
1483
|
}
|
|
1488
|
-
// ---------- reverse-search (Ctrl+R) ----------
|
|
1489
1484
|
// Start an incremental reverse search, keeping the current buffer aside so
|
|
1490
1485
|
// Esc restores it (bash behavior). An empty history leaves the editor
|
|
1491
1486
|
// untouched.
|
|
@@ -1596,7 +1591,7 @@ export class LineEditor {
|
|
|
1596
1591
|
// A previous read ended on a bare ESC. That is ambiguous: the Escape key
|
|
1597
1592
|
// or the head of a control sequence split across reads (arrows, Ctrl+Del).
|
|
1598
1593
|
// Reattach the ESC and parse the whole sequence, so a split arrow does not
|
|
1599
|
-
// abort the generation
|
|
1594
|
+
// abort the generation.
|
|
1600
1595
|
if (this._escPending) {
|
|
1601
1596
|
this._escPending = false;
|
|
1602
1597
|
if (this._escTimer) {
|
package/dist/markdown.js
CHANGED
package/dist/mcp.js
CHANGED
|
@@ -477,11 +477,8 @@ export async function createMcpPool(opts) {
|
|
|
477
477
|
}
|
|
478
478
|
}
|
|
479
479
|
// Distinct servers can produce the SAME qualified name (server 'a' + tool
|
|
480
|
-
// 'b__c' vs server 'a__b' + tool 'c')
|
|
481
|
-
//
|
|
482
|
-
// tool silently unreachable (the model calls "the right name", a DIFFERENT
|
|
483
|
-
// tool runs). Rename the later one with a numeric suffix so every name is
|
|
484
|
-
// unique.
|
|
480
|
+
// 'b__c' vs server 'a__b' + tool 'c'); dedupeToolNames below explains why
|
|
481
|
+
// that has to be fixed rather than left to tools.find().
|
|
485
482
|
dedupeToolNames(tools);
|
|
486
483
|
let closed = false;
|
|
487
484
|
return {
|
package/dist/net-capture.js
CHANGED
|
@@ -259,7 +259,7 @@ export function isFinishedWithAnswer(body) {
|
|
|
259
259
|
return false;
|
|
260
260
|
// A truncated RESPONSE head (a lone `{`) is NOT a complete answer: the
|
|
261
261
|
// finish loop would return it as the model's text and the tool-call parser
|
|
262
|
-
// would fail on it. Require real content (see
|
|
262
|
+
// would fail on it. Require real content (see responseTextLength).
|
|
263
263
|
return responseContentLength(b) >= MIN_REAL_ANSWER_CHARS;
|
|
264
264
|
}
|
|
265
265
|
// The CONTEXT size (in tokens) DeepSeek reports for the current answer.
|
package/dist/path-token.js
CHANGED
|
@@ -118,15 +118,11 @@ export function extractAtFileRefs(text) {
|
|
|
118
118
|
}
|
|
119
119
|
let j = i + 1;
|
|
120
120
|
let tok = '';
|
|
121
|
-
// The token is rejected WHOLE when it contains an at-sign that is NOT a
|
|
122
|
-
// list separator — `@Component` / `user@host` are a decorator and an
|
|
123
|
-
// email. Trimming the prefix at that at-sign would otherwise inline a
|
|
124
|
-
// bogus path.
|
|
125
|
-
let hadInnerAt = false;
|
|
126
121
|
// A token that contains an at-sign (`@Component`, `user@host`) is a
|
|
127
|
-
// decorator
|
|
128
|
-
//
|
|
129
|
-
// BEFORE it to the inliner
|
|
122
|
+
// decorator or an email, NOT a file reference, and the WHOLE token is
|
|
123
|
+
// rejected: the scan used to stop at that at-sign and hand the part
|
|
124
|
+
// BEFORE it to the inliner, which inlined a bogus path.
|
|
125
|
+
let hadInnerAt = false;
|
|
130
126
|
const isRef = (t) => t.length > 0 && !t.includes('@');
|
|
131
127
|
while (j < n && !isRefEnd(chars[j])) {
|
|
132
128
|
// A COMMA directly followed by another `@` starts the NEXT reference:
|
|
@@ -154,12 +150,9 @@ export function extractAtFileRefs(text) {
|
|
|
154
150
|
seen.add(cleaned);
|
|
155
151
|
out.push(cleaned);
|
|
156
152
|
}
|
|
157
|
-
// The
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
// The scan stopped ON a separator that split the token (`,`, `;` or a
|
|
161
|
-
// second at-sign): resume AT it so the next part is scanned as its own
|
|
162
|
-
// reference. Continuing past it dropped the second file of a list.
|
|
153
|
+
// The scan may have stopped ON a separator that split the token (`,`, `;`
|
|
154
|
+
// or a second at-sign): resume AT it so the next part is scanned as its
|
|
155
|
+
// own reference. Continuing past it dropped the second file of a list.
|
|
163
156
|
const sep = j < n ? chars[j] : '';
|
|
164
157
|
if (j > i + 1 && (sep === ',' || sep === ';' || sep === '@'))
|
|
165
158
|
i = j;
|
package/dist/prompt.js
CHANGED
|
@@ -31,7 +31,8 @@ export async function promptOnce(question) {
|
|
|
31
31
|
const wasRaw = stdin.isRaw;
|
|
32
32
|
stdin.setRawMode(true);
|
|
33
33
|
stdin.resume();
|
|
34
|
-
//
|
|
34
|
+
// Bracketed paste is enabled/disabled in pairs, so the terminal is left in
|
|
35
|
+
// normal mode however the prompt exits.
|
|
35
36
|
stdout.write('\x1b[?2004h');
|
|
36
37
|
stdout.write(question);
|
|
37
38
|
let line = '';
|
|
@@ -40,11 +41,11 @@ export async function promptOnce(question) {
|
|
|
40
41
|
const PASTE_START = '\x1b[200~';
|
|
41
42
|
const PASTE_END = '\x1b[201~';
|
|
42
43
|
const redraw = () => {
|
|
43
|
-
//
|
|
44
|
+
// Redraw in place: CR + erase-to-end, then the prompt and the line again.
|
|
44
45
|
stdout.write(String.fromCharCode(13));
|
|
45
46
|
stdout.write('\x1b[K');
|
|
46
47
|
stdout.write(question + line);
|
|
47
|
-
//
|
|
48
|
+
// Step back to the cursor, measured in UTF-16 units.
|
|
48
49
|
const back = line.length - cursor;
|
|
49
50
|
if (back > 0)
|
|
50
51
|
stdout.write('\x1b[' + back + 'D');
|