dsh-ssh-tui 0.3.1 → 0.3.3

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 120).
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';
@@ -53,7 +54,7 @@ export interface TuiConfig {
53
54
  /**
54
55
  * Minimum milliseconds between paints while a turn is streaming.
55
56
  * Jump-host / proxied SSH can raise this so token ticks do not flood the
56
- * link. Defaults from `DSH_TUI_PAINT_MS` (120).
57
+ * link. Defaults from `DSH_TUI_PAINT_MS` (160).
57
58
  */
58
59
  paintIntervalMs?: number;
59
60
  }
@@ -121,6 +122,11 @@ type Row = {
121
122
  expanded: boolean;
122
123
  /** When true the plan stays in the scrolling transcript, not the dock. */
123
124
  archived?: boolean;
125
+ /**
126
+ * Display-only: the last turn ended while todos were still open.
127
+ * Does not rewrite the session log.
128
+ */
129
+ turnLeftOpen?: boolean;
124
130
  } | {
125
131
  kind: 'question';
126
132
  questionId: string;
@@ -155,11 +161,21 @@ interface ToolDiffHunk {
155
161
  export interface TuiController {
156
162
  dispose(): Promise<void>;
157
163
  }
164
+ export type PaintLinkKind = 'local' | 'ssh';
158
165
  /**
159
- * Paint cadence for jump-host / proxied SSH. Token ticks coalesce into one
160
- * frame; the default stays snappy, slower links raise `DSH_TUI_PAINT_MS`.
166
+ * Explicit env/config always wins. Otherwise local TTYs stay snappy and SSH
167
+ * sessions pick a tier from a measured round-trip (CSI 6n), falling back to
168
+ * 160 ms when the probe is missing.
161
169
  */
162
- export declare function resolvePaintIntervalMs(configured?: number, env?: NodeJS.ProcessEnv): number;
170
+ export declare function resolvePaintIntervalMs(configured?: number, env?: NodeJS.ProcessEnv, options?: {
171
+ ssh?: boolean;
172
+ rttMs?: number;
173
+ }): number;
174
+ /** True when this process is attached to an SSH session (jump host / proxy). */
175
+ export declare function detectSshSession(env?: NodeJS.ProcessEnv): boolean;
176
+ /** Map a CSI-6n round-trip to a paint cadence. Unknown RTT uses the SSH default. */
177
+ export declare function paintIntervalForRtt(rttMs: number | undefined): number;
178
+ export declare function paintLinkLabel(kind: PaintLinkKind, intervalMs: number, probed: boolean): string;
163
179
  /** One incremental paint as a single stdout write (one SSH packet when corked). */
164
180
  export declare function composePaintOutput(options: {
165
181
  width: number;
@@ -249,6 +265,17 @@ export declare function openCodeSourceFor(provider: string, llmPiAiSection: unkn
249
265
  export declare function formatOpenCodeGoUsage(payload: unknown, source: OpenCodeSource): string;
250
266
  /** Whether `text` could still grow into a recognized escape sequence. */
251
267
  export declare function isEscapePrefix(text: string): boolean;
268
+ /** Parse a Device Status Report cursor reply (`CSI row;col R`). */
269
+ export declare function parseCursorPositionReply(text: string): {
270
+ row: number;
271
+ column: number;
272
+ } | undefined;
273
+ /**
274
+ * Round-trip to the attached terminal via CSI 6n. Returns undefined when the
275
+ * reply never arrives (dumb pipe, blocked DSR). Does not interpret the
276
+ * coordinates — only the elapsed milliseconds matter.
277
+ */
278
+ export declare function probeTerminalRttMs(stdin?: NodeJS.ReadStream, stdout?: NodeJS.WriteStream, timeoutMs?: number): Promise<number | undefined>;
252
279
  /** True while a plan still belongs in the dock (latest incomplete work). */
253
280
  export declare function planIsLive(plan: {
254
281
  active: boolean;
@@ -257,6 +284,19 @@ export declare function planIsLive(plan: {
257
284
  planMarkdown?: string;
258
285
  archived?: boolean;
259
286
  }): boolean;
287
+ /** Open todos left behind when a turn ends without a completing todo_write. */
288
+ export declare function planTurnLeftOpen(plan: {
289
+ todos: readonly PlanTodoItem[];
290
+ }): boolean;
291
+ /** Mark leftover in-progress/pending todos as display-stale after turn/end. */
292
+ export declare function applyTurnEndToPlan<T extends {
293
+ todos: PlanTodoItem[];
294
+ turnLeftOpen?: boolean;
295
+ }>(plan: T): T;
296
+ /** Follow-up that asks the model to close leftover todos. One per open list. */
297
+ export declare function planCloseNudgeText(plan: {
298
+ todos: readonly PlanTodoItem[];
299
+ }): string;
260
300
  export type CardCategory = 'thinking' | 'plan' | 'subagent' | 'reply' | 'tool' | 'question' | 'goal';
261
301
  /** Category for jump / search. Assistant replies are not collapsible cards. */
262
302
  export declare function cardCategoryOf(row: {
@@ -275,6 +315,7 @@ export declare function planDockNote(plan: {
275
315
  pending: boolean;
276
316
  todos: readonly PlanTodoItem[];
277
317
  planMarkdown?: string;
318
+ turnLeftOpen?: boolean;
278
319
  }): string;
279
320
  /** Compact per-status counts matching the web plan strip. */
280
321
  export declare function todoProgressLabel(todos: readonly PlanTodoItem[]): string;
@@ -404,13 +445,19 @@ export declare class SshTui {
404
445
  private lastChromeKey;
405
446
  private lastPaintWidth;
406
447
  private lastPaintHeight;
407
- private readonly paintIntervalMs;
448
+ private paintIntervalMs;
449
+ private paintLink;
450
+ private paintProbed;
408
451
  private searchHits;
409
452
  private searchIndex;
410
453
  private searchQuery;
454
+ private planNudgePending;
455
+ private pendingReveal;
411
456
  constructor(ctx: Context, agent: Agent, config: TuiConfig);
412
457
  /** Enter raw mode, switch to the alternate screen, and start listening. */
413
458
  start(): void;
459
+ private startRenderTimer;
460
+ private calibratePaintInterval;
414
461
  /** Replay the durable session log so a resumed session renders its history. */
415
462
  replayHistory(): void;
416
463
  /** Show the first-launch provider/API-key onboarding when nothing is configured. */
@@ -438,6 +485,8 @@ export declare class SshTui {
438
485
  private upsertPlanRow;
439
486
  /** Whether the live plan strip should occupy the workspace footer. */
440
487
  private shouldDockPlan;
488
+ /** One follow-up per leftover list; replay and cancelled turns stay quiet. */
489
+ private queuePlanCloseNudge;
441
490
  /** Compact web-style plan strip pinned above the input, not in the transcript. */
442
491
  private paintPlanDock;
443
492
  private paintCollapsibleHeader;
@@ -447,6 +496,8 @@ export declare class SshTui {
447
496
  private toggleCollapsible;
448
497
  /** Expand all collapsible blocks, or collapse them again when all are open. */
449
498
  private toggleAllCollapsible;
499
+ private highlightSearchLine;
500
+ private revealRow;
450
501
  private focusCard;
451
502
  /** Jump to the newest card in a category (thinking / plan / subagent / reply). */
452
503
  private jumpToCategory;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ssh-tui",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "SSH-friendly interactive terminal TUI plugin for DeepSeek Harness",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -42,7 +42,10 @@
42
42
  "cordis.patch.yml",
43
43
  "README.md",
44
44
  "README.en.md",
45
- "docs/screenshots",
45
+ "docs/screenshots/compare.png",
46
+ "docs/screenshots/headless.png",
47
+ "docs/screenshots/workspace.png",
48
+ "docs/screenshots/slow-link.gif",
46
49
  "LICENSE"
47
50
  ],
48
51
  "license": "MIT",
@@ -63,7 +66,8 @@
63
66
  "install:npm": "bash scripts/install-npm.sh",
64
67
  "uninstall:dsh": "bash scripts/uninstall.sh",
65
68
  "verify:dsh": "bash scripts/verify.sh",
66
- "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"
67
71
  },
68
72
  "engines": {
69
73
  "node": ">=22.19"