zames_pro 2.82.51 → 2.82.53
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 +19 -0
- package/dist/checkpoint.js +25 -2
- package/dist/config.js +33 -5
- package/dist/glm-chats.js +13 -1
- package/dist/index.js +10 -2
- package/dist/qwen-chats.js +22 -1
- package/dist/tui/InputLine.js +8 -4
- package/dist/tui/log-window.js +9 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.82.53] - 2026-10-23
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **glm:** keep a message without a timestamp in dialogue order
|
|
15
|
+
- **qwen:** keep a message without a timestamp in dialogue order
|
|
16
|
+
- **rewind:** resolve a full checkpoint stamp against the whole index
|
|
17
|
+
|
|
18
|
+
## [2.82.52] - 2026-10-23
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **config:** tolerate an unparsable config file when writing
|
|
23
|
+
- **tui:** use the shared width table when estimating log rows
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **tui:** derive the input box width from the shared helper
|
|
28
|
+
|
|
10
29
|
## [2.82.51] - 2026-10-23
|
|
11
30
|
|
|
12
31
|
### Added
|
package/dist/checkpoint.js
CHANGED
|
@@ -44,10 +44,30 @@ export function rotateRecords(records, max) {
|
|
|
44
44
|
* Resolve a `/rewind` selector against records sorted NEWEST FIRST.
|
|
45
45
|
* `1` = most recent; a non-numeric selector is matched as a stamp prefix.
|
|
46
46
|
*/
|
|
47
|
-
|
|
47
|
+
/** True for a selector that is exactly a full epoch-ms stamp (13 digits). */
|
|
48
|
+
export function isFullStampSelector(selector) {
|
|
49
|
+
return /^\d{13}$/.test(String(selector ?? '').trim());
|
|
50
|
+
}
|
|
51
|
+
const isFullStamp = isFullStampSelector;
|
|
52
|
+
export function pickRecord(records, selector,
|
|
53
|
+
/** The FULL record set, when `records` is only a display window. */
|
|
54
|
+
allRecords) {
|
|
48
55
|
const sel = String(selector ?? '').trim();
|
|
49
56
|
if (!sel)
|
|
50
57
|
return null;
|
|
58
|
+
// A FULL epoch-ms stamp names exactly ONE checkpoint, wherever it sits in
|
|
59
|
+
// the index. The command resolves against the newest-10 display window while
|
|
60
|
+
// restore() searches the WHOLE index, so an older checkpoint picked by its
|
|
61
|
+
// full stamp used to report "/rewind failed" — and, worse, a stamp that
|
|
62
|
+
// happened to share its first digits with a NEWER one was resolved to that
|
|
63
|
+
// WRONG record. A full stamp is therefore matched against the full set
|
|
64
|
+
// FIRST, and only then treated as an index.
|
|
65
|
+
if (isFullStamp(sel)) {
|
|
66
|
+
const pool = allRecords && allRecords.length ? allRecords : records;
|
|
67
|
+
const exact = pool.find((r) => String(r.stamp) === sel);
|
|
68
|
+
if (exact)
|
|
69
|
+
return exact;
|
|
70
|
+
}
|
|
51
71
|
if (/^\d+$/.test(sel)) {
|
|
52
72
|
const n = Number(sel);
|
|
53
73
|
// A small number is an index (1 = newest).
|
|
@@ -60,7 +80,10 @@ export function pickRecord(records, selector) {
|
|
|
60
80
|
if (sel.length < 2)
|
|
61
81
|
return null;
|
|
62
82
|
}
|
|
63
|
-
|
|
83
|
+
const pool = allRecords && allRecords.length ? allRecords : records;
|
|
84
|
+
return (records.find((r) => String(r.stamp).startsWith(sel)) ||
|
|
85
|
+
pool.find((r) => String(r.stamp).startsWith(sel)) ||
|
|
86
|
+
null);
|
|
64
87
|
}
|
|
65
88
|
// ---------- tar / fs plumbing ----------
|
|
66
89
|
function runTar(args, cwd, timeout = 60_000) {
|
package/dist/config.js
CHANGED
|
@@ -696,19 +696,47 @@ export function configPathFor(scope) {
|
|
|
696
696
|
* writing does not "freeze" all defaults into the user's file.
|
|
697
697
|
*/
|
|
698
698
|
export function readConfigFile(file) {
|
|
699
|
+
let raw;
|
|
699
700
|
try {
|
|
700
|
-
|
|
701
|
-
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
702
|
-
return data;
|
|
703
|
-
}
|
|
701
|
+
raw = fs.readFileSync(file, 'utf-8');
|
|
704
702
|
}
|
|
705
703
|
catch (e) {
|
|
706
704
|
const err = e;
|
|
705
|
+
// A MISSING file is the normal first-write case. Anything else (EACCES,
|
|
706
|
+
// EISDIR) is reported — the caller must know the write cannot land.
|
|
707
707
|
if (err.code !== 'ENOENT') {
|
|
708
708
|
throw new Error(`failed to read ${file}: ${err.message}`);
|
|
709
709
|
}
|
|
710
|
+
return {};
|
|
711
|
+
}
|
|
712
|
+
try {
|
|
713
|
+
const data = JSON.parse(raw);
|
|
714
|
+
if (data && typeof data === 'object' && !Array.isArray(data)) {
|
|
715
|
+
return data;
|
|
716
|
+
}
|
|
717
|
+
return {};
|
|
718
|
+
}
|
|
719
|
+
catch (e) {
|
|
720
|
+
// A file that does not PARSE (a hand edit with a trailing comma, a
|
|
721
|
+
// merge-conflict marker, a bare-word value) is NOT a reason to fail the
|
|
722
|
+
// write: loadConfig() already tolerates it with a warning. Failing here
|
|
723
|
+
// made /config set useless on such a project, and a caller that ignored
|
|
724
|
+
// the error overwrote the WHOLE file with the single edited key.
|
|
725
|
+
//
|
|
726
|
+
// The unparsable text is backed up beside the file first, so the operator's
|
|
727
|
+
// other settings are recoverable by hand; the warning goes to stderr, the
|
|
728
|
+
// same channel loadConfig() uses.
|
|
729
|
+
const backup = file + '.broken-' + Date.now();
|
|
730
|
+
try {
|
|
731
|
+
fs.copyFileSync(file, backup);
|
|
732
|
+
console.error(`config: ${file} is not valid JSON (${e.message});` +
|
|
733
|
+
` its content was kept in ${backup} and the write starts a fresh file`);
|
|
734
|
+
}
|
|
735
|
+
catch {
|
|
736
|
+
// Best-effort: an unreadable backup must not block the write either.
|
|
737
|
+
}
|
|
738
|
+
return {};
|
|
710
739
|
}
|
|
711
|
-
return {};
|
|
712
740
|
}
|
|
713
741
|
/**
|
|
714
742
|
* Write a setting value to the file. Returns the final object that was
|
package/dist/glm-chats.js
CHANGED
|
@@ -176,7 +176,19 @@ export function parseGlmHistory(json) {
|
|
|
176
176
|
};
|
|
177
177
|
const sorted = arr
|
|
178
178
|
.filter((x) => !!x && typeof x === 'object')
|
|
179
|
-
|
|
179
|
+
// Order by INSERTION INDEX first and by stamp only when BOTH messages
|
|
180
|
+
// carry one. `stampOf` answers 0 for a missing stamp, so a half-stamped
|
|
181
|
+
// payload pushed every undated turn BEFORE all dated ones and the dialogue
|
|
182
|
+
// came back as "the model answered before it was asked".
|
|
183
|
+
.map((m, i) => ({ m, i, t: stampOf(m) }))
|
|
184
|
+
.sort((a, b) => {
|
|
185
|
+
const da = a.t !== 0;
|
|
186
|
+
const db = b.t !== 0;
|
|
187
|
+
if (da && db && a.t !== b.t)
|
|
188
|
+
return a.t - b.t;
|
|
189
|
+
return a.i - b.i;
|
|
190
|
+
})
|
|
191
|
+
.map((x) => x.m);
|
|
180
192
|
const out = [];
|
|
181
193
|
for (const m of sorted) {
|
|
182
194
|
// The role comes from `role` (confirmed by the live fixture). Only the
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ import { handleConfigCommand as runConfigCommand } from './config-command.js';
|
|
|
28
28
|
import { translate, normalizeLocale, localeDisplayName, isLocale, } from './i18n.js';
|
|
29
29
|
import { Transcript } from './transcript.js';
|
|
30
30
|
import { UndoStore } from './undo.js';
|
|
31
|
-
import { CheckpointStore, pickRecord as pickCheckpoint } from './checkpoint.js';
|
|
31
|
+
import { CheckpointStore, pickRecord as pickCheckpoint, isFullStampSelector, } from './checkpoint.js';
|
|
32
32
|
import { selfReview, selfDiff, selfApply, selfList } from './self-review.js';
|
|
33
33
|
import { performCompact } from './compact.js';
|
|
34
34
|
import { clampGoalCondition } from './goal.js';
|
|
@@ -3913,8 +3913,16 @@ async function main() {
|
|
|
3913
3913
|
if (editor)
|
|
3914
3914
|
editor.lock(t('msg.input_locked'));
|
|
3915
3915
|
try {
|
|
3916
|
+
// The DISPLAY window is the newest 10, but a FULL stamp must resolve
|
|
3917
|
+
// against the whole index (restore() searches all of it): an older
|
|
3918
|
+
// checkpoint picked by its stamp used to report "/rewind failed", and a
|
|
3919
|
+
// stamp sharing its first digits with a newer one could resolve to
|
|
3920
|
+
// THAT wrong record.
|
|
3916
3921
|
const list = await checkpoints.list(10);
|
|
3917
|
-
const
|
|
3922
|
+
const all = isFullStampSelector(selector)
|
|
3923
|
+
? await checkpoints.list(Number.MAX_SAFE_INTEGER)
|
|
3924
|
+
: list;
|
|
3925
|
+
const target = pickCheckpoint(list, selector, all);
|
|
3918
3926
|
if (!target) {
|
|
3919
3927
|
console.error(theme.error(t('rewind.failed', { v: selector })));
|
|
3920
3928
|
continue;
|
package/dist/qwen-chats.js
CHANGED
|
@@ -208,7 +208,28 @@ export function parseQwenHistory(json) {
|
|
|
208
208
|
// The map is UNORDERED; `timestamp` (seconds) restores the dialogue order.
|
|
209
209
|
const sorted = arr
|
|
210
210
|
.filter((x) => !!x && typeof x === 'object')
|
|
211
|
-
|
|
211
|
+
// A message WITHOUT a usable timestamp must not be forced to 0: that put
|
|
212
|
+
// it BEFORE every dated turn, so a partially stamped payload reordered the
|
|
213
|
+
// dialogue (the operator's question surfaced after the model's answer).
|
|
214
|
+
// Order by index first — the map is in insertion order — and only then by
|
|
215
|
+
// stamp; a missing stamp inherits its neighbour's position instead of
|
|
216
|
+
// jumping to the front. A stable sort keeps the original order on a tie.
|
|
217
|
+
.map((m, i) => ({ m, i }))
|
|
218
|
+
.sort((a, b) => {
|
|
219
|
+
const ta = Number(a.m.timestamp);
|
|
220
|
+
const tb = Number(b.m.timestamp);
|
|
221
|
+
const va = Number.isFinite(ta) ? ta : NaN;
|
|
222
|
+
const vb = Number.isFinite(tb) ? tb : NaN;
|
|
223
|
+
if (Number.isFinite(va) && Number.isFinite(vb)) {
|
|
224
|
+
if (va !== vb)
|
|
225
|
+
return va - vb;
|
|
226
|
+
return a.i - b.i;
|
|
227
|
+
}
|
|
228
|
+
// Only ONE side is dated: keep the insertion order (a half-stamped
|
|
229
|
+
// payload has no trustworthy clock to sort by).
|
|
230
|
+
return a.i - b.i;
|
|
231
|
+
})
|
|
232
|
+
.map((x) => x.m);
|
|
212
233
|
const out = [];
|
|
213
234
|
for (const m of sorted) {
|
|
214
235
|
// Role detection is DEFENSIVE: the field has been `role` (string,
|
package/dist/tui/InputLine.js
CHANGED
|
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
11
11
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
12
12
|
import { Box, Text, useInput, usePaste, useCursor, useWindowSize, useStdin, } from 'ink';
|
|
13
13
|
import { translate } from '../i18n.js';
|
|
14
|
-
import { layoutInput, SUGGEST_PAGE, } from '../input/layout.js';
|
|
14
|
+
import { inputInnerCols, layoutInput, SUGGEST_PAGE, } from '../input/layout.js';
|
|
15
15
|
import { insertText, insertPaste, insertNewline, backspace, del, moveLeft, moveRight, moveHome, moveEnd, wordLeft, wordRight, deleteWordLeft, deleteWordRight, killToLineEnd, moveVisualRows, pushUndo, undoEdit, historyUp, historyDown, searchStart, searchNext, searchAccept, searchCancel, searchInput, searchBackspace, recordHistory, afterSubmit, } from './edit.js';
|
|
16
16
|
import { dispatchKey, parseCsiU } from './keys.js';
|
|
17
17
|
import { useSyncExternalStore } from 'react';
|
|
@@ -42,9 +42,13 @@ export function InputLine(props) {
|
|
|
42
42
|
}));
|
|
43
43
|
}, [props.store]);
|
|
44
44
|
// The input lives inside a rounded box: 2 columns of border (left/right) plus
|
|
45
|
-
// 1 column of inner padding on each side
|
|
46
|
-
//
|
|
47
|
-
|
|
45
|
+
// 1 column of inner padding on each side, so the CONTENT width is `cols - 4`.
|
|
46
|
+
// Use the SHARED helper: `inputBoxRows()` — the height the full-screen layout
|
|
47
|
+
// reserves — is computed from the SAME function, and a hand-written
|
|
48
|
+
// `Math.max(4, cols - 4)` here was one independent copy that could drift from
|
|
49
|
+
// the reservation (the box then rendered more rows than were reserved and the
|
|
50
|
+
// input rows overprinted the status bar).
|
|
51
|
+
const innerCols = inputInnerCols(cols);
|
|
48
52
|
// Mirror the state into a ref so the useInput callback (a stable closure Ink
|
|
49
53
|
// calls for every key) always sees the latest values.
|
|
50
54
|
const stRef = useRef(st);
|
package/dist/tui/log-window.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// purpose: measuring the real Yoga layout per entry would need a ref per row
|
|
8
8
|
// and a re-render, while counting wrapped lines is exact enough for a log and
|
|
9
9
|
// costs nothing.
|
|
10
|
+
import { charWidth } from '../input/layout.js';
|
|
10
11
|
/** Visible width of a line without ANSI escapes (wide chars count 2). */
|
|
11
12
|
function visWidth(s) {
|
|
12
13
|
let w = 0;
|
|
@@ -43,16 +44,14 @@ function visWidth(s) {
|
|
|
43
44
|
continue;
|
|
44
45
|
}
|
|
45
46
|
const cp = s.codePointAt(i);
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
else
|
|
55
|
-
w += 1;
|
|
47
|
+
// Use the SAME width rule as the input/status layout (charWidth in
|
|
48
|
+
// src/input/layout.ts). The old table here counted EVERYTHING up to
|
|
49
|
+
// U+9FFF as two cells, so a line of box arrows (`→`, U+2192), the
|
|
50
|
+
// hourglass `⧗` (U+29D7) or Cyrillic/Greek letters over-estimated its
|
|
51
|
+
// rows; at a narrow width that reserves an extra row per entry and the
|
|
52
|
+
// pane believes it is full, stopping the scroll early. Sharing ONE table
|
|
53
|
+
// keeps the viewport and the rest of the TUI from disagreeing.
|
|
54
|
+
w += charWidth(cp);
|
|
56
55
|
i += cp > 0xffff ? 2 : 1;
|
|
57
56
|
}
|
|
58
57
|
return w;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.82.
|
|
3
|
+
"version": "2.82.53",
|
|
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": {
|