dsh-ssh-tui 0.5.1 → 0.5.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.
@@ -1,3 +1,15 @@
1
+ /**
2
+ * AI reviewer for /approval auto mode: commands whose shape the rule table
3
+ * cannot classify are judged by the subagent-configured model with compact
4
+ * context (latest user message, recent model output, the pending tool call)
5
+ * instead of paging the human.
6
+ *
7
+ * Injection hardening: everything inside the marked data regions is review
8
+ * MATERIAL, never instructions — the system prompt says so, and material that
9
+ * tries to instruct is treated as an injection attempt and rejected. The
10
+ * caller also enforces its own floor: `high` risk or `authorization: no`
11
+ * is never approved, whatever the model claims.
12
+ */
1
13
  export interface ReviewInput {
2
14
  userText: string;
3
15
  segments: string[];
@@ -13,8 +25,4 @@ export interface ReviewVerdict {
13
25
  export declare const REVIEW_SYSTEM_PROMPT: string;
14
26
  /** Assemble the compact, fence-marked user message for the reviewer. */
15
27
  export declare function buildReviewUserMessage(input: ReviewInput): string;
16
- /**
17
- * Parse the reviewer's one-line JSON verdict. Anything unreadable, missing
18
- * fields, or with invalid enum values returns undefined (fail-safe).
19
- */
20
28
  export declare function parseReviewOutput(text: string): ReviewVerdict | undefined;
@@ -12,8 +12,9 @@
12
12
  export type AutoApprovalMode = 'off' | 'auto';
13
13
  export type ApprovalDecision = 'allow' | 'deny' | 'ask';
14
14
  /**
15
- * Whole-command danger patterns, checked before anything else. A match keeps
16
- * the interactive prompt regardless of what else the command contains.
15
+ * Whole-command danger patterns, checked before anything else. A match
16
+ * auto-rejects. This is a UX heuristic, not a security boundary: obfuscated
17
+ * or interpreter-wrapped damage still has to be contained by the sandbox.
17
18
  */
18
19
  export declare const DANGER_PATTERNS: RegExp[];
19
20
  /**
@@ -38,5 +39,27 @@ export declare function classifyApproval(toolName: string, command: string | und
38
39
  * Returns undefined for non-shell tools or unparseable args.
39
40
  */
40
41
  export declare function commandFromArgs(toolName: string, args: string): string | undefined;
42
+ /**
43
+ * Pull a shell command out of an approval-request `reason` when the
44
+ * classifier never saw the tool-call JSON (sandbox escalation, missing
45
+ * callId, or a card whose args were not recorded).
46
+ */
47
+ export declare function commandFromApprovalReason(reason: string | undefined): string | undefined;
48
+ /**
49
+ * Resolve the command the classifier should see for one approval request.
50
+ * Prefer the streamed bash/pwsh card, then a command already decoded on the
51
+ * card, then the request's reason text.
52
+ */
53
+ export declare function commandForApprovalRequest(input: {
54
+ toolName: string;
55
+ reason?: string;
56
+ row?: {
57
+ name: string;
58
+ args: string;
59
+ command?: string;
60
+ };
61
+ }): string | undefined;
41
62
  /** Parse the /approval argument into a mode. */
42
63
  export declare function parseAutoApprovalMode(raw: string): AutoApprovalMode | undefined;
64
+ /** True when `/approval <arg>` should print the current mode and counters. */
65
+ export declare function isApprovalStatusArg(raw: string): boolean;
@@ -15,11 +15,13 @@ export declare const UI_LOCALE_SCHEMA: z<Schemastery.ObjectS<{
15
15
  skipUpdate: z<string, string>;
16
16
  view: z<string, string>;
17
17
  disconnect: z<string, string>;
18
+ autoApproval: z<string, string>;
18
19
  }>, Schemastery.ObjectT<{
19
20
  language: z<string, string>;
20
21
  skipUpdate: z<string, string>;
21
22
  view: z<string, string>;
22
23
  disconnect: z<string, string>;
24
+ autoApproval: z<string, string>;
23
25
  }>>;
24
26
  export declare function localeFromTag(tag: string): Locale | undefined;
25
27
  /** Pick zh/en from env, optionally after a saved settings value. */
@@ -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
+ import type { ApprovalOutcome, ApprovalRequest } from '@deepseek-ai/dsh-user-approval';
20
21
  export type DisconnectPolicyName = 'pause' | 'continue';
21
22
  /** Presentation configuration for the terminal channel. */
22
23
  export interface TuiConfig {
@@ -112,6 +113,8 @@ type Row = {
112
113
  expanded: boolean;
113
114
  /** Consecutive same-path reads/edits folded into this card. */
114
115
  repeats?: number;
116
+ /** Call ids folded into this card; results still match after merge. */
117
+ mergedCallIds?: string[];
115
118
  /** Sum of output characters across folded reads. */
116
119
  totalChars?: number;
117
120
  /** Sum of output lines across folded reads. */
@@ -223,6 +226,50 @@ export interface TuiController {
223
226
  export type PaintLinkKind = 'local' | 'ssh';
224
227
  /** Compact token count, matching the web stats line (517 / 12.2K / 1.2M). */
225
228
  export declare function formatTokens(n: number): string;
229
+ /**
230
+ * Prompt occupancy of the next request, from DSH `contextPressure`.
231
+ * Provider-agnostic: uses the routed model's advertised window, not a
232
+ * hardcoded xAI size. Compaction-basic still owns in-turn pressure at 80%.
233
+ */
234
+ export declare const CONTEXT_PRESSURE_WARN_RATIO = 0.8;
235
+ export declare const CONTEXT_PRESSURE_DANGER_RATIO = 0.95;
236
+ /** Idle auto-compact starts here so recovery finishes before the 80% in-turn trigger. */
237
+ export declare const CONTEXT_IDLE_COMPACT_RATIO = 0.72;
238
+ export interface ContextPressureSample {
239
+ usedTokens: number;
240
+ contextWindow: number;
241
+ }
242
+ export interface ContextPressureView {
243
+ usedTokens: number;
244
+ contextWindow: number;
245
+ percent: number;
246
+ level: 'ok' | 'warn' | 'danger';
247
+ }
248
+ /** Prompt-side occupancy of one usage sample: uncached input plus cache traffic. */
249
+ export declare function promptPressureTokens(usage: {
250
+ inputTokens: number;
251
+ cacheReadTokens?: number;
252
+ cacheWriteTokens?: number;
253
+ }): number;
254
+ /** Prefer the next-request projection; fall back to last-request pressure. */
255
+ export declare function contextPressureUsedTokens(pressure: {
256
+ projectedTokens?: number;
257
+ pressureTokens?: number;
258
+ } | undefined): number | undefined;
259
+ export declare function parseContextPressure(value: unknown): ContextPressureSample | undefined;
260
+ export declare function contextPressureView(sample: ContextPressureSample): ContextPressureView;
261
+ /**
262
+ * 8-segment Braille ring. Empty `⣀`; full `⣿`. Width is always 1 cell.
263
+ * Index is `ceil(percent / 12.5)` clamped to 0..8.
264
+ */
265
+ export declare const CONTEXT_RING_EMPTY = "\u28C0";
266
+ export declare const CONTEXT_RING_SEGMENTS: readonly ["⣀", "⠉", "⠋", "⠛", "⠞", "⠟", "⠿", "⡿", "⣿"];
267
+ export declare function formatContextPressureRing(percent: number): string;
268
+ export declare function contextPressureRingColor(level: ContextPressureView['level']): string;
269
+ export declare function formatContextPressureChip(view: ContextPressureView, color?: boolean): string;
270
+ export declare function formatContextPressureStatusLine(view: ContextPressureView | undefined): string;
271
+ export declare function contextPressureAlertText(view: ContextPressureView): string;
272
+ export declare function shouldIdleAutoCompact(view: ContextPressureView | undefined): boolean;
226
273
  /** Compact duration, matching the web stats line (45.2s / 2m42s). */
227
274
  export declare function formatDuration(ms: number): string;
228
275
  export declare function formatTokensPerSecond(tokensPerSecond: number): string;
@@ -306,6 +353,7 @@ export interface FooterStatusInput {
306
353
  subDiffers: boolean;
307
354
  quotaCode?: string;
308
355
  quotaPercent?: number;
356
+ contextChip?: string;
309
357
  balanceText?: string;
310
358
  search?: {
311
359
  index: number;
@@ -359,6 +407,7 @@ export interface StatusReportInput {
359
407
  disconnect?: DisconnectPolicyName;
360
408
  waitingQuestions: number;
361
409
  quota?: QuotaSnapshot;
410
+ context?: ContextPressureView;
362
411
  parentModel?: string;
363
412
  subProvider?: string;
364
413
  subModel: string;
@@ -573,6 +622,15 @@ export declare function countDiffAddDel(hunks: readonly ToolDiffHunk[] | undefin
573
622
  */
574
623
  export declare function diffStatToken(add: number, del: number): string;
575
624
  export declare function toolTargetPath(name: string, args: string, fallback?: string): string;
625
+ /** Path shown on a compact single-file edit summary. */
626
+ export declare function compactEditPath(item: {
627
+ name: string;
628
+ args: string;
629
+ summary?: string;
630
+ diff?: readonly {
631
+ path?: string;
632
+ }[];
633
+ }): string;
576
634
  export declare function countOutputLines(text: string): number;
577
635
  /**
578
636
  * Consecutive same-path reads (or edits) collapse onto one card.
@@ -642,6 +700,8 @@ export declare function parseFindQuery(raw: string): {
642
700
  export declare function promptInjectionSources(text: string, plugin?: string): string[];
643
701
  export declare function promptInjectionTitle(sources: readonly string[]): string;
644
702
  export declare function isPromptInjectionMessage(sourceKind: string, text: string, plugin?: string): boolean;
703
+ /** Official `/compact` idle-only failures, mapped to a local sentence. */
704
+ export declare function formatCompactCommandError(text: string): string;
645
705
  export declare function compactionHeaderText(row: {
646
706
  status: 'running' | 'ok' | 'error';
647
707
  pruneCount: number;
@@ -822,6 +882,8 @@ export declare class SshTui {
822
882
  private activeSubagents;
823
883
  private subagentSessions;
824
884
  private openToolCalls;
885
+ /** Survives result settlement so a card-less result can still be labelled. */
886
+ private toolCallNames;
825
887
  private readonly stats;
826
888
  private openStepStats;
827
889
  private readonly pendingToolTimes;
@@ -859,6 +921,10 @@ export declare class SshTui {
859
921
  private quotaAlerted;
860
922
  private quotaStepsSinceRefresh;
861
923
  private quotaRefreshInFlight;
924
+ private contextPressure;
925
+ private contextAlertLevel;
926
+ private idleCompactInFlight;
927
+ private lastIdleCompactAt;
862
928
  private searchHits;
863
929
  private searchIndex;
864
930
  private searchQuery;
@@ -936,6 +1002,8 @@ export declare class SshTui {
936
1002
  private markDirty;
937
1003
  private toolCardSummary;
938
1004
  private mergeIntoToolCard;
1005
+ private findToolRowByCallId;
1006
+ private findMergeableToolRow;
939
1007
  /** Append one transcript row, bounding memory on long sessions. */
940
1008
  private pushRow;
941
1009
  /** The transcript rows that support per-row expand/collapse. */
@@ -959,8 +1027,8 @@ export declare class SshTui {
959
1027
  private upsertPlanRow;
960
1028
  /** Whether the live plan strip should occupy the workspace footer. */
961
1029
  private shouldDockPlan;
962
- /** One follow-up per leftover list; replay and cancelled turns stay quiet. */
963
- private queuePlanCloseNudge;
1030
+ /** Send the leftover-todo nudge only from true idle, so /compact is not blocked. */
1031
+ private flushPlanCloseNudge;
964
1032
  /** Compact web-style plan strip pinned above the input, not in the transcript. */
965
1033
  private paintPlanDock;
966
1034
  private paintToolBodyLine;
@@ -1022,6 +1090,11 @@ export declare class SshTui {
1022
1090
  private handleExtensionEvent;
1023
1091
  private findCompactionRow;
1024
1092
  private handleCompactionEvent;
1093
+ private readContextPressure;
1094
+ private refreshContextPressure;
1095
+ private canRunCompactCommand;
1096
+ private maybeIdleAutoCompact;
1097
+ private dispatchCompactCommand;
1025
1098
  private handleCommandRun;
1026
1099
  private formatCommandText;
1027
1100
  private handleCommandDone;
@@ -1047,7 +1120,8 @@ export declare class SshTui {
1047
1120
  * output was unusable (caller falls back to prompt/reject).
1048
1121
  */
1049
1122
  private reviewUnknownWithModel;
1050
- private readonly handleApproval;
1123
+ private recordAutoApproval;
1124
+ readonly handleApproval: (request: ApprovalRequest, _next: () => Promise<ApprovalOutcome>) => Promise<ApprovalOutcome>;
1051
1125
  readonly handleUserQuestions: (request: AskUserQuestionRequest) => Promise<AskUserQuestionAnswer>;
1052
1126
  /** Queue one dialog behind an already-open one instead of overwriting it. */
1053
1127
  private openDialog;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ssh-tui",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "SSH-friendly interactive terminal TUI plugin for DeepSeek Harness",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -56,12 +56,12 @@
56
56
  "patch": "./cordis.patch.yml"
57
57
  },
58
58
  "compatibility": {
59
- "dsh": ">=0.1.1-rc.2 <0.2.0",
59
+ "dsh": ">=0.1.1-rc.2 <0.1.3",
60
60
  "dshReleases": {
61
61
  "0.1.2-rc.1": "compatible",
62
62
  "0.1.3-alpha.1": "unknown",
63
- "0.1.3-alpha.2": "unknown",
64
- "0.1.5-alpha.1": "unknown"
63
+ "0.1.3-alpha.2": "incompatible",
64
+ "0.1.5-alpha.1": "incompatible"
65
65
  },
66
66
  "profiles": ["tui"]
67
67
  }