zames_pro 2.96.8 → 2.96.9

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 CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.96.9] - 2026-11-06
11
+
12
+ ### Fixed
13
+
14
+ - **statusline:** a backgrounded child no longer holds the badge until the timeout
15
+ - **resume:** a junk session record is skipped instead of crashing /sessions
16
+ - **model:** a model id with a newline is flattened to one line
17
+ - **context-limit:** a non-finite retry budget gives up instead of retrying forever
18
+ - **spinner:** a dots-only phrase keeps a label instead of going empty
19
+
10
20
  ## [2.96.8] - 2026-11-06
11
21
 
12
22
  ### Fixed
@@ -9,7 +9,11 @@ export async function recoverFromContextLimit(deps, original) {
9
9
  notice(t(keys.giveUp, { provider }));
10
10
  return { resend: false, error: original };
11
11
  };
12
- if (!recover || attempt > maxRetries)
12
+ // A NON-FINITE budget makes `attempt > maxRetries` false forever (`x > NaN`
13
+ // is false), so a chat that can never accept another message was
14
+ // re-compacted on every send with no budget at all. Require a real number.
15
+ const budget = Number.isFinite(maxRetries) && maxRetries >= 0 ? maxRetries : 0;
16
+ if (!recover || attempt > budget)
13
17
  return giveUp();
14
18
  notice(t(keys.compact, { provider }));
15
19
  let recovered = false;
@@ -12,7 +12,13 @@ export function parseModelIntent(provider, arg, t = translate('en')) {
12
12
  message: t('model.unsupported', { provider }),
13
13
  };
14
14
  }
15
- const model = String(arg ?? '').trim();
15
+ // A model id is a SINGLE token: the operator's text can carry a newline
16
+ // (a paste, a wrapped line), and it was stored and echoed verbatim — the
17
+ // newline broke the status line and the `/model set` report into fake
18
+ // rows. Collapse whitespace; an id is never allowed to contain a break.
19
+ const model = String(arg ?? '')
20
+ .replace(/\s+/g, ' ')
21
+ .trim();
16
22
  return model ? { kind: 'set', model } : { kind: 'list' };
17
23
  }
18
24
  /** The text for an EMPTY model list (a provider that answered with nothing). */
@@ -14,8 +14,13 @@ export function filterSessions(sessions, filter) {
14
14
  .toLowerCase();
15
15
  if (!q)
16
16
  return sessions.slice();
17
- return sessions.filter((s) => (s.title || '').toLowerCase().includes(q) ||
18
- (s.workdir || '').toLowerCase().includes(q));
17
+ // Session files are hand-editable and are written by OLDER builds: an array
18
+ // element can be `null`, and a field can be a number. Reading them blindly
19
+ // threw a raw TypeError out of `/sessions`.
20
+ const text = (v) => (typeof v === 'string' ? v : '');
21
+ return sessions.filter((s) => !!s &&
22
+ (text(s.title).toLowerCase().includes(q) ||
23
+ text(s.workdir).toLowerCase().includes(q)));
19
24
  }
20
25
  /**
21
26
  * A chat id only exists on the backend that created it, so opening a session
package/dist/spinner.js CHANGED
@@ -15,7 +15,10 @@ export function randomThinkingPhrase(locale = DEFAULT_LOCALE) {
15
15
  }
16
16
  // Strip the trailing ellipsis from the phrase — dots are animated separately.
17
17
  export function stripEllipsis(phrase) {
18
- return phrase.replace(/[.…]+\s*$/, '');
18
+ const out = phrase.replace(/[.…]+\s*$/, '');
19
+ // A phrase that is ONLY dots (an odd translation) must not become an EMPTY
20
+ // label: the spinner would then animate bare dots with no text at all.
21
+ return out === '' ? phrase.trim() : out;
19
22
  }
20
23
  // Dot animation shared by BOTH UIs (the ora spinner and the LineEditor): start
21
24
  // from an empty string (0 dots), then grow. The width is padded to the maximum
@@ -94,6 +94,15 @@ export function runStatusLineCommand(workdir, command, info, timeoutMs = 4000) {
94
94
  clearTimeout(timer);
95
95
  finish();
96
96
  });
97
+ // A NEWLINE already ends the FIRST line, and that is all the badge shows.
98
+ // A backgrounded grandchild keeps the pipe OPEN (`my-status &`), so `end`
99
+ // never fired and the badge waited out the whole timeout on every tick.
100
+ child.stdout?.on('data', (c) => {
101
+ if (String(c).includes(String.fromCharCode(10))) {
102
+ clearTimeout(timer);
103
+ finish();
104
+ }
105
+ });
97
106
  child.on('close', () => {
98
107
  clearTimeout(timer);
99
108
  finish();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.96.8",
3
+ "version": "2.96.9",
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": {