zames_pro 2.34.0 → 2.35.0

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/dist/commands.js CHANGED
@@ -35,12 +35,15 @@ export function summarizeTranscript(entries) {
35
35
  toolCounts: {},
36
36
  durationMs: 0,
37
37
  startedAt: null,
38
+ autoCompacts: 0,
38
39
  };
39
40
  for (const e of entries) {
40
41
  if (!e || typeof e !== 'object')
41
42
  continue;
42
43
  if (e.type === 'user_task')
43
44
  stats.turns++;
45
+ if (e.type === 'auto_compact_done')
46
+ stats.autoCompacts++;
44
47
  if (e.type === 'tool_call') {
45
48
  stats.toolCalls++;
46
49
  const tool = String(e.tool ?? '?');
@@ -97,6 +100,9 @@ export function renderCost(stats, transcriptFile, tokenUsage = null) {
97
100
  }
98
101
  lines.push(' tasks: ' + stats.turns);
99
102
  lines.push(' tool calls: ' + stats.toolCalls);
103
+ if (stats.autoCompacts > 0) {
104
+ lines.push(' auto-compacts: ' + stats.autoCompacts);
105
+ }
100
106
  const top = Object.entries(stats.toolCounts).sort((a, b) => b[1] - a[1]);
101
107
  if (top.length) {
102
108
  lines.push(' by tool:');
@@ -119,6 +125,15 @@ export function formatExport(entries, meta = {}) {
119
125
  if (meta.chatId)
120
126
  out.push('- chat: ' + meta.chatId);
121
127
  out.push('- exported: ' + new Date().toISOString());
128
+ // A self-contained summary, so the exported Markdown stands on its own as a
129
+ // report without opening the transcript or a /cost run.
130
+ const stats = summarizeTranscript(entries);
131
+ out.push('- tasks: ' + stats.turns);
132
+ out.push('- tool calls: ' + stats.toolCalls);
133
+ out.push('- duration: ' + formatDuration(stats.durationMs));
134
+ if (stats.autoCompacts > 0) {
135
+ out.push('- auto-compacts: ' + stats.autoCompacts);
136
+ }
122
137
  out.push('');
123
138
  for (const e of entries) {
124
139
  const type = String(e.type ?? '?');
@@ -181,6 +196,9 @@ export function renderDoctor(d) {
181
196
  if (typeof d.minSendIntervalMs === 'number') {
182
197
  row(d.minSendIntervalMs > 0, 'send pause', Math.round(d.minSendIntervalMs / 1000) + 's between agent sends');
183
198
  }
199
+ if (typeof d.sshRemote === 'boolean') {
200
+ row(true, 'session', d.sshRemote ? 'SSH remote' : 'local terminal');
201
+ }
184
202
  return 'Doctor:' + NL + rows.join(NL);
185
203
  }
186
204
  export function renderPermissions(p) {
@@ -482,6 +500,15 @@ export function parseGoalCommand(text) {
482
500
  return { sub: 'clear' };
483
501
  return { sub: 'set', goal: rest };
484
502
  }
503
+ // True when a `/config ...` line can be handled LIVE (while the agent is busy):
504
+ // the text subcommands only touch config values, never the chat. The bare
505
+ // `/config` and `/config menu` (which pause the editor and read keys) are NOT
506
+ // live — they must go through the main loop, so a mid-run menu never fights
507
+ // the running task. Pure, so the interception is unit-tested.
508
+ export function isLiveConfigCommand(text) {
509
+ return /^\/config\s+(?!menu\b|ui\b)\S+/i.test(String(text ?? '').trim());
510
+ }
511
+ // Parse a `/queue` invocation. Returns the subcommand ('list' | 'clear') or
485
512
  // null when the text is not a /queue command at all. This runs WHILE the agent
486
513
  // is busy (intercepted in the input handler), because the main command loop is
487
514
  // blocked on runTask() and a queued /queue would only run after the task —
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ import { translate, normalizeLocale, localeDisplayName, isLocale, } from './i18n
16
16
  import { Transcript } from './transcript.js';
17
17
  import { UndoStore } from './undo.js';
18
18
  import { selfReview, selfDiff, selfApply, selfList } from './self-review.js';
19
- import { formatDiff, diffGitArgs, parseTranscript, summarizeTranscript, renderCost, formatExport, defaultExportPath, renderDoctor, renderPermissions, resolveExtraDir, buildReviewPrompt, formatDuration, trimRestoredMessages, RESTORED_HISTORY_LIMIT, mergeMessages, isSlashCommand, parseQueueCommand, parseLiveToggle, parseGoalCommand, withGoal, formatQueueList, ctrlCEscalation, } from './commands.js';
19
+ import { formatDiff, diffGitArgs, parseTranscript, summarizeTranscript, renderCost, formatExport, defaultExportPath, renderDoctor, renderPermissions, resolveExtraDir, buildReviewPrompt, formatDuration, trimRestoredMessages, RESTORED_HISTORY_LIMIT, mergeMessages, isSlashCommand, parseQueueCommand, parseLiveToggle, parseGoalCommand, isLiveConfigCommand, withGoal, formatQueueList, ctrlCEscalation, } from './commands.js';
20
20
  import { performCompact } from './compact.js';
21
21
  import { Scheduler, parseInterval, formatInterval, parseCron, } from './scheduler.js';
22
22
  import { renderMarkdown } from './markdown.js';
@@ -1466,6 +1466,9 @@ async function main() {
1466
1466
  // The editor pulls the number on every render, so it follows the live
1467
1467
  // DeepSeek counter (accumulated_token_usage) without a polling timer.
1468
1468
  ed.onContextQuery = () => browser.getLastTokenUsage();
1469
+ // Queue badge: show "⧗N" while messages are waiting to be sent after the
1470
+ // current task. Pulled on every render (like the context counter).
1471
+ ed.onQueueQuery = () => pendingQueue.length;
1469
1472
  // Use the configured context window for the fill percentage/color.
1470
1473
  ed.setContextLimit(config.ui.contextLimit);
1471
1474
  // Toggle icons (🧠 deep thinking, 🌐 web search) before the context
@@ -1596,6 +1599,16 @@ async function main() {
1596
1599
  applyLiveToggle(tg);
1597
1600
  return;
1598
1601
  }
1602
+ // `/config <sub>` text commands (list/get/set/reset/lang) are safe
1603
+ // mid-run: they only touch config values, never the chat. The
1604
+ // interactive menu (`/config` bare or `menu`) is NOT intercepted —
1605
+ // it pauses the editor and would fight the running task, so it stays
1606
+ // queued. `setConfigRuntime` already applies hot values (locale,
1607
+ // toggles, context limit) to the live objects immediately.
1608
+ if (isLiveConfigCommand(text)) {
1609
+ void handleConfigCommand(text);
1610
+ return;
1611
+ }
1599
1612
  }
1600
1613
  const attachments = (items || []).map((a) => ({
1601
1614
  path: a.path,
@@ -2767,6 +2780,7 @@ async function main() {
2767
2780
  ? (await runGit('git remote', currentWorkdir, 5000)).includes('origin')
2768
2781
  : undefined,
2769
2782
  minSendIntervalMs: config.browser.minSendIntervalMs,
2783
+ sshRemote: !!(process.env.SSH_CONNECTION || process.env.SSH_TTY),
2770
2784
  })));
2771
2785
  continue;
2772
2786
  }
package/dist/input.js CHANGED
@@ -310,6 +310,13 @@ export class LineEditor {
310
310
  contextStatus;
311
311
  // The raw token count behind contextStatus (for the color level).
312
312
  contextTokens;
313
+ // Number of messages waiting in the queue, shown as a small "⧗N" badge in
314
+ // the status line so the operator sees that typed-ahead messages are still
315
+ // pending. 0 hides it. Updated by the caller.
316
+ queueLength;
317
+ // A callback the editor calls before every render to read the CURRENT queue
318
+ // length, so the badge stays live without the caller polling.
319
+ onQueueQuery;
313
320
  // Whether the DeepSeek "Deep thinking" / "Smart search" toggles are ON.
314
321
  // Shown as two colored icons before the context counter: a dim gray icon
315
322
  // means off, a teal-green icon means on.
@@ -388,6 +395,8 @@ export class LineEditor {
388
395
  this.contextLimit = CONTEXT_LIMIT;
389
396
  this.onToggleQuery = null;
390
397
  this.onContextQuery = null;
398
+ this.queueLength = 0;
399
+ this.onQueueQuery = null;
391
400
  }
392
401
  // Refresh the toggle icon states from onToggleQuery (if wired) before a
393
402
  // render. A broken callback must never break the render.
@@ -436,6 +445,23 @@ export class LineEditor {
436
445
  return theme.warn(ctx);
437
446
  return theme.success(ctx);
438
447
  }
448
+ // The queue badge ("⧗N") shown before the context counter when messages are
449
+ // waiting. Returns '' when the queue is empty, so it never clutters an idle
450
+ // status line. Refreshed from onQueueQuery before every render when wired.
451
+ _queueBadge() {
452
+ if (this.onQueueQuery) {
453
+ try {
454
+ const n = this.onQueueQuery();
455
+ this.queueLength = typeof n === 'number' && n > 0 ? n : 0;
456
+ }
457
+ catch {
458
+ // A broken callback must never break the render.
459
+ }
460
+ }
461
+ if (this.queueLength <= 0)
462
+ return '';
463
+ return theme.warn('⧗' + this.queueLength) + ' ';
464
+ }
439
465
  // Icons for the DeepSeek toggles: 🧠 "Deep thinking" and 🌐 "Smart search",
440
466
  // shown before the context counter. Only the ENABLED toggles are shown, so
441
467
  // the state is unambiguous WITHOUT relying on the terminal coloring the
@@ -671,7 +697,7 @@ export class LineEditor {
671
697
  // but the icons are still meaningful.
672
698
  const ctxRaw = this._contextText();
673
699
  this._refreshToggles();
674
- const ctxText = this._toggleIcons() + ctxRaw;
700
+ const ctxText = this._queueBadge() + this._toggleIcons() + ctxRaw;
675
701
  let statusOut = '';
676
702
  let top = 0;
677
703
  // Never fill a row to the FULL terminal width: on terminals with
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.34.0",
3
+ "version": "2.35.0",
4
4
  "description": "Terminal coding agent over chat.deepseek.com via Playwright",
5
5
  "type": "module",
6
6
  "bin": {