pi-interactive-shell 0.7.1 → 0.8.1
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/CHANGELOG.md +19 -1
- package/README.md +19 -3
- package/config.ts +10 -2
- package/headless-monitor.ts +6 -0
- package/index.ts +6 -1
- package/overlay-component.ts +17 -5
- package/package.json +1 -1
- package/reattach-overlay.ts +8 -5
- package/tool-schema.ts +4 -0
- package/types.ts +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@ All notable changes to the `pi-interactive-shell` extension will be documented i
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.8.1] - 2026-02-08
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- README: documented `handsFree.gracePeriod` tool parameter and startup grace period behavior in Auto-Exit on Quiet and Dispatch sections.
|
|
11
|
+
- README: added missing `handoffPreviewLines` and `handoffPreviewMaxChars` to config settings table.
|
|
12
|
+
|
|
13
|
+
## [0.8.0] - 2026-02-08
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- `autoExitGracePeriod` config option (default: 30000ms, clamped 5000-120000ms) and `handsFree.gracePeriod` tool parameter override for startup quiet-kill grace control.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Default `overlayHeightPercent` increased from 45 to 60 for improved usable terminal rows on smaller displays.
|
|
20
|
+
- Overlay sizing now uses dynamic footer chrome: compact 2-line footer in normal states and full 6-line footer in detach dialog, increasing terminal viewport height during normal operation.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- Dispatch/hands-free `autoExitOnQuiet` no longer kills sessions during startup silence; quiet timer now re-arms during grace period and applies auto-kill only after grace expires.
|
|
24
|
+
- README config table missing `handoffPreviewLines` and `handoffPreviewMaxChars` entries despite appearing in the JSON example.
|
|
25
|
+
|
|
7
26
|
## [0.7.1] - 2026-02-03
|
|
8
27
|
|
|
9
28
|
### Changed
|
|
@@ -290,4 +309,3 @@ interactive_shell({ sessionId: "abc", input: "y", inputKeys: ["enter"] })
|
|
|
290
309
|
### Fixed
|
|
291
310
|
- Prevented TUI width crashes by avoiding unbounded terminal escape rendering.
|
|
292
311
|
- Reduced flicker by sanitizing/redrawing in a controlled overlay viewport.
|
|
293
|
-
|
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ Attach to review full output: interactive_shell({ attach: "calm-reef" })
|
|
|
115
115
|
|
|
116
116
|
The notification includes a brief tail (last 5 lines) and a reattach instruction. The PTY is preserved for 5 minutes so the agent can attach to review full scrollback.
|
|
117
117
|
|
|
118
|
-
Dispatch defaults `autoExitOnQuiet: true` — the session is killed after output goes silent (5s by default), which signals completion for task-oriented subagents.
|
|
118
|
+
Dispatch defaults `autoExitOnQuiet: true` — the session gets a 30s startup grace period, then is killed after output goes silent (5s by default), which signals completion for task-oriented subagents. Tune the grace period with `handsFree: { gracePeriod: 60000 }` or opt out entirely with `handsFree: { autoExitOnQuiet: false }`.
|
|
119
119
|
|
|
120
120
|
The overlay still shows for the user, who can Ctrl+T to transfer output, Ctrl+B to background, take over by typing, or Ctrl+Q for more options.
|
|
121
121
|
|
|
@@ -161,6 +161,18 @@ interactive_shell({
|
|
|
161
161
|
})
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
A 30s startup grace period prevents the session from being killed before the subprocess has time to produce output. Customize it per-call with `gracePeriod`:
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
interactive_shell({
|
|
168
|
+
command: 'pi "Run the full test suite"',
|
|
169
|
+
mode: "hands-free",
|
|
170
|
+
handsFree: { autoExitOnQuiet: true, gracePeriod: 60000 }
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The default grace period is also configurable globally via `autoExitGracePeriod` in the config file.
|
|
175
|
+
|
|
164
176
|
For multi-turn sessions where you need back-and-forth interaction, leave it disabled (default) and use `kill: true` when done.
|
|
165
177
|
|
|
166
178
|
### Send Input
|
|
@@ -260,7 +272,7 @@ Configuration files (project overrides global):
|
|
|
260
272
|
```json
|
|
261
273
|
{
|
|
262
274
|
"overlayWidthPercent": 95,
|
|
263
|
-
"overlayHeightPercent":
|
|
275
|
+
"overlayHeightPercent": 60,
|
|
264
276
|
"scrollbackLines": 5000,
|
|
265
277
|
"exitAutoCloseDelay": 10,
|
|
266
278
|
"minQueryIntervalSeconds": 60,
|
|
@@ -271,6 +283,7 @@ Configuration files (project overrides global):
|
|
|
271
283
|
"handsFreeUpdateMode": "on-quiet",
|
|
272
284
|
"handsFreeUpdateInterval": 60000,
|
|
273
285
|
"handsFreeQuietThreshold": 5000,
|
|
286
|
+
"autoExitGracePeriod": 30000,
|
|
274
287
|
"handsFreeUpdateMaxChars": 1500,
|
|
275
288
|
"handsFreeMaxTotalChars": 100000,
|
|
276
289
|
"handoffPreviewEnabled": true,
|
|
@@ -284,7 +297,7 @@ Configuration files (project overrides global):
|
|
|
284
297
|
| Setting | Default | Description |
|
|
285
298
|
|---------|---------|-------------|
|
|
286
299
|
| `overlayWidthPercent` | 95 | Overlay width (10-100%) |
|
|
287
|
-
| `overlayHeightPercent` |
|
|
300
|
+
| `overlayHeightPercent` | 60 | Overlay height (20-90%) |
|
|
288
301
|
| `scrollbackLines` | 5000 | Terminal scrollback buffer |
|
|
289
302
|
| `exitAutoCloseDelay` | 10 | Seconds before auto-close after exit |
|
|
290
303
|
| `minQueryIntervalSeconds` | 60 | Rate limit between agent queries |
|
|
@@ -294,10 +307,13 @@ Configuration files (project overrides global):
|
|
|
294
307
|
| `completionNotifyMaxChars` | 5000 | Max chars in completion notification (1KB-50KB) |
|
|
295
308
|
| `handsFreeUpdateMode` | "on-quiet" | "on-quiet" or "interval" |
|
|
296
309
|
| `handsFreeQuietThreshold` | 5000 | Silence duration before update (ms) |
|
|
310
|
+
| `autoExitGracePeriod` | 30000 | Startup grace before `autoExitOnQuiet` kill (ms) |
|
|
297
311
|
| `handsFreeUpdateInterval` | 60000 | Max interval between updates (ms) |
|
|
298
312
|
| `handsFreeUpdateMaxChars` | 1500 | Max chars per update |
|
|
299
313
|
| `handsFreeMaxTotalChars` | 100000 | Total char budget for updates |
|
|
300
314
|
| `handoffPreviewEnabled` | true | Include tail in tool result |
|
|
315
|
+
| `handoffPreviewLines` | 30 | Lines in tail preview (0-500) |
|
|
316
|
+
| `handoffPreviewMaxChars` | 2000 | Max chars in tail preview (0-50KB) |
|
|
301
317
|
| `handoffSnapshotEnabled` | false | Write transcript on detach/exit |
|
|
302
318
|
| `ansiReemit` | true | Preserve ANSI colors in output |
|
|
303
319
|
|
package/config.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface InteractiveShellConfig {
|
|
|
24
24
|
handsFreeUpdateMode: "on-quiet" | "interval";
|
|
25
25
|
handsFreeUpdateInterval: number;
|
|
26
26
|
handsFreeQuietThreshold: number;
|
|
27
|
+
autoExitGracePeriod: number;
|
|
27
28
|
handsFreeUpdateMaxChars: number;
|
|
28
29
|
handsFreeMaxTotalChars: number;
|
|
29
30
|
// Query rate limiting
|
|
@@ -33,7 +34,7 @@ export interface InteractiveShellConfig {
|
|
|
33
34
|
const DEFAULT_CONFIG: InteractiveShellConfig = {
|
|
34
35
|
exitAutoCloseDelay: 10,
|
|
35
36
|
overlayWidthPercent: 95,
|
|
36
|
-
overlayHeightPercent:
|
|
37
|
+
overlayHeightPercent: 60,
|
|
37
38
|
scrollbackLines: 5000,
|
|
38
39
|
ansiReemit: true,
|
|
39
40
|
handoffPreviewEnabled: true,
|
|
@@ -52,6 +53,7 @@ const DEFAULT_CONFIG: InteractiveShellConfig = {
|
|
|
52
53
|
handsFreeUpdateMode: "on-quiet" as const,
|
|
53
54
|
handsFreeUpdateInterval: 60000,
|
|
54
55
|
handsFreeQuietThreshold: 5000,
|
|
56
|
+
autoExitGracePeriod: 30000,
|
|
55
57
|
handsFreeUpdateMaxChars: 1500,
|
|
56
58
|
handsFreeMaxTotalChars: 100000,
|
|
57
59
|
// Query rate limiting (default 60 seconds between queries)
|
|
@@ -87,7 +89,7 @@ export function loadConfig(cwd: string): InteractiveShellConfig {
|
|
|
87
89
|
...merged,
|
|
88
90
|
exitAutoCloseDelay: clampInt(merged.exitAutoCloseDelay, DEFAULT_CONFIG.exitAutoCloseDelay, 0, 60),
|
|
89
91
|
overlayWidthPercent: clampPercent(merged.overlayWidthPercent, DEFAULT_CONFIG.overlayWidthPercent),
|
|
90
|
-
// Height: 20-90% range (default
|
|
92
|
+
// Height: 20-90% range (default 60%)
|
|
91
93
|
overlayHeightPercent: clampInt(merged.overlayHeightPercent, DEFAULT_CONFIG.overlayHeightPercent, 20, 90),
|
|
92
94
|
scrollbackLines: clampInt(merged.scrollbackLines, DEFAULT_CONFIG.scrollbackLines, 200, 50000),
|
|
93
95
|
ansiReemit: merged.ansiReemit !== false,
|
|
@@ -127,6 +129,12 @@ export function loadConfig(cwd: string): InteractiveShellConfig {
|
|
|
127
129
|
1000,
|
|
128
130
|
30000,
|
|
129
131
|
),
|
|
132
|
+
autoExitGracePeriod: clampInt(
|
|
133
|
+
merged.autoExitGracePeriod,
|
|
134
|
+
DEFAULT_CONFIG.autoExitGracePeriod,
|
|
135
|
+
5000,
|
|
136
|
+
120000,
|
|
137
|
+
),
|
|
130
138
|
handsFreeUpdateMaxChars: clampInt(
|
|
131
139
|
merged.handsFreeUpdateMaxChars,
|
|
132
140
|
DEFAULT_CONFIG.handsFreeUpdateMaxChars,
|
package/headless-monitor.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { InteractiveShellConfig } from "./config.js";
|
|
|
4
4
|
export interface HeadlessMonitorOptions {
|
|
5
5
|
autoExitOnQuiet: boolean;
|
|
6
6
|
quietThreshold: number;
|
|
7
|
+
gracePeriod?: number;
|
|
7
8
|
timeout?: number;
|
|
8
9
|
}
|
|
9
10
|
|
|
@@ -80,6 +81,11 @@ export class HeadlessDispatchMonitor {
|
|
|
80
81
|
this.quietTimer = setTimeout(() => {
|
|
81
82
|
this.quietTimer = null;
|
|
82
83
|
if (!this._disposed && this.options.autoExitOnQuiet) {
|
|
84
|
+
const gracePeriod = this.options.gracePeriod ?? this.config.autoExitGracePeriod;
|
|
85
|
+
if (Date.now() - this.startTime < gracePeriod) {
|
|
86
|
+
this.resetQuietTimer();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
83
89
|
this.session.kill();
|
|
84
90
|
this.handleCompletion(null, undefined, false, true);
|
|
85
91
|
}
|
package/index.ts
CHANGED
|
@@ -494,6 +494,7 @@ export default function interactiveShellExtension(pi: ExtensionAPI) {
|
|
|
494
494
|
autoExitOnQuiet: mode === "dispatch"
|
|
495
495
|
? handsFree?.autoExitOnQuiet !== false
|
|
496
496
|
: handsFree?.autoExitOnQuiet === true,
|
|
497
|
+
autoExitGracePeriod: handsFree?.gracePeriod ?? config.autoExitGracePeriod,
|
|
497
498
|
handoffPreviewEnabled: handoffPreview?.enabled,
|
|
498
499
|
handoffPreviewLines: handoffPreview?.lines,
|
|
499
500
|
handoffPreviewMaxChars: handoffPreview?.maxChars,
|
|
@@ -637,6 +638,7 @@ export default function interactiveShellExtension(pi: ExtensionAPI) {
|
|
|
637
638
|
const monitor = new HeadlessDispatchMonitor(session, config, {
|
|
638
639
|
autoExitOnQuiet: handsFree?.autoExitOnQuiet !== false,
|
|
639
640
|
quietThreshold: handsFree?.quietThreshold ?? config.handsFreeQuietThreshold,
|
|
641
|
+
gracePeriod: handsFree?.gracePeriod ?? config.autoExitGracePeriod,
|
|
640
642
|
timeout,
|
|
641
643
|
}, makeMonitorCompletionCallback(pi, id, startTime));
|
|
642
644
|
headlessMonitors.set(id, monitor);
|
|
@@ -695,6 +697,7 @@ export default function interactiveShellExtension(pi: ExtensionAPI) {
|
|
|
695
697
|
autoExitOnQuiet: mode === "dispatch"
|
|
696
698
|
? handsFree?.autoExitOnQuiet !== false
|
|
697
699
|
: handsFree?.autoExitOnQuiet === true,
|
|
700
|
+
autoExitGracePeriod: handsFree?.gracePeriod ?? config.autoExitGracePeriod,
|
|
698
701
|
handoffPreviewEnabled: handoffPreview?.enabled,
|
|
699
702
|
handoffPreviewLines: handoffPreview?.lines,
|
|
700
703
|
handoffPreviewMaxChars: handoffPreview?.maxChars,
|
|
@@ -760,6 +763,7 @@ export default function interactiveShellExtension(pi: ExtensionAPI) {
|
|
|
760
763
|
handsFreeUpdateMaxChars: handsFree?.updateMaxChars,
|
|
761
764
|
handsFreeMaxTotalChars: handsFree?.maxTotalChars,
|
|
762
765
|
autoExitOnQuiet: handsFree?.autoExitOnQuiet,
|
|
766
|
+
autoExitGracePeriod: handsFree?.gracePeriod ?? config.autoExitGracePeriod,
|
|
763
767
|
onHandsFreeUpdate: mode === "hands-free"
|
|
764
768
|
? (update) => {
|
|
765
769
|
let statusText: string;
|
|
@@ -974,7 +978,7 @@ function setupDispatchCompletion(
|
|
|
974
978
|
command: string;
|
|
975
979
|
reason?: string;
|
|
976
980
|
timeout?: number;
|
|
977
|
-
handsFree?: { autoExitOnQuiet?: boolean; quietThreshold?: number };
|
|
981
|
+
handsFree?: { autoExitOnQuiet?: boolean; quietThreshold?: number; gracePeriod?: number };
|
|
978
982
|
overlayStartTime?: number;
|
|
979
983
|
},
|
|
980
984
|
): void {
|
|
@@ -1030,6 +1034,7 @@ function setupDispatchCompletion(
|
|
|
1030
1034
|
const monitor = new HeadlessDispatchMonitor(bgSession.session, config, {
|
|
1031
1035
|
autoExitOnQuiet: ctx.handsFree?.autoExitOnQuiet !== false,
|
|
1032
1036
|
quietThreshold: ctx.handsFree?.quietThreshold ?? config.handsFreeQuietThreshold,
|
|
1037
|
+
gracePeriod: ctx.handsFree?.gracePeriod ?? config.autoExitGracePeriod,
|
|
1033
1038
|
timeout: remainingTimeout,
|
|
1034
1039
|
}, makeMonitorCompletionCallback(pi, bgId, bgStartTime));
|
|
1035
1040
|
headlessMonitors.set(bgId, monitor);
|
package/overlay-component.ts
CHANGED
|
@@ -13,8 +13,9 @@ import {
|
|
|
13
13
|
type InteractiveShellOptions,
|
|
14
14
|
type DialogChoice,
|
|
15
15
|
type OverlayState,
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
HEADER_LINES,
|
|
17
|
+
FOOTER_LINES_COMPACT,
|
|
18
|
+
FOOTER_LINES_DIALOG,
|
|
18
19
|
formatDuration,
|
|
19
20
|
} from "./types.js";
|
|
20
21
|
|
|
@@ -80,7 +81,7 @@ export class InteractiveShellOverlay implements Component, Focusable {
|
|
|
80
81
|
const overlayWidth = Math.floor((tui.terminal.columns * this.config.overlayWidthPercent) / 100);
|
|
81
82
|
const overlayHeight = Math.floor((tui.terminal.rows * this.config.overlayHeightPercent) / 100);
|
|
82
83
|
const cols = Math.max(20, overlayWidth - 4);
|
|
83
|
-
const rows = Math.max(3, overlayHeight -
|
|
84
|
+
const rows = Math.max(3, overlayHeight - (HEADER_LINES + FOOTER_LINES_COMPACT + 2));
|
|
84
85
|
|
|
85
86
|
const ptyEvents = {
|
|
86
87
|
onData: () => {
|
|
@@ -449,6 +450,15 @@ export class InteractiveShellOverlay implements Component, Focusable {
|
|
|
449
450
|
if (this.state === "hands-free") {
|
|
450
451
|
// Auto-exit on quiet: kill session when output stops (agent likely finished task)
|
|
451
452
|
if (this.options.autoExitOnQuiet) {
|
|
453
|
+
const gracePeriod = this.options.autoExitGracePeriod ?? this.config.autoExitGracePeriod;
|
|
454
|
+
if (Date.now() - this.startTime < gracePeriod) {
|
|
455
|
+
if (this.hasUnsentData) {
|
|
456
|
+
this.emitHandsFreeUpdate();
|
|
457
|
+
this.hasUnsentData = false;
|
|
458
|
+
}
|
|
459
|
+
this.resetQuietTimer();
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
452
462
|
// Emit final update with any pending output
|
|
453
463
|
if (this.hasUnsentData) {
|
|
454
464
|
this.emitHandsFreeUpdate();
|
|
@@ -1077,7 +1087,9 @@ export class InteractiveShellOverlay implements Component, Focusable {
|
|
|
1077
1087
|
lines.push(border("├" + "─".repeat(width - 2) + "┤"));
|
|
1078
1088
|
|
|
1079
1089
|
const overlayHeight = Math.floor((this.tui.terminal.rows * this.config.overlayHeightPercent) / 100);
|
|
1080
|
-
const
|
|
1090
|
+
const footerHeight = this.state === "detach-dialog" ? FOOTER_LINES_DIALOG : FOOTER_LINES_COMPACT;
|
|
1091
|
+
const chrome = HEADER_LINES + footerHeight + 2;
|
|
1092
|
+
const termRows = Math.max(3, overlayHeight - chrome);
|
|
1081
1093
|
|
|
1082
1094
|
if (innerWidth !== this.lastWidth || termRows !== this.lastHeight) {
|
|
1083
1095
|
this.session.resize(innerWidth, termRows);
|
|
@@ -1136,7 +1148,7 @@ export class InteractiveShellOverlay implements Component, Focusable {
|
|
|
1136
1148
|
footerLines.push(row(dim("Ctrl+T transfer • Ctrl+B background • Ctrl+Q menu • Shift+Up/Down scroll")));
|
|
1137
1149
|
}
|
|
1138
1150
|
|
|
1139
|
-
while (footerLines.length <
|
|
1151
|
+
while (footerLines.length < footerHeight) {
|
|
1140
1152
|
footerLines.push(emptyRow());
|
|
1141
1153
|
}
|
|
1142
1154
|
lines.push(...footerLines);
|
package/package.json
CHANGED
package/reattach-overlay.ts
CHANGED
|
@@ -11,8 +11,9 @@ import {
|
|
|
11
11
|
type InteractiveShellResult,
|
|
12
12
|
type DialogChoice,
|
|
13
13
|
type OverlayState,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
HEADER_LINES,
|
|
15
|
+
FOOTER_LINES_COMPACT,
|
|
16
|
+
FOOTER_LINES_DIALOG,
|
|
16
17
|
} from "./types.js";
|
|
17
18
|
|
|
18
19
|
export class ReattachOverlay implements Component, Focusable {
|
|
@@ -74,7 +75,7 @@ export class ReattachOverlay implements Component, Focusable {
|
|
|
74
75
|
const overlayWidth = Math.floor((tui.terminal.columns * this.config.overlayWidthPercent) / 100);
|
|
75
76
|
const overlayHeight = Math.floor((tui.terminal.rows * this.config.overlayHeightPercent) / 100);
|
|
76
77
|
const cols = Math.max(20, overlayWidth - 4);
|
|
77
|
-
const rows = Math.max(3, overlayHeight -
|
|
78
|
+
const rows = Math.max(3, overlayHeight - (HEADER_LINES + FOOTER_LINES_COMPACT + 2));
|
|
78
79
|
bgSession.session.resize(cols, rows);
|
|
79
80
|
}
|
|
80
81
|
|
|
@@ -400,7 +401,9 @@ export class ReattachOverlay implements Component, Focusable {
|
|
|
400
401
|
lines.push(border("├" + "─".repeat(width - 2) + "┤"));
|
|
401
402
|
|
|
402
403
|
const overlayHeight = Math.floor((this.tui.terminal.rows * this.config.overlayHeightPercent) / 100);
|
|
403
|
-
const
|
|
404
|
+
const footerHeight = this.state === "detach-dialog" ? FOOTER_LINES_DIALOG : FOOTER_LINES_COMPACT;
|
|
405
|
+
const chrome = HEADER_LINES + footerHeight + 2;
|
|
406
|
+
const termRows = Math.max(3, overlayHeight - chrome);
|
|
404
407
|
|
|
405
408
|
if (innerWidth !== this.lastWidth || termRows !== this.lastHeight) {
|
|
406
409
|
this.session.resize(innerWidth, termRows);
|
|
@@ -457,7 +460,7 @@ export class ReattachOverlay implements Component, Focusable {
|
|
|
457
460
|
footerLines.push(row(dim("Ctrl+T transfer • Ctrl+B background • Ctrl+Q menu • Shift+Up/Down scroll")));
|
|
458
461
|
}
|
|
459
462
|
|
|
460
|
-
while (footerLines.length <
|
|
463
|
+
while (footerLines.length < footerHeight) {
|
|
461
464
|
footerLines.push(emptyRow());
|
|
462
465
|
}
|
|
463
466
|
lines.push(...footerLines);
|
package/tool-schema.ts
CHANGED
|
@@ -236,6 +236,9 @@ export const toolParameters = Type.Object({
|
|
|
236
236
|
quietThreshold: Type.Optional(
|
|
237
237
|
Type.Number({ description: "Silence duration before emitting update in on-quiet mode (default: 5000ms)" }),
|
|
238
238
|
),
|
|
239
|
+
gracePeriod: Type.Optional(
|
|
240
|
+
Type.Number({ description: "Startup grace period before autoExitOnQuiet can kill the session (default: 30000ms)" }),
|
|
241
|
+
),
|
|
239
242
|
updateMaxChars: Type.Optional(
|
|
240
243
|
Type.Number({ description: "Max chars per update (default: 1500)" }),
|
|
241
244
|
),
|
|
@@ -299,6 +302,7 @@ export interface ToolParams {
|
|
|
299
302
|
updateMode?: "on-quiet" | "interval";
|
|
300
303
|
updateInterval?: number;
|
|
301
304
|
quietThreshold?: number;
|
|
305
|
+
gracePeriod?: number;
|
|
302
306
|
updateMaxChars?: number;
|
|
303
307
|
maxTotalChars?: number;
|
|
304
308
|
autoExitOnQuiet?: boolean;
|
package/types.ts
CHANGED
|
@@ -70,6 +70,7 @@ export interface InteractiveShellOptions {
|
|
|
70
70
|
onHandsFreeUpdate?: (update: HandsFreeUpdate) => void;
|
|
71
71
|
// Auto-exit when output stops (for agents that don't exit on their own)
|
|
72
72
|
autoExitOnQuiet?: boolean;
|
|
73
|
+
autoExitGracePeriod?: number;
|
|
73
74
|
// Auto-kill timeout
|
|
74
75
|
timeout?: number;
|
|
75
76
|
// Existing PTY session (for attach flow -- skip creating a new PTY)
|
|
@@ -80,9 +81,9 @@ export type DialogChoice = "kill" | "background" | "transfer" | "cancel";
|
|
|
80
81
|
export type OverlayState = "running" | "exited" | "detach-dialog" | "hands-free";
|
|
81
82
|
|
|
82
83
|
// UI constants
|
|
83
|
-
export const
|
|
84
|
+
export const FOOTER_LINES_COMPACT = 2;
|
|
85
|
+
export const FOOTER_LINES_DIALOG = 6;
|
|
84
86
|
export const HEADER_LINES = 4;
|
|
85
|
-
export const CHROME_LINES = HEADER_LINES + FOOTER_LINES + 2;
|
|
86
87
|
|
|
87
88
|
/** Format milliseconds to human-readable duration */
|
|
88
89
|
export function formatDuration(ms: number): string {
|