dsh-ssh-tui 0.3.2 → 0.3.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.
@@ -7,7 +7,8 @@
7
7
  *
8
8
  * The renderer uses plain ANSI and coalesces each frame into one stdout
9
9
  * write of dirty rows only — jump-host / proxied SSH should see one packet
10
- * per paint, not one per line. Cadence is DSH_TUI_PAINT_MS (default 160).
10
+ * per paint, not one per line. Cadence is DSH_TUI_PAINT_MS, else local 80 ms
11
+ * or an SSH tier from a CSI 6n round-trip (default 160 ms).
11
12
  */
12
13
  import type { Agent, ModelSelection, ModelSelectionRef } from '@deepseek-ai/dsh-agent';
13
14
  import type { Context } from '@deepseek-ai/cordis';
@@ -142,6 +143,17 @@ type Row = {
142
143
  phase: 'active' | 'paused' | 'blocked' | 'complete' | 'cleared';
143
144
  blockedReason?: string;
144
145
  expanded: boolean;
146
+ } | {
147
+ kind: 'compaction';
148
+ compactionId: string;
149
+ status: 'running' | 'ok' | 'error';
150
+ startedAt: number;
151
+ endedAt?: number;
152
+ pruneCount: number;
153
+ prunedTokens: number;
154
+ summary?: string;
155
+ error?: string;
156
+ expanded: boolean;
145
157
  } | {
146
158
  kind: 'system';
147
159
  text: string;
@@ -160,11 +172,21 @@ interface ToolDiffHunk {
160
172
  export interface TuiController {
161
173
  dispose(): Promise<void>;
162
174
  }
175
+ export type PaintLinkKind = 'local' | 'ssh';
163
176
  /**
164
- * Paint cadence for jump-host / proxied SSH. Token ticks coalesce into one
165
- * frame; the default stays snappy, slower links raise `DSH_TUI_PAINT_MS`.
177
+ * Explicit env/config always wins. Otherwise local TTYs stay snappy and SSH
178
+ * sessions pick a tier from a measured round-trip (CSI 6n), falling back to
179
+ * 160 ms when the probe is missing.
166
180
  */
167
- export declare function resolvePaintIntervalMs(configured?: number, env?: NodeJS.ProcessEnv): number;
181
+ export declare function resolvePaintIntervalMs(configured?: number, env?: NodeJS.ProcessEnv, options?: {
182
+ ssh?: boolean;
183
+ rttMs?: number;
184
+ }): number;
185
+ /** True when this process is attached to an SSH session (jump host / proxy). */
186
+ export declare function detectSshSession(env?: NodeJS.ProcessEnv): boolean;
187
+ /** Map a CSI-6n round-trip to a paint cadence. Unknown RTT uses the SSH default. */
188
+ export declare function paintIntervalForRtt(rttMs: number | undefined): number;
189
+ export declare function paintLinkLabel(kind: PaintLinkKind, intervalMs: number, probed: boolean): string;
168
190
  /** One incremental paint as a single stdout write (one SSH packet when corked). */
169
191
  export declare function composePaintOutput(options: {
170
192
  width: number;
@@ -250,10 +272,50 @@ export interface OpenCodeSource {
250
272
  * routes are recognized by their `opencode.ai` base URL.
251
273
  */
252
274
  export declare function openCodeSourceFor(provider: string, llmPiAiSection: unknown): OpenCodeSource | null;
275
+ export type QuotaPeriod = 'hourly' | 'weekly' | 'monthly' | 'unknown';
276
+ export interface QuotaWindow {
277
+ label: string;
278
+ period: QuotaPeriod;
279
+ /** Remaining percent of the window (100 = unused). */
280
+ remainingPercent: number;
281
+ resetsAt?: string;
282
+ }
283
+ export interface QuotaSnapshot {
284
+ provider: string;
285
+ plan: string;
286
+ windows: QuotaWindow[];
287
+ }
288
+ export declare function remainingPercentFromUsed(usedPercent: number): number;
289
+ /** Cross a remaining-percent threshold from above (50 / 25 / 10 / 5). */
290
+ export declare function crossedQuotaThresholds(previousRemaining: number | undefined, remaining: number): number[];
291
+ export declare function quotaAlertText(snapshot: QuotaSnapshot, window: QuotaWindow): string;
292
+ /**
293
+ * How often to re-fetch quota, based on the tightest window.
294
+ * Hourly/5h: every 10 turns, every 4 when near a threshold.
295
+ * Weekly: every 50 turns, every 10 when near.
296
+ * Monthly: every 80 turns, every 20 when near.
297
+ */
298
+ export declare function quotaRefreshEveryTurns(window: QuotaWindow | undefined): number;
299
+ export declare function parseSuperGrokBilling(payload: unknown): QuotaSnapshot;
300
+ export declare function parseOpenCodeGoQuota(payload: unknown, provider: string): QuotaSnapshot;
301
+ export declare function formatQuotaSnapshot(snapshot: QuotaSnapshot): string;
302
+ /** Tightest remaining window — used for threshold alerts. */
303
+ export declare function tightestQuotaWindow(snapshot: QuotaSnapshot): QuotaWindow | undefined;
253
304
  /** Render the OpenCode Go quota payload as a transcript block. */
254
305
  export declare function formatOpenCodeGoUsage(payload: unknown, source: OpenCodeSource): string;
255
306
  /** Whether `text` could still grow into a recognized escape sequence. */
256
307
  export declare function isEscapePrefix(text: string): boolean;
308
+ /** Parse a Device Status Report cursor reply (`CSI row;col R`). */
309
+ export declare function parseCursorPositionReply(text: string): {
310
+ row: number;
311
+ column: number;
312
+ } | undefined;
313
+ /**
314
+ * Round-trip to the attached terminal via CSI 6n. Returns undefined when the
315
+ * reply never arrives (dumb pipe, blocked DSR). Does not interpret the
316
+ * coordinates — only the elapsed milliseconds matter.
317
+ */
318
+ export declare function probeTerminalRttMs(stdin?: NodeJS.ReadStream, stdout?: NodeJS.WriteStream, timeoutMs?: number): Promise<number | undefined>;
257
319
  /** True while a plan still belongs in the dock (latest incomplete work). */
258
320
  export declare function planIsLive(plan: {
259
321
  active: boolean;
@@ -285,6 +347,12 @@ export declare function parseFindQuery(raw: string): {
285
347
  category?: CardCategory;
286
348
  query: string;
287
349
  };
350
+ export declare function compactionHeaderText(row: {
351
+ status: 'running' | 'ok' | 'error';
352
+ pruneCount: number;
353
+ prunedTokens: number;
354
+ error?: string;
355
+ }): string;
288
356
  /** Transcript rows matching a `/find` query, newest last. */
289
357
  export declare function matchTranscriptRows(rows: readonly Row[], raw: string): Row[];
290
358
  /** One-line note under an expanded plan strip. */
@@ -423,7 +491,15 @@ export declare class SshTui {
423
491
  private lastChromeKey;
424
492
  private lastPaintWidth;
425
493
  private lastPaintHeight;
426
- private readonly paintIntervalMs;
494
+ private paintIntervalMs;
495
+ private paintLink;
496
+ private paintProbed;
497
+ private sessionTitle;
498
+ private llmRetry;
499
+ private quotaSnapshot;
500
+ private quotaAlerted;
501
+ private quotaTurnsSinceRefresh;
502
+ private quotaRefreshInFlight;
427
503
  private searchHits;
428
504
  private searchIndex;
429
505
  private searchQuery;
@@ -432,6 +508,8 @@ export declare class SshTui {
432
508
  constructor(ctx: Context, agent: Agent, config: TuiConfig);
433
509
  /** Enter raw mode, switch to the alternate screen, and start listening. */
434
510
  start(): void;
511
+ private startRenderTimer;
512
+ private calibratePaintInterval;
435
513
  /** Replay the durable session log so a resumed session renders its history. */
436
514
  replayHistory(): void;
437
515
  /** Show the first-launch provider/API-key onboarding when nothing is configured. */
@@ -512,6 +590,10 @@ export declare class SshTui {
512
590
  private readonly handleDisposed;
513
591
  /** Plan-mode / command / team events that plugins merge into SessionEventMap. */
514
592
  private handleExtensionEvent;
593
+ private findCompactionRow;
594
+ private handleCompactionEvent;
595
+ private handleCommandRun;
596
+ private handleCommandDone;
515
597
  private handleGoalChange;
516
598
  private handleSubagentExtensionEvent;
517
599
  /** Fold a live subagent's own session events into that child's card. */
@@ -590,8 +672,13 @@ export declare class SshTui {
590
672
  private fetchOpenCodeGoUsage;
591
673
  /** Explain Zen metered billing instead of pretending it has a quota. */
592
674
  private zenUsageText;
593
- /** /usage and /quota: show Zen billing info or live Go quota usage. */
675
+ /** /usage and /quota: remaining quota for the current subscription provider. */
594
676
  private runUsageCommand;
677
+ private applyQuotaSnapshot;
678
+ private refreshQuota;
679
+ private fetchQuotaSnapshot;
680
+ private resolveSuperGrokToken;
681
+ private fetchJson;
595
682
  private readonly handleData;
596
683
  /** Handle one data chunk that may contain bracketed-paste markers. */
597
684
  private processPasteChunk;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ssh-tui",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "SSH-friendly interactive terminal TUI plugin for DeepSeek Harness",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -45,6 +45,7 @@
45
45
  "docs/screenshots/compare.png",
46
46
  "docs/screenshots/headless.png",
47
47
  "docs/screenshots/workspace.png",
48
+ "docs/screenshots/slow-link.gif",
48
49
  "LICENSE"
49
50
  ],
50
51
  "license": "MIT",
@@ -65,7 +66,8 @@
65
66
  "install:npm": "bash scripts/install-npm.sh",
66
67
  "uninstall:dsh": "bash scripts/uninstall.sh",
67
68
  "verify:dsh": "bash scripts/verify.sh",
68
- "screenshots": "npm run build && node scripts/capture-readme-frames.mjs"
69
+ "screenshots": "npm run build && node scripts/capture-readme-frames.mjs",
70
+ "screenshots:slow": "npm run build && node scripts/capture-slow-link.mjs"
69
71
  },
70
72
  "engines": {
71
73
  "node": ">=22.19"