pty-manager 1.6.0 → 1.6.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/README.md +8 -4
- package/dist/index.d.mts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +19 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +19 -8
- package/dist/index.mjs.map +1 -1
- package/dist/pty-worker.js +19 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ PTY session manager with lifecycle management, pluggable adapters, and blocking
|
|
|
10
10
|
- **Auto-response rules** - Automatically respond to known prompts with text or key sequences
|
|
11
11
|
- **TUI menu navigation** - Navigate arrow-key menus via `selectMenuOption()` and key-sequence rules
|
|
12
12
|
- **Stall detection** - Content-based stall detection with pluggable external classifiers, loading suppression, and exponential backoff
|
|
13
|
-
- **Task completion detection** -
|
|
13
|
+
- **Task completion detection** - Settle-based fast-path that short-circuits the LLM stall classifier when the CLI returns to its idle prompt, resilient to decorative TUI rendering after the prompt
|
|
14
14
|
- **Terminal attachment** - Attach to sessions for raw I/O streaming
|
|
15
15
|
- **Special key support** - Send Ctrl, Alt, Shift, and function key combinations via `sendKeys()`
|
|
16
16
|
- **Bracketed paste** - Proper paste handling with bracketed paste mode support
|
|
@@ -475,11 +475,15 @@ const handle = await manager.spawn({
|
|
|
475
475
|
|
|
476
476
|
## Stall Detection & Task Completion
|
|
477
477
|
|
|
478
|
-
Content-based stall detection monitors sessions for output that stops changing. The content hash strips the full buffer first, then slices the last 500 characters of the normalized text — this ensures identical visual content always produces the same hash regardless of how many raw escape sequences surround it. The normalization strips ANSI escape codes, TUI spinner characters, and countdown/duration text (e.g. `8m 17s` → constant) so that live timers and
|
|
478
|
+
Content-based stall detection monitors sessions for output that stops changing. The content hash strips the full buffer first, then slices the last 500 characters of the normalized text — this ensures identical visual content always produces the same hash regardless of how many raw escape sequences surround it. The normalization strips ANSI escape codes, carriage returns (`\r`), non-breaking spaces (`\xa0`), TUI spinner characters, and countdown/duration text (e.g. `8m 17s` → constant) so that live timers, TUI line-overwrites, and redraws don't perpetually reset the stall timer. All detection work (ready, blocking prompt, login, exit, stall) runs in a deferred `setImmediate()` tick so that node-pty's synchronous data delivery cannot starve the event loop — timers and I/O callbacks always get a chance to run between data bursts. The output buffer is capped at 100 KB to prevent unbounded growth during long tasks.
|
|
479
479
|
|
|
480
|
-
|
|
480
|
+
### Task Completion Fast-Path (Settle Pattern)
|
|
481
481
|
|
|
482
|
-
|
|
482
|
+
When a busy session's output matches the adapter's `detectReady()` pattern, a `task_complete` debounce timer starts. TUI agents like Claude Code continue rendering decorative output after the prompt — update notices, shortcut hints, status bar updates. Instead of cancelling the timer on each new data chunk (which would prevent the event from ever firing), the session uses a **settle pattern**: the debounce timer resets on each new chunk but is never cancelled. The timer callback re-verifies `detectReady()` before transitioning, so stale triggers are safe.
|
|
483
|
+
|
|
484
|
+
This mirrors the `readySettlePending` pattern used for startup ready detection, and ensures the `task_complete` event fires reliably even when TUI chrome continues rendering after the agent has finished its task.
|
|
485
|
+
|
|
486
|
+
If the fast-path timer doesn't fire (e.g. the prompt indicator disappears from the buffer), the session falls back to stall detection which emits `stall_detected` for external classification.
|
|
483
487
|
|
|
484
488
|
```typescript
|
|
485
489
|
// Enable stall detection with a pluggable classifier
|
package/dist/index.d.mts
CHANGED
|
@@ -461,6 +461,7 @@ declare class PTYSession extends EventEmitter {
|
|
|
461
461
|
private _stallBackoffMs;
|
|
462
462
|
private static readonly MAX_STALL_BACKOFF_MS;
|
|
463
463
|
private _taskCompleteTimer;
|
|
464
|
+
private _taskCompletePending;
|
|
464
465
|
private static readonly TASK_COMPLETE_DEBOUNCE_MS;
|
|
465
466
|
private _readySettleTimer;
|
|
466
467
|
private _readySettlePending;
|
|
@@ -536,8 +537,12 @@ declare class PTYSession extends EventEmitter {
|
|
|
536
537
|
handleStallClassification(classification: StallClassification | null): void;
|
|
537
538
|
/**
|
|
538
539
|
* Schedule a task_complete transition after a debounce period.
|
|
539
|
-
*
|
|
540
|
-
*
|
|
540
|
+
* Uses a settle pattern: each call resets the debounce timer instead of
|
|
541
|
+
* being a no-op when already scheduled. This allows TUI agents that
|
|
542
|
+
* continue rendering decorative output (status bar, update notices) after
|
|
543
|
+
* the prompt to eventually settle, rather than having the timer cancelled
|
|
544
|
+
* by every new data chunk. The callback re-verifies detectReady() before
|
|
545
|
+
* transitioning, so stale triggers are safe.
|
|
541
546
|
*/
|
|
542
547
|
private scheduleTaskComplete;
|
|
543
548
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -461,6 +461,7 @@ declare class PTYSession extends EventEmitter {
|
|
|
461
461
|
private _stallBackoffMs;
|
|
462
462
|
private static readonly MAX_STALL_BACKOFF_MS;
|
|
463
463
|
private _taskCompleteTimer;
|
|
464
|
+
private _taskCompletePending;
|
|
464
465
|
private static readonly TASK_COMPLETE_DEBOUNCE_MS;
|
|
465
466
|
private _readySettleTimer;
|
|
466
467
|
private _readySettlePending;
|
|
@@ -536,8 +537,12 @@ declare class PTYSession extends EventEmitter {
|
|
|
536
537
|
handleStallClassification(classification: StallClassification | null): void;
|
|
537
538
|
/**
|
|
538
539
|
* Schedule a task_complete transition after a debounce period.
|
|
539
|
-
*
|
|
540
|
-
*
|
|
540
|
+
* Uses a settle pattern: each call resets the debounce timer instead of
|
|
541
|
+
* being a no-op when already scheduled. This allows TUI agents that
|
|
542
|
+
* continue rendering decorative output (status bar, update notices) after
|
|
543
|
+
* the prompt to eventually settle, rather than having the timer cancelled
|
|
544
|
+
* by every new data chunk. The callback re-verifies detectReady() before
|
|
545
|
+
* transitioning, so stale triggers are safe.
|
|
541
546
|
*/
|
|
542
547
|
private scheduleTaskComplete;
|
|
543
548
|
/**
|
package/dist/index.js
CHANGED
|
@@ -350,6 +350,7 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
350
350
|
static MAX_STALL_BACKOFF_MS = 3e4;
|
|
351
351
|
// Task completion detection (idle detection when busy)
|
|
352
352
|
_taskCompleteTimer = null;
|
|
353
|
+
_taskCompletePending = false;
|
|
353
354
|
static TASK_COMPLETE_DEBOUNCE_MS = 1500;
|
|
354
355
|
// Ready detection settle delay — defers session_ready until output goes quiet
|
|
355
356
|
_readySettleTimer = null;
|
|
@@ -562,7 +563,8 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
562
563
|
result = result.replace(/\x1b\[\d*(?:;\d+)?[Hf]/g, " ");
|
|
563
564
|
result = result.replace(/\x1b\[\d*[JK]/g, " ");
|
|
564
565
|
result = result.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
565
|
-
result = result.replace(/[\x00-\x08\x0b
|
|
566
|
+
result = result.replace(/[\x00-\x08\x0b-\x1f\x7f]/g, "");
|
|
567
|
+
result = result.replace(/\xa0/g, " ");
|
|
566
568
|
result = result.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷✻✶✳✢⏺←→↑↓⬆⬇◆◇▪▫■□▲△▼▽◈⟨⟩⌘⏎⏏⌫⌦⇧⇪⌥]/g, " ");
|
|
567
569
|
result = result.replace(/\d+[hms](?:\s+\d+[hms])*/g, "0s");
|
|
568
570
|
result = result.replace(/ {2,}/g, " ");
|
|
@@ -639,13 +641,21 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
639
641
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
640
642
|
/**
|
|
641
643
|
* Schedule a task_complete transition after a debounce period.
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
+
* Uses a settle pattern: each call resets the debounce timer instead of
|
|
645
|
+
* being a no-op when already scheduled. This allows TUI agents that
|
|
646
|
+
* continue rendering decorative output (status bar, update notices) after
|
|
647
|
+
* the prompt to eventually settle, rather than having the timer cancelled
|
|
648
|
+
* by every new data chunk. The callback re-verifies detectReady() before
|
|
649
|
+
* transitioning, so stale triggers are safe.
|
|
644
650
|
*/
|
|
645
651
|
scheduleTaskComplete() {
|
|
646
|
-
if (this._taskCompleteTimer)
|
|
652
|
+
if (this._taskCompleteTimer) {
|
|
653
|
+
clearTimeout(this._taskCompleteTimer);
|
|
654
|
+
}
|
|
655
|
+
this._taskCompletePending = true;
|
|
647
656
|
this._taskCompleteTimer = setTimeout(() => {
|
|
648
657
|
this._taskCompleteTimer = null;
|
|
658
|
+
this._taskCompletePending = false;
|
|
649
659
|
if (this._status !== "busy") return;
|
|
650
660
|
if (!this.adapter.detectReady(this.outputBuffer)) return;
|
|
651
661
|
this._status = "ready";
|
|
@@ -666,6 +676,7 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
666
676
|
clearTimeout(this._taskCompleteTimer);
|
|
667
677
|
this._taskCompleteTimer = null;
|
|
668
678
|
}
|
|
679
|
+
this._taskCompletePending = false;
|
|
669
680
|
}
|
|
670
681
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
671
682
|
// Ready Detection Settle Delay
|
|
@@ -807,10 +818,10 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
807
818
|
this.scheduleReadySettle();
|
|
808
819
|
return;
|
|
809
820
|
}
|
|
810
|
-
if (this._status === "busy"
|
|
811
|
-
this.
|
|
812
|
-
|
|
813
|
-
|
|
821
|
+
if (this._status === "busy") {
|
|
822
|
+
if (this._taskCompletePending || this.adapter.detectReady(this.outputBuffer)) {
|
|
823
|
+
this.scheduleTaskComplete();
|
|
824
|
+
}
|
|
814
825
|
}
|
|
815
826
|
const blockingPrompt = this.detectAndHandleBlockingPrompt();
|
|
816
827
|
if (blockingPrompt) {
|