dsh-ssh-tui 0.4.0 → 0.5.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/README.en.md +63 -38
- package/README.md +44 -20
- package/cordis.patch.yml +6 -0
- package/docs/screenshots/compare.png +0 -0
- package/docs/screenshots/headless.png +0 -0
- package/docs/screenshots/slow-link.gif +0 -0
- package/docs/screenshots/workspace.png +0 -0
- package/lib/approval-reviewer.js +62 -0
- package/lib/approval-reviewer.js.map +1 -0
- package/lib/auto-approval.js +124 -0
- package/lib/auto-approval.js.map +1 -0
- package/lib/display-sock.js +583 -0
- package/lib/display-sock.js.map +1 -0
- package/lib/dsh-compat.js +45 -0
- package/lib/dsh-compat.js.map +1 -0
- package/lib/i18n/en.js +108 -14
- package/lib/i18n/en.js.map +1 -1
- package/lib/i18n/index.js +2 -1
- package/lib/i18n/index.js.map +1 -1
- package/lib/i18n/zh.js +108 -14
- package/lib/i18n/zh.js.map +1 -1
- package/lib/index.js +110 -6
- package/lib/index.js.map +1 -1
- package/lib/picker.js +14 -1
- package/lib/picker.js.map +1 -1
- package/lib/provider-catalog.js +169 -0
- package/lib/provider-catalog.js.map +1 -0
- package/lib/route-memory.js +1 -1
- package/lib/route-memory.js.map +1 -1
- package/lib/session-list.js +116 -11
- package/lib/session-list.js.map +1 -1
- package/lib/session-lock.js +182 -6
- package/lib/session-lock.js.map +1 -1
- package/lib/startup.js +2 -0
- package/lib/startup.js.map +1 -1
- package/lib/subagent-model.js +1 -1
- package/lib/subagent-model.js.map +1 -1
- package/lib/tui.js +1776 -370
- package/lib/tui.js.map +1 -1
- package/lib/types/approval-reviewer.d.ts +20 -0
- package/lib/types/auto-approval.d.ts +42 -0
- package/lib/types/display-sock.d.ts +74 -0
- package/lib/types/dsh-compat.d.ts +30 -0
- package/lib/types/i18n/index.d.ts +2 -0
- package/lib/types/picker.d.ts +4 -0
- package/lib/types/provider-catalog.d.ts +35 -0
- package/lib/types/session-list.d.ts +8 -1
- package/lib/types/session-lock.d.ts +51 -0
- package/lib/types/tui.d.ts +208 -24
- package/package.json +46 -30
package/lib/types/tui.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session';
|
|
|
17
17
|
import type { SubagentRunEndInfo, SubagentRunInfo } from '@deepseek-ai/dsh-subagent';
|
|
18
18
|
import { type SubagentSelectionRef } from './subagent-model.js';
|
|
19
19
|
import { type AskUserQuestionAnswer, type AskUserQuestionRequest } from '@deepseek-ai/dsh-user-questions';
|
|
20
|
+
export type DisconnectPolicyName = 'pause' | 'continue';
|
|
20
21
|
/** Presentation configuration for the terminal channel. */
|
|
21
22
|
export interface TuiConfig {
|
|
22
23
|
/** Exact shared agent/session identity driven by this terminal. */
|
|
@@ -59,6 +60,14 @@ export interface TuiConfig {
|
|
|
59
60
|
* link. Defaults from `DSH_TUI_PAINT_MS` (160).
|
|
60
61
|
*/
|
|
61
62
|
paintIntervalMs?: number;
|
|
63
|
+
/** Called after hangup when the Host is kept (busy) so the launcher can update the lock. */
|
|
64
|
+
onHangup?: () => void | Promise<void>;
|
|
65
|
+
/** Called when a Display relay attaches after hangup. */
|
|
66
|
+
onReattach?: () => void | Promise<void>;
|
|
67
|
+
/** Host process: no local TTY; paint only through the display socket. */
|
|
68
|
+
headlessDisplay?: boolean;
|
|
69
|
+
/** Hangup policy while busy: pause cancels the turn; continue lets it finish detached. Idle hangup always exits. */
|
|
70
|
+
disconnectPolicy?: DisconnectPolicyName;
|
|
62
71
|
}
|
|
63
72
|
type SubagentLogKind = 'user' | 'assistant' | 'tool' | 'result' | 'turn' | 'approval' | 'team' | 'system';
|
|
64
73
|
/** One child-session event folded into a parent-side subagent card. */
|
|
@@ -101,6 +110,14 @@ type Row = {
|
|
|
101
110
|
exitCode?: number;
|
|
102
111
|
signal?: string;
|
|
103
112
|
expanded: boolean;
|
|
113
|
+
/** Consecutive same-path reads/edits folded into this card. */
|
|
114
|
+
repeats?: number;
|
|
115
|
+
/** Sum of output characters across folded reads. */
|
|
116
|
+
totalChars?: number;
|
|
117
|
+
/** Sum of output lines across folded reads. */
|
|
118
|
+
totalLines?: number;
|
|
119
|
+
/** Flip-card animation until this timestamp (ms since epoch). */
|
|
120
|
+
flipUntil?: number;
|
|
104
121
|
} | {
|
|
105
122
|
kind: 'subagent';
|
|
106
123
|
sessionId: string;
|
|
@@ -200,6 +217,8 @@ interface ToolDiffHunk {
|
|
|
200
217
|
/** Lifecycle handle for a mounted interactive terminal channel. */
|
|
201
218
|
export interface TuiController {
|
|
202
219
|
dispose(): Promise<void>;
|
|
220
|
+
handleHangup(): Promise<void>;
|
|
221
|
+
disconnectPolicy(): DisconnectPolicyName;
|
|
203
222
|
}
|
|
204
223
|
export type PaintLinkKind = 'local' | 'ssh';
|
|
205
224
|
/** Compact token count, matching the web stats line (517 / 12.2K / 1.2M). */
|
|
@@ -218,6 +237,21 @@ export declare function resolvePaintIntervalMs(configured?: number, env?: NodeJS
|
|
|
218
237
|
}): number;
|
|
219
238
|
/** True when this process is attached to an SSH session (jump host / proxy). */
|
|
220
239
|
export declare function detectSshSession(env?: NodeJS.ProcessEnv): boolean;
|
|
240
|
+
/** Node errno on a write/close that means the TTY is gone (SSH drop, HUP). */
|
|
241
|
+
export declare function isHangupErrno(error: unknown): boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Replace launcher SIGTERM/SIGINT/SIGHUP handlers with `handler`. SSH drop
|
|
244
|
+
* otherwise lets `dsh` dispose the whole tree before this plugin can detach.
|
|
245
|
+
*/
|
|
246
|
+
export declare function captureHangupSignals(handler: () => void): void;
|
|
247
|
+
export declare function releaseHangupSignals(handler: () => void): void;
|
|
248
|
+
/** After detach, extra HUP/TERM from sshd must not kill the leftover Host. */
|
|
249
|
+
export declare function ignoreFurtherHangupSignals(): void;
|
|
250
|
+
/**
|
|
251
|
+
* Wait until `isIdle` is true or `timeoutMs` elapses. Used after cancel so a
|
|
252
|
+
* hangup can flush a settled session log instead of tearing a live write.
|
|
253
|
+
*/
|
|
254
|
+
export declare function waitUntilIdleOrTimeout(isIdle: () => boolean, timeoutMs: number, now?: () => number, wait?: (ms: number) => Promise<void>): Promise<'idle' | 'timeout'>;
|
|
221
255
|
/** Map a CSI-6n round-trip to a paint cadence. Unknown RTT uses the SSH default. */
|
|
222
256
|
export declare function paintIntervalForRtt(rttMs: number | undefined): number;
|
|
223
257
|
export declare function paintLinkLabel(kind: PaintLinkKind, intervalMs: number, probed: boolean): string;
|
|
@@ -272,6 +306,7 @@ export interface FooterStatusInput {
|
|
|
272
306
|
subDiffers: boolean;
|
|
273
307
|
quotaCode?: string;
|
|
274
308
|
quotaPercent?: number;
|
|
309
|
+
balanceText?: string;
|
|
275
310
|
search?: {
|
|
276
311
|
index: number;
|
|
277
312
|
total: number;
|
|
@@ -321,6 +356,7 @@ export interface StatusReportInput {
|
|
|
321
356
|
activeSubagents: number;
|
|
322
357
|
plan: 'off' | 'pending' | 'on';
|
|
323
358
|
paint: string;
|
|
359
|
+
disconnect?: DisconnectPolicyName;
|
|
324
360
|
waitingQuestions: number;
|
|
325
361
|
quota?: QuotaSnapshot;
|
|
326
362
|
parentModel?: string;
|
|
@@ -361,22 +397,27 @@ export declare function fmtElapsedCompact(elapsedSecs: number): string;
|
|
|
361
397
|
*/
|
|
362
398
|
export declare function shimmerText(text: string, nowMs: number, color: boolean): string;
|
|
363
399
|
/**
|
|
364
|
-
* Codex
|
|
365
|
-
* the first
|
|
366
|
-
*
|
|
400
|
+
* Codex `extract_first_bold`: the first **closed** `**bold**` in the thinking
|
|
401
|
+
* stream, else the first markdown heading. An unclosed `**` means the title
|
|
402
|
+
* has not arrived yet, so return undefined and keep the default header —
|
|
403
|
+
* never fall back to hard-truncated reasoning, reply, or prompt text.
|
|
367
404
|
*/
|
|
368
|
-
export declare function waitSummaryFromReasoning(text: string
|
|
405
|
+
export declare function waitSummaryFromReasoning(text: string): string | undefined;
|
|
369
406
|
/** Wait-card header + optional detail. Header tracks model work when known. */
|
|
370
407
|
export declare function waitCardCopy(input: {
|
|
371
408
|
toolTitle?: string;
|
|
372
409
|
toolSummary?: string;
|
|
373
410
|
reasoning?: string;
|
|
374
|
-
reply?: string;
|
|
375
|
-
prompt?: string;
|
|
376
411
|
}): {
|
|
377
412
|
header: string;
|
|
378
413
|
detail?: string;
|
|
379
414
|
};
|
|
415
|
+
/**
|
|
416
|
+
* Codex `wrapped_details_lines`: word-wrap the wait-card detail under the
|
|
417
|
+
* ` └ ` prefix, continue wrapped rows at the prefix width, cap at 3 rows and
|
|
418
|
+
* end the last one with an ellipsis when the text does not fit.
|
|
419
|
+
*/
|
|
420
|
+
export declare function wrapWaitDetails(detail: string, width: number, maxLines?: number): string[];
|
|
380
421
|
export declare function displayWidth(text: string): number;
|
|
381
422
|
/** Pad or clip one already-sanitized line so it occupies exactly `width` cells. */
|
|
382
423
|
export declare function padToWidth(text: string, width: number): string;
|
|
@@ -460,12 +501,9 @@ export declare function remainingPercentFromUsed(usedPercent: number): number;
|
|
|
460
501
|
export declare function crossedQuotaThresholds(previousRemaining: number | undefined, remaining: number): number[];
|
|
461
502
|
export declare function quotaAlertText(snapshot: QuotaSnapshot, window: QuotaWindow): string;
|
|
462
503
|
/**
|
|
463
|
-
* How often to re-fetch quota,
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
* Hourly/5h: every 10 steps, every 4 when near a threshold.
|
|
467
|
-
* Weekly: every 50 steps, every 10 when near.
|
|
468
|
-
* Monthly: every 80 steps, every 20 when near.
|
|
504
|
+
* How often to re-fetch quota or prepaid balance, counted in model steps.
|
|
505
|
+
* Default is every 10 steps. Near a remaining-percent threshold, hourly
|
|
506
|
+
* windows refresh every 4 steps.
|
|
469
507
|
*/
|
|
470
508
|
export declare function quotaRefreshEverySteps(window: QuotaWindow | undefined): number;
|
|
471
509
|
/** @deprecated Same cadence as {@link quotaRefreshEverySteps}; the name predates step accounting. */
|
|
@@ -495,6 +533,8 @@ export declare function joinUrl(base: string, path: string): string;
|
|
|
495
533
|
export declare function parseDeepSeekBalance(payload: unknown, provider?: string): AccountBalanceSnapshot;
|
|
496
534
|
/** Best-effort parse of OpenAI-compatible credit/balance JSON. */
|
|
497
535
|
export declare function parseOpenAiCompatibleBalance(payload: unknown, provider: string, path: string): AccountBalanceSnapshot | undefined;
|
|
536
|
+
/** Compact footer chip: `余额 86.42 CNY`. Prefers remaining/available lines. */
|
|
537
|
+
export declare function formatFooterBalance(snapshot: AccountBalanceSnapshot): string | undefined;
|
|
498
538
|
export declare function formatAccountBalance(snapshot: AccountBalanceSnapshot): string;
|
|
499
539
|
/** Whether `text` could still grow into a recognized escape sequence. */
|
|
500
540
|
export declare function isEscapePrefix(text: string): boolean;
|
|
@@ -510,8 +550,42 @@ export declare function parseCursorPositionReply(text: string): {
|
|
|
510
550
|
*/
|
|
511
551
|
export declare function probeTerminalRttMs(stdin?: NodeJS.ReadStream, stdout?: NodeJS.WriteStream, timeoutMs?: number): Promise<number | undefined>;
|
|
512
552
|
export type WorkspaceView = 'detailed' | 'compact';
|
|
553
|
+
/** Sliding window of `windowSize` items that keeps `cursor` visible. */
|
|
554
|
+
export declare function pickerWindowStart(cursor: number, total: number, windowSize?: number): number;
|
|
555
|
+
export declare function parseDisconnectPolicy(raw: string): DisconnectPolicyName | undefined;
|
|
556
|
+
/** Parse `/effort high` / `/subeffort default`. Empty or unknown → undefined. */
|
|
557
|
+
export declare function parseEffortArg(raw: string): {
|
|
558
|
+
kind: 'default';
|
|
559
|
+
} | {
|
|
560
|
+
kind: 'id';
|
|
561
|
+
id: string;
|
|
562
|
+
} | undefined;
|
|
513
563
|
export declare function parseWorkspaceView(raw: string): WorkspaceView | undefined;
|
|
514
564
|
export declare function countDiffLines(hunks: readonly ToolDiffHunk[] | undefined): number;
|
|
565
|
+
/** Added / removed line counts for a diff (`oldText: null` means a new file). */
|
|
566
|
+
export declare function countDiffAddDel(hunks: readonly ToolDiffHunk[] | undefined): {
|
|
567
|
+
add: number;
|
|
568
|
+
del: number;
|
|
569
|
+
};
|
|
570
|
+
/**
|
|
571
|
+
* Git diffstat token, deletions first like `-13 +24`. Zero parts drop out
|
|
572
|
+
* (a new file shows only `+24`); empty when the diff has no counted lines.
|
|
573
|
+
*/
|
|
574
|
+
export declare function diffStatToken(add: number, del: number): string;
|
|
575
|
+
export declare function toolTargetPath(name: string, args: string, fallback?: string): string;
|
|
576
|
+
export declare function countOutputLines(text: string): number;
|
|
577
|
+
/**
|
|
578
|
+
* Consecutive same-path reads (or edits) collapse onto one card.
|
|
579
|
+
* A → B → C → A becomes four cards; A ×5 stays one card with repeats=5.
|
|
580
|
+
*/
|
|
581
|
+
export declare function canMergeToolCall(previous: Extract<Row, {
|
|
582
|
+
kind: 'tool';
|
|
583
|
+
}> | undefined, next: {
|
|
584
|
+
name: string;
|
|
585
|
+
args: string;
|
|
586
|
+
}): previous is Extract<Row, {
|
|
587
|
+
kind: 'tool';
|
|
588
|
+
}>;
|
|
515
589
|
export declare function compactToolGroups(tools: readonly Extract<Row, {
|
|
516
590
|
kind: 'tool';
|
|
517
591
|
}>[]): {
|
|
@@ -521,9 +595,18 @@ export declare function compactToolGroups(tools: readonly Extract<Row, {
|
|
|
521
595
|
calls: Extract<Row, {
|
|
522
596
|
kind: 'tool';
|
|
523
597
|
}>[];
|
|
524
|
-
editLines: number;
|
|
525
598
|
failedCalls: number;
|
|
526
599
|
};
|
|
600
|
+
/**
|
|
601
|
+
* Split compact-view tools into the bursts that belong with each assistant
|
|
602
|
+
* reply: tools after reply N sit with that reply, until the next reply.
|
|
603
|
+
*/
|
|
604
|
+
export declare function compactToolBursts(rows: readonly Row[]): Array<{
|
|
605
|
+
after: Extract<Row, {
|
|
606
|
+
kind: 'assistant';
|
|
607
|
+
}> | undefined;
|
|
608
|
+
groups: ReturnType<typeof compactToolGroups>;
|
|
609
|
+
}>;
|
|
527
610
|
/** True while a plan still belongs in the dock (latest incomplete work). */
|
|
528
611
|
export declare function planIsLive(plan: {
|
|
529
612
|
active: boolean;
|
|
@@ -607,7 +690,7 @@ interface DiffDisplayLine {
|
|
|
607
690
|
/** Running / ok / error → ANSI color for the status dot and status word only. */
|
|
608
691
|
export declare function toolStateColor(status: 'running' | 'ok' | 'error' | undefined): '33' | '32' | '31';
|
|
609
692
|
export declare function toolStateLabel(status: 'running' | 'ok' | 'error' | undefined): string;
|
|
610
|
-
/** Header + SGR spans: default title, dim operand, colored
|
|
693
|
+
/** Header + SGR spans: default title, dim operand, colored ●. `[ok]` is omitted — the green dot is enough. */
|
|
611
694
|
export declare function buildToolHeader(input: {
|
|
612
695
|
focused: boolean;
|
|
613
696
|
expanded: boolean;
|
|
@@ -618,6 +701,11 @@ export declare function buildToolHeader(input: {
|
|
|
618
701
|
signal?: string;
|
|
619
702
|
exitCode?: number;
|
|
620
703
|
spinner?: string;
|
|
704
|
+
flipping?: boolean;
|
|
705
|
+
diffStat?: {
|
|
706
|
+
add: number;
|
|
707
|
+
del: number;
|
|
708
|
+
};
|
|
621
709
|
}): {
|
|
622
710
|
plain: string;
|
|
623
711
|
segments: TextSegment[];
|
|
@@ -663,12 +751,25 @@ export declare class SshTui {
|
|
|
663
751
|
private readonly agent;
|
|
664
752
|
private readonly rows;
|
|
665
753
|
private streaming;
|
|
754
|
+
private autoApprovalMode;
|
|
755
|
+
private autoAllowedCount;
|
|
756
|
+
private autoDeniedCount;
|
|
757
|
+
private aiReviewCount;
|
|
758
|
+
/** Host knobs folded from the session log: auto mode needs approval=ask to see requests. */
|
|
759
|
+
private hostSandboxMode;
|
|
760
|
+
private hostApprovalPolicy;
|
|
761
|
+
private approvalMismatchWarned;
|
|
762
|
+
/** Web-aligned provider presets from the host's pi-ai catalog (undefined until loaded / when unreachable). */
|
|
763
|
+
private catalogPresets;
|
|
764
|
+
private catalogLoad;
|
|
666
765
|
private input;
|
|
667
766
|
private cursor;
|
|
668
767
|
private inputFolded;
|
|
669
768
|
private inPaste;
|
|
670
769
|
private history;
|
|
671
770
|
private historyIndex;
|
|
771
|
+
/** Live input parked while browsing history with ↑. Restored by ↓ past the newest item. */
|
|
772
|
+
private historyDraft;
|
|
672
773
|
private status;
|
|
673
774
|
private dialog;
|
|
674
775
|
private readonly dialogQueue;
|
|
@@ -676,6 +777,17 @@ export declare class SshTui {
|
|
|
676
777
|
private dirty;
|
|
677
778
|
private disposed;
|
|
678
779
|
private exiting;
|
|
780
|
+
private hangingUp;
|
|
781
|
+
private readonly onDirectResize;
|
|
782
|
+
private readonly headlessDisplay;
|
|
783
|
+
private disconnectPolicy;
|
|
784
|
+
private detachedIdleTimer;
|
|
785
|
+
private displayDetached;
|
|
786
|
+
private displayHost;
|
|
787
|
+
private relayColumns;
|
|
788
|
+
private relayRows;
|
|
789
|
+
private readonly onHangup;
|
|
790
|
+
private readonly onReattach;
|
|
679
791
|
private renderTimer;
|
|
680
792
|
private readonly decoder;
|
|
681
793
|
private readonly color;
|
|
@@ -706,6 +818,7 @@ export declare class SshTui {
|
|
|
706
818
|
private stalledWarningShown;
|
|
707
819
|
private lastPaintAt;
|
|
708
820
|
private commandAbort;
|
|
821
|
+
private readonly seenCommandDoneIds;
|
|
709
822
|
private activeSubagents;
|
|
710
823
|
private subagentSessions;
|
|
711
824
|
private openToolCalls;
|
|
@@ -721,7 +834,6 @@ export declare class SshTui {
|
|
|
721
834
|
private escapeTimer;
|
|
722
835
|
private thinkingStartedAt;
|
|
723
836
|
private waitStartedAt;
|
|
724
|
-
private waitPrompt;
|
|
725
837
|
private completionSignaled;
|
|
726
838
|
private replaying;
|
|
727
839
|
private completedAt;
|
|
@@ -743,6 +855,7 @@ export declare class SshTui {
|
|
|
743
855
|
private sessionTitle;
|
|
744
856
|
private llmRetry;
|
|
745
857
|
private quotaSnapshot;
|
|
858
|
+
private balanceSnapshot;
|
|
746
859
|
private quotaAlerted;
|
|
747
860
|
private quotaStepsSinceRefresh;
|
|
748
861
|
private quotaRefreshInFlight;
|
|
@@ -754,14 +867,22 @@ export declare class SshTui {
|
|
|
754
867
|
constructor(ctx: Context, agent: Agent, config: TuiConfig);
|
|
755
868
|
/** Enter raw mode, switch to the alternate screen, and start listening. */
|
|
756
869
|
start(): void;
|
|
870
|
+
private bindAgentEvents;
|
|
871
|
+
private bootBackgroundTasks;
|
|
757
872
|
private notifyPluginUpdate;
|
|
758
873
|
private readSkippedUpdate;
|
|
759
874
|
private persistSkippedUpdate;
|
|
760
875
|
private mergeUiSettings;
|
|
761
876
|
private readWorkspaceView;
|
|
877
|
+
private readDisconnectPolicy;
|
|
878
|
+
private readAutoApprovalMode;
|
|
879
|
+
private detachedIdleMs;
|
|
880
|
+
private clearDetachedIdleTimer;
|
|
881
|
+
private armDetachedIdleTimer;
|
|
762
882
|
private isCompactView;
|
|
763
883
|
/** Test helper: switch the workspace view without going through /view. */
|
|
764
884
|
setWorkspaceView(view: WorkspaceView): void;
|
|
885
|
+
private paintCompactBurst;
|
|
765
886
|
private paintCompactSummary;
|
|
766
887
|
private startRenderTimer;
|
|
767
888
|
private calibratePaintInterval;
|
|
@@ -772,22 +893,59 @@ export declare class SshTui {
|
|
|
772
893
|
/** Run the provider/API-key onboarding wizard. Resolves true when saved. */
|
|
773
894
|
private runOnboarding;
|
|
774
895
|
private cancelOnboarding;
|
|
775
|
-
/**
|
|
896
|
+
/**
|
|
897
|
+
* Drop the TTY without disposing the agent. Safe to call when the fd is
|
|
898
|
+
* already dead: DECSET restore is best-effort and never throws.
|
|
899
|
+
*/
|
|
900
|
+
detachDisplay(): void;
|
|
901
|
+
/** Restore the terminal and drop event wiring. Does not flush or exit. */
|
|
776
902
|
dispose(): Promise<void>;
|
|
777
903
|
/** Human-facing exit with goodbye and flush; called from key handling. */
|
|
778
904
|
requestExit(code: number): Promise<void>;
|
|
905
|
+
/**
|
|
906
|
+
* True while the session is doing work the user would lose by killing the
|
|
907
|
+
* Host: a running turn (thinking / reply / tools), live subagents, in-flight
|
|
908
|
+
* compaction, or an LLM retry. Idle (including a waiting approval dialog
|
|
909
|
+
* after the turn has already settled) is not busy — hangup then exits
|
|
910
|
+
* instead of leaving a leftover process.
|
|
911
|
+
*/
|
|
912
|
+
private isBusyForHangupKeepalive;
|
|
913
|
+
/**
|
|
914
|
+
* SSH / TTY hangup: drop the local display, flush, and either keep the Host
|
|
915
|
+
* (busy: thinking / reply / tools / subagents) or exit (idle).
|
|
916
|
+
* When keeping the Host, `pause` cancels the turn; `continue` lets it finish.
|
|
917
|
+
* Ctrl+C is not a hangup.
|
|
918
|
+
*/
|
|
919
|
+
handleHangup(): Promise<void>;
|
|
920
|
+
private readonly handleHangupSignal;
|
|
921
|
+
private readonly handleHangupStream;
|
|
922
|
+
private readonly handleIoError;
|
|
923
|
+
private writeGoodbye;
|
|
924
|
+
private flushSession;
|
|
925
|
+
private exitProcess;
|
|
779
926
|
/** Capture one painted frame. Used by README screenshot fixtures. */
|
|
780
927
|
captureFrame(columns?: number, rows?: number): string[];
|
|
928
|
+
private screenColumns;
|
|
929
|
+
private screenRows;
|
|
781
930
|
private write;
|
|
931
|
+
private ensureDisplayHost;
|
|
932
|
+
/** Re-open DECSET and start painting to an attached Display relay. */
|
|
933
|
+
attachRelayDisplay(): void;
|
|
934
|
+
currentDisconnectPolicy(): DisconnectPolicyName;
|
|
935
|
+
applyProbedRtt(rttMs: number | undefined): void;
|
|
782
936
|
private markDirty;
|
|
937
|
+
private toolCardSummary;
|
|
938
|
+
private mergeIntoToolCard;
|
|
783
939
|
/** Append one transcript row, bounding memory on long sessions. */
|
|
784
940
|
private pushRow;
|
|
785
941
|
/** The transcript rows that support per-row expand/collapse. */
|
|
786
942
|
private collapsibleRows;
|
|
787
943
|
private spinnerFrame;
|
|
788
944
|
/**
|
|
789
|
-
* Codex wait card: shown while the turn is running.
|
|
790
|
-
*
|
|
945
|
+
* Codex wait card: shown while the turn is running. The live thinking
|
|
946
|
+
* stream feeds the shimmer header; a live tool becomes the detail rows.
|
|
947
|
+
* While the reply itself is streaming, the transcript paints those tokens
|
|
948
|
+
* and the card yields (Codex hides the status row once output commits).
|
|
791
949
|
*/
|
|
792
950
|
private waitCardVisible;
|
|
793
951
|
private beginWait;
|
|
@@ -865,6 +1023,7 @@ export declare class SshTui {
|
|
|
865
1023
|
private findCompactionRow;
|
|
866
1024
|
private handleCompactionEvent;
|
|
867
1025
|
private handleCommandRun;
|
|
1026
|
+
private formatCommandText;
|
|
868
1027
|
private handleCommandDone;
|
|
869
1028
|
private handleGoalChange;
|
|
870
1029
|
private pushPromptInjection;
|
|
@@ -873,6 +1032,21 @@ export declare class SshTui {
|
|
|
873
1032
|
readonly handleSubagentSessionEvent: (sessionId: SessionId, event: SessionEvent) => void;
|
|
874
1033
|
readonly handleSubagentStart: (info: SubagentRunInfo) => void;
|
|
875
1034
|
readonly handleSubagentEnd: (info: SubagentRunEndInfo) => void;
|
|
1035
|
+
private hasLiveDisplay;
|
|
1036
|
+
private waitForLiveDisplay;
|
|
1037
|
+
/**
|
|
1038
|
+
* Auto mode rides the approval waterfall: when the host approval policy is
|
|
1039
|
+
* `never` no request is ever produced, so the classifier silently does
|
|
1040
|
+
* nothing. Say so instead of letting the user believe a guard is active.
|
|
1041
|
+
*/
|
|
1042
|
+
private warnApprovalMismatch;
|
|
1043
|
+
/**
|
|
1044
|
+
* AI review for rule-table `ask` outcomes: one shot at the subagent
|
|
1045
|
+
* model route with a compact, injection-fenced context. Returns
|
|
1046
|
+
* 'allow' | 'deny', or undefined when the reviewer is unavailable or its
|
|
1047
|
+
* output was unusable (caller falls back to prompt/reject).
|
|
1048
|
+
*/
|
|
1049
|
+
private reviewUnknownWithModel;
|
|
876
1050
|
private readonly handleApproval;
|
|
877
1051
|
readonly handleUserQuestions: (request: AskUserQuestionRequest) => Promise<AskUserQuestionAnswer>;
|
|
878
1052
|
/** Queue one dialog behind an already-open one instead of overwriting it. */
|
|
@@ -900,13 +1074,9 @@ export declare class SshTui {
|
|
|
900
1074
|
private discoverEndpointModels;
|
|
901
1075
|
/** Add one endpoint-listed model to the stored provider profile when needed. */
|
|
902
1076
|
private ensureProviderModelConfigured;
|
|
903
|
-
/** How many endpoint-listed models fit on one picker page alongside navigation. */
|
|
904
|
-
private readonly MODEL_PAGE_SIZE;
|
|
905
|
-
private readonly MODEL_PAGE_PREV;
|
|
906
|
-
private readonly MODEL_PAGE_NEXT;
|
|
907
1077
|
/**
|
|
908
|
-
* One pick across a possibly long model list
|
|
909
|
-
*
|
|
1078
|
+
* One pick across a possibly long model list. The question dialog keeps a
|
|
1079
|
+
* 12-row sliding window so dozens of models stay selectable with ↑/↓.
|
|
910
1080
|
*/
|
|
911
1081
|
private pickModelOption;
|
|
912
1082
|
/** Live adapter routes the TUI can switch to, plus the current selection. */
|
|
@@ -945,12 +1115,17 @@ export declare class SshTui {
|
|
|
945
1115
|
private subagentModelOptions;
|
|
946
1116
|
/** /submodel: pick (or set) the model subagent children use. */
|
|
947
1117
|
private runSubmodelCommand;
|
|
1118
|
+
/** /effort: pick or set the reasoning effort for the current model. */
|
|
1119
|
+
private runEffortCommand;
|
|
1120
|
+
private setReasoningEffort;
|
|
948
1121
|
/** /subeffort: pick the reasoning effort subagent children use. */
|
|
949
1122
|
private runSubeffortCommand;
|
|
950
1123
|
/** /language or /lang: persist zh/en and repaint chrome immediately. */
|
|
951
1124
|
private runLanguageCommand;
|
|
952
1125
|
/** /view: detailed (see the work) vs compact (Codex-like summary). */
|
|
953
1126
|
private runViewCommand;
|
|
1127
|
+
/** /disconnect: pause (default) or continue the turn after SSH drop. */
|
|
1128
|
+
private runDisconnectCommand;
|
|
954
1129
|
/** /mode: pick an agent preset (standard / minimal / ptc / cordis / routing-suite / ...). */
|
|
955
1130
|
private runModeCommand;
|
|
956
1131
|
/** /resume: switch to a past session, or open a picker when no id is given. */
|
|
@@ -978,7 +1153,14 @@ export declare class SshTui {
|
|
|
978
1153
|
private handlePasteText;
|
|
979
1154
|
private handlePlainText;
|
|
980
1155
|
private handleChar;
|
|
1156
|
+
private moveQuestionCursor;
|
|
981
1157
|
private handleDialogChar;
|
|
1158
|
+
/**
|
|
1159
|
+
* The wizard's first-step list: the pinned templates plus the web-catalog
|
|
1160
|
+
* presets (deduped), filtered by the search box text.
|
|
1161
|
+
*/
|
|
1162
|
+
private mergedProviderEntries;
|
|
1163
|
+
private moveProviderCursor;
|
|
982
1164
|
private handleOnboardingChar;
|
|
983
1165
|
private advanceOnboarding;
|
|
984
1166
|
/** Fetch the endpoint's model list into the onboarding wizard's models step. */
|
|
@@ -1008,6 +1190,8 @@ export declare class SshTui {
|
|
|
1008
1190
|
private moveCursor;
|
|
1009
1191
|
private historyBack;
|
|
1010
1192
|
private historyForward;
|
|
1193
|
+
/** Typing while browsing history detaches from the saved item. */
|
|
1194
|
+
private leaveHistoryBrowse;
|
|
1011
1195
|
}
|
|
1012
1196
|
/**
|
|
1013
1197
|
* Mount the terminal channel once the configured agent exists.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ssh-tui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "SSH-friendly interactive terminal TUI plugin for DeepSeek Harness",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -80,20 +80,20 @@
|
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
83
|
-
"@deepseek-ai/dsh-agent": ">=0.1.1-rc.2",
|
|
84
|
-
"@deepseek-ai/dsh-agent-default-model": ">=0.1.1-rc.2",
|
|
85
|
-
"@deepseek-ai/dsh-agent-loop": ">=0.1.1-rc.2",
|
|
86
|
-
"@deepseek-ai/dsh-agent-presets": ">=0.1.1-rc.2",
|
|
87
|
-
"@deepseek-ai/dsh-cmdline": ">=0.1.1-rc.2",
|
|
88
|
-
"@deepseek-ai/dsh-commands": ">=0.1.1-rc.2",
|
|
89
|
-
"@deepseek-ai/dsh-credentials": ">=0.1.1-rc.2",
|
|
90
|
-
"@deepseek-ai/dsh-llm": ">=0.1.1-rc.2",
|
|
91
|
-
"@deepseek-ai/dsh-session": ">=0.1.1-rc.2",
|
|
92
|
-
"@deepseek-ai/dsh-settings": ">=0.1.1-rc.2",
|
|
93
|
-
"@deepseek-ai/dsh-subagent": ">=0.1.1-rc.2",
|
|
94
|
-
"@deepseek-ai/dsh-user-approval": ">=0.1.1-rc.2",
|
|
95
|
-
"@deepseek-ai/dsh-user-questions": ">=0.1.1-rc.2",
|
|
96
|
-
"@deepseek-ai/dsh-session-persistence": ">=0.1.1-rc.2"
|
|
83
|
+
"@deepseek-ai/dsh-agent": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
84
|
+
"@deepseek-ai/dsh-agent-default-model": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
85
|
+
"@deepseek-ai/dsh-agent-loop": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
86
|
+
"@deepseek-ai/dsh-agent-presets": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
87
|
+
"@deepseek-ai/dsh-cmdline": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
88
|
+
"@deepseek-ai/dsh-commands": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
89
|
+
"@deepseek-ai/dsh-credentials": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
90
|
+
"@deepseek-ai/dsh-llm": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
91
|
+
"@deepseek-ai/dsh-session": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
92
|
+
"@deepseek-ai/dsh-settings": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
93
|
+
"@deepseek-ai/dsh-subagent": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
94
|
+
"@deepseek-ai/dsh-user-approval": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
95
|
+
"@deepseek-ai/dsh-user-questions": ">=0.1.1-rc.2 || >=0.1.2-a",
|
|
96
|
+
"@deepseek-ai/dsh-session-persistence": ">=0.1.1-rc.2 || >=0.1.2-a"
|
|
97
97
|
},
|
|
98
98
|
"peerDependenciesMeta": {
|
|
99
99
|
"@deepseek-ai/dsh-user-approval": {
|
|
@@ -111,21 +111,37 @@
|
|
|
111
111
|
},
|
|
112
112
|
"devDependencies": {
|
|
113
113
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
114
|
-
"@deepseek-ai/
|
|
115
|
-
"@deepseek-ai/dsh-agent
|
|
116
|
-
"@deepseek-ai/dsh-agent-
|
|
117
|
-
"@deepseek-ai/dsh-agent-
|
|
118
|
-
"@deepseek-ai/dsh-
|
|
119
|
-
"@deepseek-ai/dsh-
|
|
120
|
-
"@deepseek-ai/dsh-
|
|
121
|
-
"@deepseek-ai/dsh-
|
|
122
|
-
"@deepseek-ai/dsh-
|
|
123
|
-
"@deepseek-ai/dsh-
|
|
124
|
-
"@deepseek-ai/dsh-
|
|
125
|
-
"@deepseek-ai/dsh-
|
|
126
|
-
"@deepseek-ai/dsh-
|
|
114
|
+
"@deepseek-ai/cordis-plugin-loader": "^1.0.3",
|
|
115
|
+
"@deepseek-ai/dsh-agent": "0.1.2-rc.1",
|
|
116
|
+
"@deepseek-ai/dsh-agent-default-model": "0.1.2-rc.1",
|
|
117
|
+
"@deepseek-ai/dsh-agent-loop": "0.1.2-rc.1",
|
|
118
|
+
"@deepseek-ai/dsh-agent-presets": "0.1.2-rc.1",
|
|
119
|
+
"@deepseek-ai/dsh-atomic-write": "0.1.2-rc.1",
|
|
120
|
+
"@deepseek-ai/dsh-attachment": "0.1.2-rc.1",
|
|
121
|
+
"@deepseek-ai/dsh-brand": "0.1.2-rc.1",
|
|
122
|
+
"@deepseek-ai/dsh-cmdline": "0.1.2-rc.1",
|
|
123
|
+
"@deepseek-ai/dsh-commands": "0.1.2-rc.1",
|
|
124
|
+
"@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
|
|
125
|
+
"@deepseek-ai/dsh-home-paths": "0.1.2-rc.1",
|
|
126
|
+
"@deepseek-ai/dsh-invariants": "0.1.2-rc.1",
|
|
127
|
+
"@deepseek-ai/dsh-jobs": "0.1.2-rc.1",
|
|
128
|
+
"@deepseek-ai/dsh-llm": "0.1.2-rc.1",
|
|
129
|
+
"@deepseek-ai/dsh-sandbox": "0.1.2-rc.1",
|
|
130
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.1.2-rc.1",
|
|
131
|
+
"@deepseek-ai/dsh-scope": "0.1.2-rc.1",
|
|
132
|
+
"@deepseek-ai/dsh-session": "0.1.2-rc.1",
|
|
133
|
+
"@deepseek-ai/dsh-session-persistence": "0.1.2-rc.1",
|
|
134
|
+
"@deepseek-ai/dsh-session-projection": "0.1.2-rc.1",
|
|
135
|
+
"@deepseek-ai/dsh-session-projection-cache": "0.1.2-rc.1",
|
|
136
|
+
"@deepseek-ai/dsh-settings": "0.1.2-rc.1",
|
|
137
|
+
"@deepseek-ai/dsh-subagent": "0.1.2-rc.1",
|
|
138
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1",
|
|
139
|
+
"@deepseek-ai/dsh-timeout": "0.1.2-rc.1",
|
|
140
|
+
"@deepseek-ai/dsh-tools": "0.1.2-rc.1",
|
|
141
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.2-rc.1",
|
|
142
|
+
"@deepseek-ai/dsh-user-approval": "0.1.2-rc.1",
|
|
143
|
+
"@deepseek-ai/dsh-user-questions": "0.1.2-rc.1",
|
|
127
144
|
"@types/node": "^24.0.0",
|
|
128
|
-
"typescript": "^5.9.0"
|
|
129
|
-
"@deepseek-ai/dsh-session-persistence": ">=0.1.1-rc.2"
|
|
145
|
+
"typescript": "^5.9.0"
|
|
130
146
|
}
|
|
131
147
|
}
|