zames_pro 2.82.137 → 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.
Files changed (58) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/dist/agent-loop.js +3 -6
  3. package/dist/answer-parse.js +10 -16
  4. package/dist/attach-refs.js +3 -3
  5. package/dist/attachments.js +7 -8
  6. package/dist/backlog.js +2 -5
  7. package/dist/banner.js +4 -5
  8. package/dist/browser-chats.js +4 -4
  9. package/dist/browser-dom.js +16 -15
  10. package/dist/browser-login.js +9 -5
  11. package/dist/browser-pure.js +5 -5
  12. package/dist/browser.js +10 -20
  13. package/dist/changelog.js +14 -20
  14. package/dist/checkpoint.js +6 -10
  15. package/dist/cli-args.js +4 -6
  16. package/dist/code-blocks.js +3 -6
  17. package/dist/commands.js +5 -9
  18. package/dist/config-command.js +6 -4
  19. package/dist/config-commands.js +2 -3
  20. package/dist/config-live.js +7 -13
  21. package/dist/config-menu.js +5 -4
  22. package/dist/config.js +5 -8
  23. package/dist/deepseek-ui.js +7 -7
  24. package/dist/diagnostics.js +5 -7
  25. package/dist/diff.js +8 -10
  26. package/dist/extraTools.js +3 -3
  27. package/dist/fsutil.js +0 -4
  28. package/dist/gitTools.js +5 -4
  29. package/dist/glm-browser.js +2 -10
  30. package/dist/glm-chats.js +0 -1
  31. package/dist/glm-net-capture.js +1 -1
  32. package/dist/glm-ui.js +2 -2
  33. package/dist/goal.js +1 -4
  34. package/dist/i18n/common.js +2 -2
  35. package/dist/i18n.js +1 -2
  36. package/dist/index.js +0 -11
  37. package/dist/input/suggest.js +4 -7
  38. package/dist/input.js +3 -8
  39. package/dist/markdown.js +0 -1
  40. package/dist/mcp.js +2 -5
  41. package/dist/net-capture.js +1 -1
  42. package/dist/path-token.js +7 -14
  43. package/dist/prompt.js +4 -3
  44. package/dist/qwen-browser.js +1 -8
  45. package/dist/qwen-chats.js +0 -1
  46. package/dist/qwen-net-capture.js +4 -7
  47. package/dist/qwen-ui.js +0 -3
  48. package/dist/run-task.js +4 -4
  49. package/dist/self-review.js +0 -5
  50. package/dist/send-runtime.js +6 -10
  51. package/dist/send-telemetry.js +5 -7
  52. package/dist/send-throttle.js +4 -5
  53. package/dist/sessions.js +6 -7
  54. package/dist/spinner.js +3 -4
  55. package/dist/statusline.js +5 -9
  56. package/dist/system-prompt.js +1 -1
  57. package/dist/tui/widgets.js +0 -1
  58. package/package.json +1 -1
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 (N29): a read may end on a bare ESC that is really the
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 (N29).
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 (N29).
1594
+ // abort the generation.
1600
1595
  if (this._escPending) {
1601
1596
  this._escPending = false;
1602
1597
  if (this._escTimer) {
package/dist/markdown.js CHANGED
@@ -15,7 +15,6 @@ const theme = {
15
15
  tableHeader: { color: '#9fb8d8', bold: true },
16
16
  tableCell: { color: '#d0d0d0' },
17
17
  };
18
- // Code highlighting via cli-highlight (already present transitively).
19
18
  function highlighter(code, lang) {
20
19
  try {
21
20
  return highlight(code, {
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'), and agent-loop resolves a call with
481
- // tools.find(name) — it takes the FIRST. A duplicate would make the second
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 {
@@ -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 MIN_FINISHED_ANSWER_CHARS).
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.
@@ -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 / an email, NOT a file reference. The scan used to stop at
128
- // that at-sign and hand the PREFIX (`Component` … no) or, worse, the part
129
- // BEFORE it to the inliner. Now the whole token is rejected instead.
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 break above leaves `j` ON the comma; step past it so the next `@`
158
- // is seen as the start of a reference. When the scan ended on a
159
- // terminator instead, continue AFTER it.
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
- // We enable/disable bracketed paste in pairs.
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
- // Return to the start of the line, erase and print again.
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
- // Put the cursor in the right position.
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');
@@ -6,7 +6,7 @@
6
6
  // and the chat-id scheme. DeepSeekBrowser is already large; folding a second
7
7
  // site into it would make both harder to audit. The shared surface is the
8
8
  // ChatProvider interface (types.ts); the agent loop only needs BrowserLike and
9
- // never sees either concrete class.
9
+ // never sees a concrete class.
10
10
  //
11
11
  // Verified against a live session (headless Chromium, guest + logged in):
12
12
  // input -> textarea.message-input-textarea
@@ -233,7 +233,6 @@ export class QwenBrowser {
233
233
  sendIntervalMs() {
234
234
  return this.minSendIntervalMs;
235
235
  }
236
- // ---------- lifecycle ----------
237
236
  async launch() {
238
237
  if (await profileLooksLocked(QWEN_USER_DATA_DIR)) {
239
238
  if (this.debug)
@@ -487,7 +486,6 @@ export class QwenBrowser {
487
486
  }
488
487
  catch { }
489
488
  }
490
- // ---------- login ----------
491
489
  // Are we signed in? Detected from the PAGE, the same way a human sees it: a
492
490
  // signed-out visitor has the header "Log in" / "Sign up" buttons, a signed-in
493
491
  // one shows the account menu instead. The `auths/refresh` API was the first
@@ -1005,7 +1003,6 @@ export class QwenBrowser {
1005
1003
  }
1006
1004
  return false;
1007
1005
  }
1008
- // ---------- chat management ----------
1009
1006
  async newChat() {
1010
1007
  const before = await this.getCurrentChatId();
1011
1008
  const btn = this.page.locator(NEW_CHAT_SELECTOR).first();
@@ -1272,7 +1269,6 @@ export class QwenBrowser {
1272
1269
  getLastTokenUsage() {
1273
1270
  return this._lastTokenUsage;
1274
1271
  }
1275
- // ---------- answer reading ----------
1276
1272
  async _readLastAnswerText() {
1277
1273
  if (this._netCapture && this._netCaptureAt >= this._lastSentAt) {
1278
1274
  return this._netCapture;
@@ -1346,7 +1342,6 @@ export class QwenBrowser {
1346
1342
  catch { }
1347
1343
  return false;
1348
1344
  }
1349
- // ---------- toggles (model thinking / search) ----------
1350
1345
  getToggleStatesSync() {
1351
1346
  return this._toggles;
1352
1347
  }
@@ -1523,7 +1518,6 @@ export class QwenBrowser {
1523
1518
  }
1524
1519
  catch { }
1525
1520
  }
1526
- // ---------- diagnostics ----------
1527
1521
  async dumpDom(filePath) {
1528
1522
  if (!this.page)
1529
1523
  throw new Error('qwen: browser not launched');
@@ -1566,7 +1560,6 @@ export class QwenBrowser {
1566
1560
  chatUrl(id) {
1567
1561
  return chatUrl(id);
1568
1562
  }
1569
- // ---------- ask ----------
1570
1563
  async _waitForSendSlot(agent) {
1571
1564
  if (!agent)
1572
1565
  return;
@@ -84,7 +84,6 @@ export function scrapeQwenChatMessages(opts) {
84
84
  }
85
85
  return out;
86
86
  }
87
- // ---------- API payload parsers (pure, unit-tested) ----------
88
87
  /**
89
88
  * Parse the chat list from `GET /api/v2/chats/?page=1&exclude_project=true`.
90
89
  * The shape has drifted between builds, so accept several:
@@ -223,13 +223,10 @@ export function isFinishedWithoutAnswer(body) {
223
223
  return extractFromSse(b) === '';
224
224
  }
225
225
  // True when the body carries Qwen's rate-limit / busy notice, so ask() can
226
- // back off instead of waiting out the deadline. Matched against the RAW body.
227
- // The classifier runs against the RAW body, so a phrase like `please try again
228
- // later` would also match a NORMAL answer (the model writing about retry
229
- // logic) and make ask() back off on a good turn. A real notice arrives in an
230
- // ERROR payload (`event: error` / `"type":"error"`) or as a code; answer text
231
- // lives in `choices[].delta.content`. We therefore only inspect the body when
232
- // it is NOT a plain answer stream, or when it carries no answer content.
226
+ // back off instead of waiting out the deadline. The classifier runs against the
227
+ // RAW body, where a phrase like `please try again later` also matches a NORMAL
228
+ // answer (the model writing about retry logic) and made ask() back off on a
229
+ // good turn. So it only counts an ERROR payload or a body with NO answer text.
233
230
  export function isRateLimitText(body) {
234
231
  const b = String(body || '');
235
232
  if (!b)
package/dist/qwen-ui.js CHANGED
@@ -174,9 +174,6 @@ export const STATUS_RE = /^(thinking|searching|generating|thinking completed|о
174
174
  // Qwen's rate-limit / busy notices (best-effort; the toast may be short-lived).
175
175
  export const RATE_LIMIT_RE = /too many requests|rate limit|too frequent|please try again later|слишком часто/i;
176
176
  export const SERVER_BUSY_RE = /server is busy|service unavailable|try again later/i;
177
- // ---------- toggles ----------
178
- // The "Auto" thinking/search toggle and the mode selector. Read-only probing
179
- // support; the model's thinking mode is set via config when supported.
180
177
  // ---------- toggles (thinking mode / web search) ----------
181
178
  // Qwen exposes thinking as a MODE dropdown (`.qwen-thinking-selector`, label
182
179
  // "Auto" by default) with options Auto / Thinking / Fast — NOT a boolean
package/dist/run-task.js CHANGED
@@ -330,7 +330,7 @@ export async function runTask(deps, browser, tools, taskText, workdir, opts, att
330
330
  });
331
331
  try {
332
332
  // Set when the loop ends without a real answer (iteration limit / watchdog)
333
- // so the one-shot mode can exit non-zero. See N5.
333
+ // so the one-shot mode can exit non-zero.
334
334
  let failure = null;
335
335
  // The machine-readable reason for the last task's outcome.
336
336
  let outcomeKind = 'ok';
@@ -351,7 +351,7 @@ export async function runTask(deps, browser, tools, taskText, workdir, opts, att
351
351
  // Calling it here made the spinner run for the whole pre-send phase (chat
352
352
  // creation, throttle wait) with no generation in flight.
353
353
  while (true) {
354
- // T7: per-task summary (duration + number of tool calls + tokens).
354
+ // Per-task summary: duration, number of tool calls and tokens spent.
355
355
  const taskStart = Date.now();
356
356
  // Token delta for THIS task: accumulated_token_usage is cumulative for
357
357
  // the whole chat, so the per-task spend is after - before. Null when the
@@ -461,8 +461,8 @@ export async function runTask(deps, browser, tools, taskText, workdir, opts, att
461
461
  else {
462
462
  outcomeKind = 'ok';
463
463
  }
464
- // T7: one summary line per task (duration + tool calls + tokens spent).
465
- // Printed even on an abort, so the operator sees what happened.
464
+ // One summary line per task, printed even on an abort, so the operator
465
+ // sees what happened.
466
466
  const tokensAfter = getTokenUsage ? getTokenUsage() : null;
467
467
  const tokenDelta = typeof tokensBefore === 'number' && typeof tokensAfter === 'number'
468
468
  ? Math.max(0, tokensAfter - tokensBefore)
@@ -122,7 +122,6 @@ async function copyContextFiles(srcDir, snapDir) {
122
122
  }
123
123
  }
124
124
  }
125
- // ---------- /self-review ----------
126
125
  export async function selfReview({ browser, focus, transcript, locale, }) {
127
126
  const t = translator(locale);
128
127
  const stamp = new Date().toISOString().replace(/[:.]/g, '-');
@@ -190,7 +189,6 @@ export async function selfReview({ browser, focus, transcript, locale, }) {
190
189
  console.log();
191
190
  return { snapDir, reportPath, changed };
192
191
  }
193
- // ---------- /self-diff ----------
194
192
  export async function selfDiff({ name, locale, }) {
195
193
  const t = translator(locale);
196
194
  const snapDir = path.join(SNAP_ROOT, name);
@@ -218,7 +216,6 @@ export async function selfDiff({ name, locale, }) {
218
216
  console.log(theme.system(t('self.no_diff')));
219
217
  }
220
218
  }
221
- // ---------- /self-apply ----------
222
219
  export async function selfApply({ name, locale, }) {
223
220
  const t = translator(locale);
224
221
  const snapDir = path.join(SNAP_ROOT, name);
@@ -247,7 +244,6 @@ export async function selfApply({ name, locale, }) {
247
244
  console.log(theme.system(t('self.backup', { dir: backupDir })));
248
245
  console.log(theme.warn('\n' + t('self.restart_hint') + '\n'));
249
246
  }
250
- // ---------- /self-list ----------
251
247
  export async function selfList({ locale, } = {}) {
252
248
  const t = translator(locale);
253
249
  const snapRoot = path.join(SNAP_ROOT);
@@ -285,7 +281,6 @@ export async function selfList({ locale, } = {}) {
285
281
  console.log();
286
282
  console.log(theme.system(t('self.list_commands') + '\n'));
287
283
  }
288
- // ---------- helpers ----------
289
284
  async function diffFiles(origDir, newDir) {
290
285
  const files = await listTsFiles(newDir).catch(() => []);
291
286
  const changed = [];
@@ -78,11 +78,9 @@ export class SendRuntime {
78
78
  if (typeof extra.retryAfterMs === 'number') {
79
79
  e.retryAfterMs = extra.retryAfterMs;
80
80
  }
81
- // A SUCCESS after a rate limit IS the lift of that block. onBlockLifted()
82
- // was the only producer of `blockMs`, but no provider's ask() calls it (a
83
- // rate limit is retried IN PLACE and the retry simply succeeds), and the
84
- // fold below clears `blockedAt` first. The measured duration is therefore
85
- // taken HERE, before the state is folded; it is attached to the SAME event
81
+ // A SUCCESS after a rate limit IS the lift of that block: the retry happens
82
+ // IN PLACE and simply succeeds. The duration is therefore measured HERE,
83
+ // before the fold below clears `blockedAt`, and attached to the SAME event
86
84
  // line, so one block still yields exactly one sample.
87
85
  let blockMs;
88
86
  if (outcome === 'ok' &&
@@ -101,11 +99,9 @@ export class SendRuntime {
101
99
  this.state = { ...this.state, blocked: true, blockedAt: this.now() };
102
100
  }
103
101
  if (outcome === 'rate_limit') {
104
- // The block START is only known AFTER the fold above (the adaptive path
105
- // sets it there). The event itself carries NO duration: a rate-limit
106
- // send is not the END of the block, so any value measured here is a
107
- // lower bound that would then be averaged together with the real
108
- // duration written by onBlockLifted — the same block counted TWICE.
102
+ // The event carries NO duration: a rate-limit send is not the END of the
103
+ // block, so a value measured here would be averaged together with the
104
+ // real duration measured at the lift — the same block counted TWICE.
109
105
  if (!this.state.blockedAt) {
110
106
  this.state = { ...this.state, blocked: true, blockedAt: this.now() };
111
107
  }
@@ -51,14 +51,12 @@ export function summarizeSends(events, now = Date.now()) {
51
51
  const age = now - t;
52
52
  return age >= 0 && age <= ms;
53
53
  }).length;
54
- // `blockMs` may arrive as a NUMBER (the writer) or a string (an older
55
- // build, a hand-edited file): a numeric string is accepted so the average
56
- // is not silently 0 for a file that records the block.
57
54
  const blocks = events
58
- // `Number(null)`/`Number(undefined)` — 0/NaN, and 0 IS a finite
59
- // non-negative number: an event that carries no block duration (the
60
- // rate-limit event only records `blockedAt`) was averaged as a ZERO-length
61
- // block, halving the reported average. Only a REAL value counts.
55
+ // `blockMs` may arrive as a NUMBER (the writer) or a string (an older
56
+ // build, a hand-edited file), so it is coerced. A missing value must NOT
57
+ // become 0: 0 is a finite non-negative number, and an event without a
58
+ // block duration (the rate-limit event only records `blockedAt`) was
59
+ // averaged as a ZERO-length block, halving the reported average.
62
60
  .map((e) => e.blockMs === null || e.blockMs === undefined ? NaN : Number(e.blockMs))
63
61
  // A finite value is not enough: the SUM of many large durations overflows
64
62
  // a JS number (two `Number.MAX_VALUE` entries add up to Infinity), so the
@@ -39,11 +39,10 @@ function prune(recent, now, windowMs) {
39
39
  */
40
40
  export function nextWaitMs(state, lastSentAt, now) {
41
41
  if (state.forcedWaitMs > 0) {
42
- // A forced wait must EXPIRE: a long Retry-After used to block every
43
- // send forever (the check was `> 0` only), until another event cleared
44
- // it. With the request stamp the remaining time is exact; without one
45
- // (a caller-built or restored state) the wait is reported ONCE, from
46
- // the current call, and expires on the next.
42
+ // A forced wait must EXPIRE: with only `forcedWaitMs > 0` a long
43
+ // Retry-After blocked every send until another event cleared it. The
44
+ // request stamp makes the remaining time exact; without one (a caller-built
45
+ // or restored state) the wait is reported ONCE, from the current call.
47
46
  if (state.forcedWaitAt) {
48
47
  const left = state.forcedWaitMs - (now - state.forcedWaitAt);
49
48
  return left > 0 ? left : 0;
package/dist/sessions.js CHANGED
@@ -133,13 +133,12 @@ function writeIndex({ id, workdir }) {
133
133
  }
134
134
  catch { }
135
135
  }
136
- // The last session for the working directory. If there is none —
137
- // we return the last session overall (useful when launched from a new place).
138
- // The last session for the working directory, restricted to `provider` when
139
- // given. A chat id only exists on ITS backend, so restoring a DeepSeek chat
140
- // while the active provider is Qwen (or vice versa) would fail to open — the
141
- // filter makes auto-restore and /last pick the right one (and fall back to the
142
- // global last only within the same provider).
136
+ // The last session for the working directory (the last one overall when the
137
+ // directory has none — useful when launched from a new place), restricted to
138
+ // `provider` when given. A chat id only exists on ITS backend, so restoring a
139
+ // DeepSeek chat while the active provider is Qwen (or vice versa) would fail to
140
+ // open; the filter makes auto-restore and /last pick the right one, and fall
141
+ // back to the global last only within the same provider.
143
142
  export function loadLastSession(workdir = '', provider) {
144
143
  try {
145
144
  const index = JSON.parse(fs.readFileSync(INDEX_FILE, 'utf-8'));
package/dist/spinner.js CHANGED
@@ -152,10 +152,9 @@ export function createSpinner(locale = 'ru') {
152
152
  }
153
153
  startThinking();
154
154
  },
155
- // The agent is waiting out the send-interval pause before a real send.
156
- // Show it as an ANIMATED status line with the remaining seconds, so the
157
- // spinner keeps moving during the pause (previously a static console line
158
- // was printed and the dots stayed frozen).
155
+ // The agent is waiting out the send-interval pause before a real send: an
156
+ // ANIMATED status with the remaining seconds, so the spinner keeps moving
157
+ // (a static line made the pause look frozen).
159
158
  sendPause: (seconds) => {
160
159
  if (pending)
161
160
  return;
@@ -58,10 +58,6 @@ export function runStatusLineCommand(workdir, command, info, timeoutMs = 4000) {
58
58
  // (the row overflows and the cursor arithmetic drifts).
59
59
  resolve(clipBadge(first));
60
60
  };
61
- // Only the FIRST LINE is shown, so there is no reason to keep collecting a
62
- // big blob: a chatty command (a JSON dump, `ls -R`) would grow `out`
63
- // unboundedly and the status row then repainted megabytes. Stop appending
64
- // once a short line is buffered (the rest of that line does not matter).
65
61
  const timer = setTimeout(() => {
66
62
  try {
67
63
  if (process.platform !== 'win32' && child.pid) {
@@ -74,15 +70,15 @@ export function runStatusLineCommand(workdir, command, info, timeoutMs = 4000) {
74
70
  catch { }
75
71
  finish();
76
72
  }, timeoutMs);
77
- // A status line is a short badge: cap what is buffered so a chatty
78
- // command cannot grow it (and the rendered row) without bound.
73
+ // Only the FIRST line is shown and the status line is a short badge, so the
74
+ // buffer is capped: a chatty command (a JSON dump, `ls -R`) would otherwise
75
+ // grow `out` and make the status row repaint megabytes on every tick.
79
76
  const MAX_OUT = 400;
80
77
  child.stdout?.on('data', (c) => {
81
78
  if (out.length >= MAX_OUT)
82
79
  return;
83
- // A chunk can be huge (one 64 KB read); clip it to the remaining space
84
- // so the buffer never exceeds MAX_OUT. An unbounded `out` made the
85
- // status row repaint megabytes on every tick.
80
+ // A chunk can be huge (one 64 KB read): clip it to the remaining space so
81
+ // the buffer never exceeds MAX_OUT.
86
82
  out += c.toString().slice(0, MAX_OUT - out.length);
87
83
  });
88
84
  child.on('error', () => {
@@ -124,7 +124,7 @@ export function renderContextSection(context) {
124
124
  f.content +
125
125
  '\n';
126
126
  }
127
- // B6: nested AGENTS.md/MEMORY.md from the directories this task touches.
127
+ // Nested AGENTS.md/MEMORY.md from the directories this task touches.
128
128
  // Deliberately a SEPARATE section: these apply only to part of the tree and
129
129
  // must not be mistaken for project-wide rules.
130
130
  for (const f of context.scopedAgents || []) {
@@ -1,6 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
- // ---- palette (kept in one place so every menu is consistent) ----
4
3
  export const UI = {
5
4
  accent: '#c8b06a',
6
5
  accentDim: '#7a6b45',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.82.137",
3
+ "version": "2.82.138",
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": {