zames_pro 2.96.2 → 2.96.4
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 +17 -5
- package/dist/banner.js +9 -4
- package/dist/cli-args.js +8 -0
- package/dist/commands.js +13 -2
- package/dist/context.js +5 -1
- package/dist/goal.js +10 -1
- package/dist/provider-fallback.js +4 -1
- package/dist/scheduler.js +29 -6
- package/dist/tui/layout.js +9 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,13 +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.96.4] - 2026-11-06
|
|
11
|
+
|
|
10
12
|
### Fixed
|
|
11
13
|
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
14
|
+
- **tui:** keep a tall suggestion popup from overflowing the layout
|
|
15
|
+
- **scheduler:** scan cron in UTC so a DST fall-back hour is not skipped
|
|
16
|
+
- **context:** parse a YAML block list of command arguments
|
|
17
|
+
- **provider-fallback:** probe a guest-capable provider without the guest flag
|
|
18
|
+
- **cli:** accept the --flag=value spelling of a value flag
|
|
19
|
+
|
|
20
|
+
## [2.96.3] - 2026-11-06
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **banner:** keep a banner field on one line inside the box
|
|
25
|
+
- **scheduler:** reschedule a job whose nextAt is not finite
|
|
26
|
+
- **goal:** keep a condition from injecting its own verdict block
|
|
27
|
+
- **compact:** keep operator and summary text out of the carryover headings
|
|
28
|
+
- **release:** sync the changelog and rebuild its pages inside a release
|
|
17
29
|
|
|
18
30
|
## [2.96.2] - 2026-11-06
|
|
19
31
|
|
package/dist/banner.js
CHANGED
|
@@ -81,11 +81,16 @@ function clip(s, max) {
|
|
|
81
81
|
* (the terminal width minus a safety column).
|
|
82
82
|
*/
|
|
83
83
|
export function renderStartupBanner(info, contentWidth) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
// Every field is printed INSIDE the box: a newline in the working directory
|
|
85
|
+
// or in the provider label (both come from the config/environment) broke the
|
|
86
|
+
// borders apart, so each value is flattened to one line first.
|
|
87
|
+
const oneLine = (v) => String(v ?? '').replace(/\s+/g, ' ');
|
|
88
|
+
const title = theme.assistant(' ' + oneLine(info.title)) +
|
|
89
|
+
theme.dim(' v' + oneLine(info.version));
|
|
90
|
+
const dir = theme.system(' cwd: ') + theme.dir(oneLine(info.workdir));
|
|
91
|
+
const prov = theme.system(' provider: ') + theme.toggleOn(oneLine(info.provider));
|
|
87
92
|
const lines = [title, dir, prov];
|
|
88
93
|
if (info.hint)
|
|
89
|
-
lines.push(theme.dim(' ' + info.hint));
|
|
94
|
+
lines.push(theme.dim(' ' + oneLine(info.hint)));
|
|
90
95
|
return box(lines, contentWidth);
|
|
91
96
|
}
|
package/dist/cli-args.js
CHANGED
|
@@ -22,6 +22,14 @@ export const SKIP_VALUE_FLAGS = [
|
|
|
22
22
|
export function createCliArgs(argv) {
|
|
23
23
|
const args = Array.isArray(argv) ? argv : [];
|
|
24
24
|
const getArg = (flag, fallback = null) => {
|
|
25
|
+
// `--task=fix it` is the common shell spelling: the SEPARATE form below
|
|
26
|
+
// never matched it, so the value was dropped from the task text and the
|
|
27
|
+
// operator's prompt vanished. Check the `=` form first.
|
|
28
|
+
const eq = flag + '=';
|
|
29
|
+
const joined = args.find((a) => a.startsWith(eq));
|
|
30
|
+
if (joined !== undefined && joined.length > eq.length) {
|
|
31
|
+
return joined.slice(eq.length);
|
|
32
|
+
}
|
|
25
33
|
const i = args.indexOf(flag);
|
|
26
34
|
// Truthiness, not `!== undefined`: an EMPTY value (`--task ''`) must
|
|
27
35
|
// fall back too - an empty task is not a task. A following FLAG is not
|
package/dist/commands.js
CHANGED
|
@@ -580,8 +580,19 @@ export function buildCompactToolResult(todos = [], goal = null) {
|
|
|
580
580
|
* they are session state the model-written summary is not guaranteed to keep.
|
|
581
581
|
*/
|
|
582
582
|
export function buildCompactCarryover(summary, task, todos = [], goal = null) {
|
|
583
|
-
const
|
|
584
|
-
|
|
583
|
+
const NL2 = NL + NL;
|
|
584
|
+
// The carried text becomes the FIRST USER TURN of the new chat. The task is
|
|
585
|
+
// OPERATOR text and the summary is MODEL text: a line starting with `## `
|
|
586
|
+
// would fake one of the SESSION blocks this function appends itself (a todo
|
|
587
|
+
// checklist / a goal the agent never had), so such lines are indented out of
|
|
588
|
+
// the heading namespace.
|
|
589
|
+
const noHeadings = (v) => String(v ?? '')
|
|
590
|
+
.split(NL)
|
|
591
|
+
.map((l) => (/^## /.test(l) ? ' ' + l : l))
|
|
592
|
+
.join(NL)
|
|
593
|
+
.trim();
|
|
594
|
+
const body = noHeadings(summary);
|
|
595
|
+
const goalText = noHeadings(task);
|
|
585
596
|
let out = 'Context carried over from a previous chat (compacted). ' +
|
|
586
597
|
'Treat it as the history of our work so far and continue from the current state.';
|
|
587
598
|
out += NL + NL + body;
|
package/dist/context.js
CHANGED
|
@@ -332,11 +332,15 @@ function parseFrontmatter(raw) {
|
|
|
332
332
|
const userInvokable = typeof out['user-invokable'] === 'string'
|
|
333
333
|
? out['user-invokable'].toLowerCase() !== 'false'
|
|
334
334
|
: undefined;
|
|
335
|
+
// A YAML BLOCK LIST (`arguments:` + ` - env`) folds into one string with
|
|
336
|
+
// the list markers, so splitting on whitespace produced `['-', 'env']`: every
|
|
337
|
+
// declared name was 'missing' and the command was refused. Strip the markers
|
|
338
|
+
// first; the inline form (`arguments: env mode`) still works.
|
|
335
339
|
const argumentsList = typeof out['arguments'] === 'string'
|
|
336
340
|
? out['arguments']
|
|
337
341
|
.split(/[,\s]+/)
|
|
338
342
|
.map((s) => s.trim())
|
|
339
|
-
.filter(
|
|
343
|
+
.filter((s) => s && s !== '-')
|
|
340
344
|
: undefined;
|
|
341
345
|
return {
|
|
342
346
|
name: out.name,
|
package/dist/goal.js
CHANGED
|
@@ -29,6 +29,15 @@ export function clampGoalCondition(condition) {
|
|
|
29
29
|
// is requested; no tools.
|
|
30
30
|
export function buildEvaluatorPrompt(condition) {
|
|
31
31
|
const NL = String.fromCharCode(10);
|
|
32
|
+
// The condition is OPERATOR text pasted VERBATIM. A line of it that looks
|
|
33
|
+
// like the requested answer format (`Reply with EXACTLY two lines`,
|
|
34
|
+
// `VERDICT: MET`) would let the operator's own words stand in for the
|
|
35
|
+
// judge's verdict and close an unfinished task as done: such a line is
|
|
36
|
+
// quoted instead of being left at the start of a line.
|
|
37
|
+
const safeCondition = String(condition ?? '')
|
|
38
|
+
.split(NL)
|
|
39
|
+
.map((l) => /^\s*(?:reply with|verdict\s*:|reason\s*:)/i.test(l) ? '> ' + l : l)
|
|
40
|
+
.join(NL);
|
|
32
41
|
return ('You are a strict COMPLETION EVALUATOR. You do NOT do the work and you do NOT call tools. Read ' +
|
|
33
42
|
'the conversation above and judge - from what the agent has ALREADY surfaced - whether ' +
|
|
34
43
|
'the completion condition below is satisfied.' +
|
|
@@ -36,7 +45,7 @@ export function buildEvaluatorPrompt(condition) {
|
|
|
36
45
|
NL +
|
|
37
46
|
'Completion condition:' +
|
|
38
47
|
NL +
|
|
39
|
-
|
|
48
|
+
safeCondition +
|
|
40
49
|
NL +
|
|
41
50
|
NL +
|
|
42
51
|
'Reply with EXACTLY two lines, nothing else:' +
|
|
@@ -26,7 +26,10 @@ export async function pickFallbackProvider(active, candidates, probeLogin, hasMa
|
|
|
26
26
|
continue;
|
|
27
27
|
// A live GUEST session is not a fallback (it dies in a few turns), but the
|
|
28
28
|
// provider is still authorizable — signing in makes it a candidate.
|
|
29
|
-
|
|
29
|
+
// `guestSession` is OPTIONAL: an older caller that predates the field
|
|
30
|
+
// leaves it undefined, and reading that as "a guest" silently refused to
|
|
31
|
+
// fall back to a signed-in provider with a live session.
|
|
32
|
+
if (c.guestAllowed && c.guestSession === true) {
|
|
30
33
|
authorizable.push(c.id);
|
|
31
34
|
continue;
|
|
32
35
|
}
|
package/dist/scheduler.js
CHANGED
|
@@ -157,18 +157,24 @@ export function nextCronTime(expr, fromMs) {
|
|
|
157
157
|
const c = parseCron(expr);
|
|
158
158
|
if (!c)
|
|
159
159
|
return null;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
// The scan steps in UTC MILLISECONDS, not local minutes: on the autumn DST
|
|
161
|
+
// FALL-BACK an hour repeats, and `setMinutes(getMinutes()+1)` visits every
|
|
162
|
+
// wall-clock minute TWICE (then jumps over the second occurrence), so a
|
|
163
|
+
// 02:45 job created after the first 02:45 fired the NEXT DAY. A fixed
|
|
164
|
+
// 60-second step visits every real minute exactly once; the field checks
|
|
165
|
+
// still use the LOCAL getters, so the wall-clock match is unchanged.
|
|
166
|
+
const step = 60_000;
|
|
167
|
+
let t = Math.floor(fromMs / step) * step + step;
|
|
163
168
|
const limit = fromMs + 4 * 366 * 24 * 60 * 60 * 1000;
|
|
164
|
-
while (
|
|
169
|
+
while (t <= limit) {
|
|
170
|
+
const d = new Date(t);
|
|
165
171
|
if (c.minute.has(d.getMinutes()) &&
|
|
166
172
|
c.hour.has(d.getHours()) &&
|
|
167
173
|
c.month.has(d.getMonth() + 1) &&
|
|
168
174
|
dayMatches(c, d)) {
|
|
169
|
-
return
|
|
175
|
+
return t;
|
|
170
176
|
}
|
|
171
|
-
|
|
177
|
+
t += step;
|
|
172
178
|
}
|
|
173
179
|
return null;
|
|
174
180
|
}
|
|
@@ -256,6 +262,23 @@ export class Scheduler {
|
|
|
256
262
|
due(now) {
|
|
257
263
|
const fired = [];
|
|
258
264
|
for (const job of this.jobs) {
|
|
265
|
+
// A NON-FINITE `nextAt` (a restored/hand-edited job, a NaN from a broken
|
|
266
|
+
// date) passed every comparison below: `NaN > now` is false, so the job
|
|
267
|
+
// was reported due on EVERY tick and the reschedule arithmetic kept it
|
|
268
|
+
// NaN forever — the busy loop the zero-interval guard exists to stop.
|
|
269
|
+
if (!Number.isFinite(job.nextAt)) {
|
|
270
|
+
if (job.kind === 'loop' && job.intervalMs && job.intervalMs > 0) {
|
|
271
|
+
job.nextAt = now + job.intervalMs;
|
|
272
|
+
}
|
|
273
|
+
else if (job.kind === 'cron' && job.cron) {
|
|
274
|
+
const nxt = nextCronTime(job.cron, now);
|
|
275
|
+
job.nextAt = nxt === null ? Number.POSITIVE_INFINITY : nxt;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
job.nextAt = Number.POSITIVE_INFINITY;
|
|
279
|
+
}
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
259
282
|
if (job.nextAt > now)
|
|
260
283
|
continue;
|
|
261
284
|
fired.push({ ...job });
|
package/dist/tui/layout.js
CHANGED
|
@@ -20,8 +20,15 @@ export function fullScreenLayout(input) {
|
|
|
20
20
|
// width — and the pure helper stays consistent with what it renders.
|
|
21
21
|
const cols = Math.max(1, Math.floor(input.cols) || 80);
|
|
22
22
|
const box = inputBoxRows(input.prompt, input.buf, input.cursor, cols, input.borderCols ?? 2);
|
|
23
|
-
const
|
|
24
|
-
|
|
23
|
+
const wanted = Math.max(0, Math.floor(input.suggestRows) || 0);
|
|
24
|
+
// The layout must FIT the terminal: with a tall popup (a project with many
|
|
25
|
+
// custom commands) the reserved rows exceeded `termRows`, `logHeight` was
|
|
26
|
+
// clamped to 1 and the panel still overflowed — the input rows were pushed
|
|
27
|
+
// below the screen. The popup gives way first (it is a hint list), the log
|
|
28
|
+
// keeps at least one row.
|
|
29
|
+
const fixed = HEADER_ROWS + STATUS_ROWS + box + HINT_ROWS;
|
|
30
|
+
const suggestRows = Math.max(0, Math.min(wanted, termRows - fixed - 1));
|
|
31
|
+
const panelRows = fixed + suggestRows;
|
|
25
32
|
const logHeight = Math.max(1, termRows - panelRows);
|
|
26
33
|
// The caret counts the SAME viewport the reservation uses.
|
|
27
34
|
// The suggestion popup is drawn ABOVE the box and the box does NOT move, so
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.96.
|
|
3
|
+
"version": "2.96.4",
|
|
4
4
|
"description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen or GLM — 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": {
|