zames_pro 2.82.136 → 2.82.138
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 +76 -0
- package/dist/agent-loop.js +11 -14
- 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 +15 -21
- package/dist/checkpoint.js +6 -10
- package/dist/cli-args.js +4 -6
- package/dist/code-blocks.js +3 -6
- package/dist/commands.js +6 -10
- package/dist/compact.js +1 -1
- 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 +6 -9
- 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 +8 -19
- package/dist/input/suggest.js +4 -7
- package/dist/input.js +6 -11
- 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 +11 -11
- 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 +6 -10
- package/dist/subagent.js +1 -1
- package/dist/system-prompt.js +1 -1
- package/dist/tools.js +1 -1
- package/dist/tui/widgets.js +0 -1
- package/package.json +1 -1
package/dist/changelog.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
// Conventional-Commits -> CHANGELOG draft. PURE and
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// Parse one commit SUBJECT into a conventional commit, or null when it
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// fix!: change Y feat!: breaking
|
|
11
|
-
// Explicit breaking markers: `!` after the type/scope. A BREAKING CHANGE
|
|
12
|
-
// body footer is beyond a subject-only parser (git log %s only) -- out of scope.
|
|
1
|
+
// Conventional-Commits -> CHANGELOG draft. PURE and unit-tested: the draft is
|
|
2
|
+
// built from git log by scripts/changelog-draft.mts (and the release script),
|
|
3
|
+
// and `syncUnreleased` rewrites the Unreleased section only on the explicit
|
|
4
|
+
// --write flag — the changelog must stay a deliberate document, not a
|
|
5
|
+
// byproduct of whatever landed on the branch.
|
|
6
|
+
// Parse one commit SUBJECT into a conventional commit, or null when it does
|
|
7
|
+
// not follow the format (a free-form subject is skipped from the draft).
|
|
8
|
+
// `feat: add X`, `feat(cli): add X` and `fix!: change Y` are all understood. A
|
|
9
|
+
// `BREAKING CHANGE:` body footer is out of scope: git log %s carries no body.
|
|
13
10
|
export function parseConventionalCommit(subject) {
|
|
14
11
|
const s = String(subject ?? '').trim();
|
|
15
12
|
if (!s)
|
|
@@ -37,11 +34,9 @@ const KIND_BY_TYPE = {
|
|
|
37
34
|
};
|
|
38
35
|
// Types that never belong in a user-facing changelog (build/CI/internal).
|
|
39
36
|
const SKIP_TYPES = new Set(['chore', 'ci', 'test', 'build', 'docs', 'internal']);
|
|
40
|
-
// Group parsed commits by changelog kind.
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
// order), and duplicate lines are dropped so a rebase that repeats a subject
|
|
44
|
-
// does not flip the draft twice.
|
|
37
|
+
// Group parsed commits by changelog kind. A scope is prefixed as `**scope:** `
|
|
38
|
+
// and a breaking marker appended as `(BREAKING)`. Duplicate lines are dropped
|
|
39
|
+
// so a rebase that repeats a subject does not list it twice.
|
|
45
40
|
export function groupCommits(forCommit) {
|
|
46
41
|
const groups = {
|
|
47
42
|
Added: [],
|
|
@@ -63,9 +58,8 @@ export function groupCommits(forCommit) {
|
|
|
63
58
|
}
|
|
64
59
|
return groups;
|
|
65
60
|
}
|
|
66
|
-
// Render the groups as a changelog body
|
|
67
|
-
//
|
|
68
|
-
// omitted. Returns '' when there is nothing to report.
|
|
61
|
+
// Render the groups as a changelog body, in the standard Keep-a-Changelog
|
|
62
|
+
// order and with empty groups omitted. Returns '' when there is nothing to report.
|
|
69
63
|
export function renderChangelogDraft(groups) {
|
|
70
64
|
const order = ['Added', 'Fixed', 'Changed', 'Removed'];
|
|
71
65
|
const out = [];
|
|
@@ -80,7 +74,7 @@ export function renderChangelogDraft(groups) {
|
|
|
80
74
|
}
|
|
81
75
|
/**
|
|
82
76
|
* Replace the body between `## [Unreleased]` and the next `## [` heading with
|
|
83
|
-
* a freshly drafted body
|
|
77
|
+
* a freshly drafted body. PURE so a test can pin the exact behavior.
|
|
84
78
|
*
|
|
85
79
|
* When `body` is empty the existing Unreleased section is CLEARED to just the
|
|
86
80
|
* heading (no stray blank content). A changelog without an Unreleased heading
|
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
|
|
@@ -21,7 +21,6 @@ const CHECKPOINT_DIR = path.join(os.homedir(), '.zames', 'checkpoints');
|
|
|
21
21
|
// dir. Everything else is part of the snapshot. Patterns are anchored to the
|
|
22
22
|
// archive root ('./x') so a nested `src/tmp` is NOT accidentally excluded.
|
|
23
23
|
export const CHECKPOINT_EXCLUDES = ['node_modules', '.git', 'dist', 'tmp'];
|
|
24
|
-
// ---------- pure helpers (unit-tested without tar) ----------
|
|
25
24
|
export function tarCreateArgs(archive, _dir) {
|
|
26
25
|
const args = ['-czf', archive];
|
|
27
26
|
for (const e of CHECKPOINT_EXCLUDES)
|
|
@@ -40,15 +39,14 @@ export function rotateRecords(records, max) {
|
|
|
40
39
|
return records.slice();
|
|
41
40
|
return records.slice(records.length - max);
|
|
42
41
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Resolve a `/rewind` selector against records sorted NEWEST FIRST.
|
|
45
|
-
* `1` = most recent; a non-numeric selector is matched as a stamp prefix.
|
|
46
|
-
*/
|
|
47
42
|
/** True for a selector that is exactly a full epoch-ms stamp (13 digits). */
|
|
48
43
|
export function isFullStampSelector(selector) {
|
|
49
44
|
return /^\d{13}$/.test(String(selector ?? '').trim());
|
|
50
45
|
}
|
|
51
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Resolve a `/rewind` selector against records sorted NEWEST FIRST.
|
|
48
|
+
* `1` = most recent; a longer number is matched as a stamp prefix.
|
|
49
|
+
*/
|
|
52
50
|
export function pickRecord(records, selector,
|
|
53
51
|
/** The FULL record set, when `records` is only a display window. */
|
|
54
52
|
allRecords) {
|
|
@@ -62,7 +60,7 @@ allRecords) {
|
|
|
62
60
|
// happened to share its first digits with a NEWER one was resolved to that
|
|
63
61
|
// WRONG record. A full stamp is therefore matched against the full set
|
|
64
62
|
// FIRST, and only then treated as an index.
|
|
65
|
-
if (
|
|
63
|
+
if (isFullStampSelector(sel)) {
|
|
66
64
|
const pool = allRecords && allRecords.length ? allRecords : records;
|
|
67
65
|
const exact = pool.find((r) => String(r.stamp) === sel);
|
|
68
66
|
if (exact)
|
|
@@ -85,7 +83,6 @@ allRecords) {
|
|
|
85
83
|
pool.find((r) => String(r.stamp).startsWith(sel)) ||
|
|
86
84
|
null);
|
|
87
85
|
}
|
|
88
|
-
// ---------- tar / fs plumbing ----------
|
|
89
86
|
function runTar(args, cwd, timeout = 60_000) {
|
|
90
87
|
return new Promise((resolve) => {
|
|
91
88
|
// `execFile` throws SYNCHRONOUSLY on a spawn error (ENOENT for the shell,
|
|
@@ -137,7 +134,6 @@ async function copyTree(from, to) {
|
|
|
137
134
|
await fs.copyFile(src, dst);
|
|
138
135
|
}
|
|
139
136
|
}
|
|
140
|
-
// ---------- store ----------
|
|
141
137
|
export class CheckpointStore {
|
|
142
138
|
enabled;
|
|
143
139
|
maxBackups;
|
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
|
|
@@ -23,11 +23,9 @@ export function createCliArgs(argv) {
|
|
|
23
23
|
const args = Array.isArray(argv) ? argv : [];
|
|
24
24
|
const getArg = (flag, fallback = null) => {
|
|
25
25
|
const i = args.indexOf(flag);
|
|
26
|
-
//
|
|
27
|
-
// back
|
|
28
|
-
//
|
|
29
|
-
// returns `--debug` (the original inline helper did the same); we preserve
|
|
30
|
-
// it rather than "fix" a long-standing quirk in a refactor.
|
|
26
|
+
// Truthiness, not `!== undefined`: an EMPTY value (`--task ''`) must
|
|
27
|
+
// fall back too - an empty task is not a task. A following FLAG is not
|
|
28
|
+
// special-cased either (see the CliArgs doc above).
|
|
31
29
|
return i !== -1 && args[i + 1] ? args[i + 1] : fallback;
|
|
32
30
|
};
|
|
33
31
|
const hasFlag = (flag) => args.includes(flag);
|
package/dist/code-blocks.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
// PURE extraction of fenced code blocks from a Markdown answer.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// to a key / command. That needs the RAW fenced text (the highlighted render is
|
|
5
|
-
// full of ANSI and must NOT be copied), which is what this module returns.
|
|
6
|
-
// Kept pure so it is unit-tested without a terminal.
|
|
1
|
+
// PURE extraction of fenced code blocks from a Markdown answer. It returns the
|
|
2
|
+
// RAW fenced text: the highlighted render is full of ANSI and must never be
|
|
3
|
+
// what gets copied out of the TUI.
|
|
7
4
|
/**
|
|
8
5
|
* Extract every fenced code block (``` or ~~~) from `md`, in order. Handles a
|
|
9
6
|
* longer closing fence than the opening one and an unterminated final block
|
package/dist/commands.js
CHANGED
|
@@ -3,9 +3,9 @@ import path from 'path';
|
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import { isImageName } from './attachments.js';
|
|
5
5
|
import { translate } from './i18n.js';
|
|
6
|
-
// Helpers for the
|
|
7
|
-
// /
|
|
8
|
-
// live agent/browser
|
|
6
|
+
// Helpers for the slash commands (/diff, /cost, /export, /doctor, /review,
|
|
7
|
+
// /add-dir, /queue, /goal, custom commands). Pure functions, unit-tested
|
|
8
|
+
// without a live agent/browser; index.ts only renders their output.
|
|
9
9
|
const NL = String.fromCharCode(10);
|
|
10
10
|
const CR = String.fromCharCode(13);
|
|
11
11
|
// ---------- /diff ----------
|
|
@@ -27,8 +27,8 @@ export function formatDiff(diffText, opts = {}, t = translate('en')) {
|
|
|
27
27
|
const rest = lines.length - maxLines;
|
|
28
28
|
return head + NL + t('diff.more_lines', { n: String(rest) });
|
|
29
29
|
}
|
|
30
|
-
// Git arguments for the `/diff` command. An ARRAY
|
|
31
|
-
// them through runGitArgs(), so nothing is interpreted by a shell.
|
|
30
|
+
// Git arguments for the `/diff` command. An ARRAY, not a shell string: the
|
|
31
|
+
// caller runs them through runGitArgs(), so nothing is interpreted by a shell.
|
|
32
32
|
export function diffGitArgs(staged = false) {
|
|
33
33
|
return staged ? ['diff', '--staged'] : ['diff'];
|
|
34
34
|
}
|
|
@@ -238,7 +238,7 @@ export function renderCost(stats, transcriptFile, tokenUsage = null, t = transla
|
|
|
238
238
|
for (const [name, n] of top)
|
|
239
239
|
lines.push(' ' + nameLine(name) + ': ' + n);
|
|
240
240
|
}
|
|
241
|
-
//
|
|
241
|
+
// Where the time went. Sort by total ms, show the top tools with the
|
|
242
242
|
// count and a human duration, so Read→Edit cycles and slow Bash calls are
|
|
243
243
|
// visible at a glance.
|
|
244
244
|
const byTime = Object.entries(stats.toolTime || {})
|
|
@@ -1038,10 +1038,6 @@ export function mergeMessages(msgs) {
|
|
|
1038
1038
|
list.length +
|
|
1039
1039
|
' messages while you were working. They are listed below in order — ' +
|
|
1040
1040
|
'treat them as one instruction.';
|
|
1041
|
-
// The parts are OPERATOR text: a line that reads `---` (a Markdown rule, a
|
|
1042
|
-
// pasted diff) would spoof a message boundary and the model would see an
|
|
1043
|
-
// extra message. Neutralize such a line inside a part; the separator below
|
|
1044
|
-
// stays the only one.
|
|
1045
1041
|
// The message text is OPERATOR input: a line that reads `---` (a Markdown
|
|
1046
1042
|
// rule, a pasted diff) or repeats the header sentence spoofs a message
|
|
1047
1043
|
// BOUNDARY / count — the model then sees an extra message that never
|
package/dist/compact.js
CHANGED
|
@@ -43,7 +43,7 @@ export async function performCompact(opts) {
|
|
|
43
43
|
try {
|
|
44
44
|
if (!quiet)
|
|
45
45
|
log(t('compact.start'));
|
|
46
|
-
// PreCompact hook
|
|
46
|
+
// PreCompact hook: runs BEFORE the chat is summarized/rewritten, so
|
|
47
47
|
// a hook can persist state or notify. Best-effort.
|
|
48
48
|
try {
|
|
49
49
|
const out = await runLifecycleHooks(loadHooks(workdir), 'PreCompact', workdir, { chatId: currentChatId });
|
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,
|
|
@@ -26,7 +26,7 @@ export async function handleConfigCommand(input, deps) {
|
|
|
26
26
|
? all
|
|
27
27
|
: ['/config', ...all];
|
|
28
28
|
const sub = (parts[1] || '').toLowerCase();
|
|
29
|
-
//
|
|
29
|
+
// Bare `/config` opens the menu (a text list in non-TTY, via openMenu).
|
|
30
30
|
if (!sub || sub === 'menu') {
|
|
31
31
|
await openMenu();
|
|
32
32
|
return;
|
|
@@ -39,7 +39,8 @@ export async function handleConfigCommand(input, deps) {
|
|
|
39
39
|
print(theme.system(t('cfg.paths', { global: homeConfigPath, project: projectConfigPath })));
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
|
-
//
|
|
42
|
+
// `/config lang <ru|en>` is a shortcut for `set ui.locale`; `язык` is an
|
|
43
|
+
// accepted alias because the operator types the command in their own language.
|
|
43
44
|
if (sub === 'lang' || sub === 'language' || sub === 'язык') {
|
|
44
45
|
const val = parts[2];
|
|
45
46
|
if (!val) {
|
|
@@ -123,7 +124,8 @@ export async function handleConfigCommand(input, deps) {
|
|
|
123
124
|
print(theme.assistant(t('cfg.reset', { v: key })));
|
|
124
125
|
return;
|
|
125
126
|
}
|
|
126
|
-
//
|
|
127
|
+
// An unknown subcommand reports the key AND shows the list, so the operator
|
|
128
|
+
// sees the valid keys without a second command.
|
|
127
129
|
printErr(theme.error(t('cfg.unknown_key', { v: sub })));
|
|
128
130
|
showList();
|
|
129
131
|
}
|
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
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
import { theme } from './theme.js';
|
|
7
7
|
/**
|
|
8
8
|
* The value shown for a field in the LIST: the default marker, on/off for a
|
|
9
|
-
* boolean, a fixed mask for a password, otherwise the JSON form.
|
|
10
|
-
* long-used inline logic so the output is byte-for-byte identical.
|
|
9
|
+
* boolean, a fixed mask for a password, otherwise the JSON form.
|
|
11
10
|
*/
|
|
12
11
|
export function configListValue(field, value, t) {
|
|
13
12
|
if (value === undefined)
|
package/dist/config-live.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
// construction, so a change must be pushed onto the live instance too or it
|
|
5
5
|
// is silently a no-op until a restart. The path->target mapping is PURE and
|
|
6
6
|
// unit-tested here; index.ts wires it to the live instances.
|
|
7
|
-
/** config path -> field name on the browser instance.
|
|
8
|
-
* share these names; a field that is absent on the active backend
|
|
9
|
-
*
|
|
7
|
+
/** config path -> field name on the browser instance. All three backends
|
|
8
|
+
* share these names; a field that is absent on the active backend is skipped
|
|
9
|
+
* rather than throwing, so one map serves every provider. */
|
|
10
10
|
export const BROWSER_LIVE_CONFIG = {
|
|
11
11
|
'browser.answerTimeoutMs': 'answerTimeoutMs',
|
|
12
12
|
'browser.askRetries': 'askRetries',
|
|
@@ -30,11 +30,9 @@ export function applyBrowserLiveConfig(path, value, browser) {
|
|
|
30
30
|
const field = BROWSER_LIVE_CONFIG[path];
|
|
31
31
|
if (!field || !(field in browser))
|
|
32
32
|
return false;
|
|
33
|
-
// A timing value must be a FINITE NON-NEGATIVE number
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// the derived `askDeadlineMs` update, leaving a deadline that no longer
|
|
37
|
-
// matches the timeout. Reject the bad value instead of corrupting the field.
|
|
33
|
+
// A timing value must be a FINITE NON-NEGATIVE number: a NaN reached a live
|
|
34
|
+
// field (a timer with NaN fires immediately) and a negative one is nonsense.
|
|
35
|
+
// Reject the value instead of corrupting the field.
|
|
38
36
|
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
|
|
39
37
|
return false;
|
|
40
38
|
}
|
|
@@ -45,11 +43,7 @@ export function applyBrowserLiveConfig(path, value, browser) {
|
|
|
45
43
|
// give up before the browser's own timeout ever fired. Re-derive it the same
|
|
46
44
|
// way the constructor does.
|
|
47
45
|
if (field === 'answerTimeoutMs' && 'askDeadlineMs' in browser) {
|
|
48
|
-
|
|
49
|
-
// left the deadline at its stale value when the timeout was set to 0.
|
|
50
|
-
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
51
|
-
browser.askDeadlineMs = value + 60_000;
|
|
52
|
-
}
|
|
46
|
+
browser.askDeadlineMs = value + 60_000;
|
|
53
47
|
}
|
|
54
48
|
return true;
|
|
55
49
|
}
|
package/dist/config-menu.js
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
import { theme } from './theme.js';
|
|
12
12
|
import { visLen } from './input.js';
|
|
13
13
|
const ESC = String.fromCharCode(27);
|
|
14
|
-
// Strip ANSI sequences to compute the visible length of a line.
|
|
15
|
-
//
|
|
16
|
-
//
|
|
14
|
+
// Strip ANSI sequences to compute the visible length of a line. A local copy
|
|
15
|
+
// (input.ts keeps its own private helper): the regex must match a real CSI,
|
|
16
|
+
// otherwise visCols over-counts and the erase moves to the wrong row.
|
|
17
17
|
function stripAnsiLocal(s) {
|
|
18
18
|
return s.replace(/\x1b\[[0-9;?]*[A-Za-z]/g, '');
|
|
19
19
|
}
|
|
@@ -197,7 +197,8 @@ export function runConfigMenu(opts) {
|
|
|
197
197
|
lines.push(...tail);
|
|
198
198
|
// Erase the previous block and print the new one. We count VISUAL lines
|
|
199
199
|
// (accounting for wrapping by terminal width), otherwise on narrow
|
|
200
|
-
// terminals
|
|
200
|
+
// terminals the unerased tails leave garbage behind.
|
|
201
|
+
//
|
|
201
202
|
// Reserve the last column. A line whose visible width reaches exactly the
|
|
202
203
|
// terminal width hits the autowrap edge: depending on the terminal it
|
|
203
204
|
// either consumes two rows for the following `\n` or keeps a pending wrap,
|
package/dist/config.js
CHANGED
|
@@ -135,7 +135,7 @@ export const DEFAULTS = {
|
|
|
135
135
|
contextLimit: 1_000_000,
|
|
136
136
|
// 0 = auto: terminal width, capped at 100 columns (the historical width).
|
|
137
137
|
answerWidth: 0,
|
|
138
|
-
//
|
|
138
|
+
// statusLine hook. Off by default; edit config.json to set a command.
|
|
139
139
|
statusLineCommand: '',
|
|
140
140
|
statusLineIntervalMs: 10_000,
|
|
141
141
|
// Use the Ink/React TUI for the input area (the rich editor). Set to false
|
|
@@ -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 ----------
|