pty-manager 1.3.3 → 1.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.md +15 -1
- package/dist/index.d.mts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +52 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +52 -6
- package/dist/index.mjs.map +1 -1
- package/dist/pty-worker.js +52 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -188,7 +188,7 @@ class PTYManager extends EventEmitter {
|
|
|
188
188
|
| Event | Payload | Description |
|
|
189
189
|
|-------|---------|-------------|
|
|
190
190
|
| `session_started` | `SessionHandle` | Session spawn initiated |
|
|
191
|
-
| `session_ready` | `SessionHandle` | Session ready for input |
|
|
191
|
+
| `session_ready` | `SessionHandle` | Session ready for input (after settle delay) |
|
|
192
192
|
| `session_stopped` | `SessionHandle, reason` | Session terminated |
|
|
193
193
|
| `session_error` | `SessionHandle, error` | Error occurred |
|
|
194
194
|
| `login_required` | `SessionHandle, instructions?, url?` | Auth required |
|
|
@@ -443,6 +443,20 @@ Adapters can declare `usesTuiMenus: true` to indicate they use arrow-key menus i
|
|
|
443
443
|
await session.selectMenuOption(2); // Sends Down, Down, Enter with 50ms delays
|
|
444
444
|
```
|
|
445
445
|
|
|
446
|
+
## Ready Detection Settle Delay
|
|
447
|
+
|
|
448
|
+
When an adapter's `detectReady()` first matches during startup, `session_ready` is **not** emitted immediately. Instead, the session waits for output to go quiet for `readySettleMs` (default: 100ms) before firing the event. This prevents the orchestrator from sending input while a TUI agent is still rendering (status bar, keyboard shortcuts, update notices).
|
|
449
|
+
|
|
450
|
+
Adapters can override `readySettleMs` to tune the delay for their CLI's rendering speed. If new output arrives during the settle window, the timer resets. If the ready indicator disappears (e.g. a blocking prompt appears), the settle is cancelled.
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
class MyCLIAdapter extends BaseCLIAdapter {
|
|
454
|
+
// Heavy TUI rendering — wait longer before declaring ready
|
|
455
|
+
readonly readySettleMs = 500;
|
|
456
|
+
// ...
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
446
460
|
## Stall Detection & Task Completion
|
|
447
461
|
|
|
448
462
|
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 TUI 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.
|
package/dist/index.d.mts
CHANGED
|
@@ -98,7 +98,7 @@ interface LoginDetection {
|
|
|
98
98
|
/**
|
|
99
99
|
* Types of blocking prompts that can occur
|
|
100
100
|
*/
|
|
101
|
-
type BlockingPromptType = 'login' | 'update' | 'config' | 'tos' | 'model_select' | 'project_select' | 'permission' | 'unknown';
|
|
101
|
+
type BlockingPromptType = 'login' | 'update' | 'config' | 'tos' | 'model_select' | 'project_select' | 'permission' | 'tool_wait' | 'unknown';
|
|
102
102
|
/**
|
|
103
103
|
* Blocking prompt detection result
|
|
104
104
|
*/
|
|
@@ -351,6 +351,8 @@ interface CLIAdapter {
|
|
|
351
351
|
version?: string;
|
|
352
352
|
error?: string;
|
|
353
353
|
}>;
|
|
354
|
+
/** Ms of output silence after detectReady match before emitting session_ready (default: 100) */
|
|
355
|
+
readonly readySettleMs?: number;
|
|
354
356
|
/**
|
|
355
357
|
* Optional: Get health check command
|
|
356
358
|
*/
|
|
@@ -446,6 +448,8 @@ declare class PTYSession extends EventEmitter {
|
|
|
446
448
|
private _lastContentHash;
|
|
447
449
|
private _taskCompleteTimer;
|
|
448
450
|
private static readonly TASK_COMPLETE_DEBOUNCE_MS;
|
|
451
|
+
private _readySettleTimer;
|
|
452
|
+
private _readySettlePending;
|
|
449
453
|
private _processScheduled;
|
|
450
454
|
private static readonly MAX_OUTPUT_BUFFER;
|
|
451
455
|
readonly id: string;
|
|
@@ -527,6 +531,18 @@ declare class PTYSession extends EventEmitter {
|
|
|
527
531
|
* doesn't match the idle prompt, so the agent is still working).
|
|
528
532
|
*/
|
|
529
533
|
private cancelTaskComplete;
|
|
534
|
+
/**
|
|
535
|
+
* Schedule or reset the ready-settle timer.
|
|
536
|
+
* Defers emitting session_ready until output goes quiet for readySettleMs
|
|
537
|
+
* after detectReady first matches. This prevents sending input while
|
|
538
|
+
* TUI agents are still rendering (status bar, shortcuts, update notices).
|
|
539
|
+
*/
|
|
540
|
+
private scheduleReadySettle;
|
|
541
|
+
/**
|
|
542
|
+
* Cancel a pending ready-settle timer (ready indicator disappeared
|
|
543
|
+
* or session status changed).
|
|
544
|
+
*/
|
|
545
|
+
private cancelReadySettle;
|
|
530
546
|
/**
|
|
531
547
|
* Start the PTY session
|
|
532
548
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -98,7 +98,7 @@ interface LoginDetection {
|
|
|
98
98
|
/**
|
|
99
99
|
* Types of blocking prompts that can occur
|
|
100
100
|
*/
|
|
101
|
-
type BlockingPromptType = 'login' | 'update' | 'config' | 'tos' | 'model_select' | 'project_select' | 'permission' | 'unknown';
|
|
101
|
+
type BlockingPromptType = 'login' | 'update' | 'config' | 'tos' | 'model_select' | 'project_select' | 'permission' | 'tool_wait' | 'unknown';
|
|
102
102
|
/**
|
|
103
103
|
* Blocking prompt detection result
|
|
104
104
|
*/
|
|
@@ -351,6 +351,8 @@ interface CLIAdapter {
|
|
|
351
351
|
version?: string;
|
|
352
352
|
error?: string;
|
|
353
353
|
}>;
|
|
354
|
+
/** Ms of output silence after detectReady match before emitting session_ready (default: 100) */
|
|
355
|
+
readonly readySettleMs?: number;
|
|
354
356
|
/**
|
|
355
357
|
* Optional: Get health check command
|
|
356
358
|
*/
|
|
@@ -446,6 +448,8 @@ declare class PTYSession extends EventEmitter {
|
|
|
446
448
|
private _lastContentHash;
|
|
447
449
|
private _taskCompleteTimer;
|
|
448
450
|
private static readonly TASK_COMPLETE_DEBOUNCE_MS;
|
|
451
|
+
private _readySettleTimer;
|
|
452
|
+
private _readySettlePending;
|
|
449
453
|
private _processScheduled;
|
|
450
454
|
private static readonly MAX_OUTPUT_BUFFER;
|
|
451
455
|
readonly id: string;
|
|
@@ -527,6 +531,18 @@ declare class PTYSession extends EventEmitter {
|
|
|
527
531
|
* doesn't match the idle prompt, so the agent is still working).
|
|
528
532
|
*/
|
|
529
533
|
private cancelTaskComplete;
|
|
534
|
+
/**
|
|
535
|
+
* Schedule or reset the ready-settle timer.
|
|
536
|
+
* Defers emitting session_ready until output goes quiet for readySettleMs
|
|
537
|
+
* after detectReady first matches. This prevents sending input while
|
|
538
|
+
* TUI agents are still rendering (status bar, shortcuts, update notices).
|
|
539
|
+
*/
|
|
540
|
+
private scheduleReadySettle;
|
|
541
|
+
/**
|
|
542
|
+
* Cancel a pending ready-settle timer (ready indicator disappeared
|
|
543
|
+
* or session status changed).
|
|
544
|
+
*/
|
|
545
|
+
private cancelReadySettle;
|
|
530
546
|
/**
|
|
531
547
|
* Start the PTY session
|
|
532
548
|
*/
|
package/dist/index.js
CHANGED
|
@@ -347,6 +347,9 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
347
347
|
// Task completion detection (idle detection when busy)
|
|
348
348
|
_taskCompleteTimer = null;
|
|
349
349
|
static TASK_COMPLETE_DEBOUNCE_MS = 1500;
|
|
350
|
+
// Ready detection settle delay — defers session_ready until output goes quiet
|
|
351
|
+
_readySettleTimer = null;
|
|
352
|
+
_readySettlePending = false;
|
|
350
353
|
// Deferred output processing — prevents node-pty's synchronous data
|
|
351
354
|
// delivery from starving the event loop (timers, I/O callbacks, etc.)
|
|
352
355
|
_processScheduled = false;
|
|
@@ -638,6 +641,45 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
638
641
|
}
|
|
639
642
|
}
|
|
640
643
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
644
|
+
// Ready Detection Settle Delay
|
|
645
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
646
|
+
/**
|
|
647
|
+
* Schedule or reset the ready-settle timer.
|
|
648
|
+
* Defers emitting session_ready until output goes quiet for readySettleMs
|
|
649
|
+
* after detectReady first matches. This prevents sending input while
|
|
650
|
+
* TUI agents are still rendering (status bar, shortcuts, update notices).
|
|
651
|
+
*/
|
|
652
|
+
scheduleReadySettle() {
|
|
653
|
+
this._readySettlePending = true;
|
|
654
|
+
if (this._readySettleTimer) {
|
|
655
|
+
clearTimeout(this._readySettleTimer);
|
|
656
|
+
}
|
|
657
|
+
const settleMs = this.adapter.readySettleMs ?? 100;
|
|
658
|
+
this._readySettleTimer = setTimeout(() => {
|
|
659
|
+
this._readySettleTimer = null;
|
|
660
|
+
this._readySettlePending = false;
|
|
661
|
+
if (this._status !== "starting" && this._status !== "authenticating") return;
|
|
662
|
+
if (!this.adapter.detectReady(this.outputBuffer)) return;
|
|
663
|
+
this._status = "ready";
|
|
664
|
+
this._lastBlockingPromptHash = null;
|
|
665
|
+
this.outputBuffer = "";
|
|
666
|
+
this.clearStallTimer();
|
|
667
|
+
this.emit("ready");
|
|
668
|
+
this.logger.info({ sessionId: this.id }, "Session ready (after settle)");
|
|
669
|
+
}, settleMs);
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Cancel a pending ready-settle timer (ready indicator disappeared
|
|
673
|
+
* or session status changed).
|
|
674
|
+
*/
|
|
675
|
+
cancelReadySettle() {
|
|
676
|
+
if (this._readySettleTimer) {
|
|
677
|
+
clearTimeout(this._readySettleTimer);
|
|
678
|
+
this._readySettleTimer = null;
|
|
679
|
+
}
|
|
680
|
+
this._readySettlePending = false;
|
|
681
|
+
}
|
|
682
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
641
683
|
// Lifecycle
|
|
642
684
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
643
685
|
/**
|
|
@@ -726,13 +768,16 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
726
768
|
if (this._status === "busy" || this._status === "authenticating") {
|
|
727
769
|
this.resetStallTimer();
|
|
728
770
|
}
|
|
771
|
+
if (this._readySettlePending) {
|
|
772
|
+
if ((this._status === "starting" || this._status === "authenticating") && this.adapter.detectReady(this.outputBuffer)) {
|
|
773
|
+
this.scheduleReadySettle();
|
|
774
|
+
} else {
|
|
775
|
+
this.cancelReadySettle();
|
|
776
|
+
}
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
729
779
|
if ((this._status === "starting" || this._status === "authenticating") && this.adapter.detectReady(this.outputBuffer)) {
|
|
730
|
-
this.
|
|
731
|
-
this._lastBlockingPromptHash = null;
|
|
732
|
-
this.outputBuffer = "";
|
|
733
|
-
this.clearStallTimer();
|
|
734
|
-
this.emit("ready");
|
|
735
|
-
this.logger.info({ sessionId: this.id }, "Session ready");
|
|
780
|
+
this.scheduleReadySettle();
|
|
736
781
|
return;
|
|
737
782
|
}
|
|
738
783
|
if (this._status === "busy" && this.adapter.detectReady(this.outputBuffer)) {
|
|
@@ -1068,6 +1113,7 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
1068
1113
|
this._status = "stopping";
|
|
1069
1114
|
this.clearStallTimer();
|
|
1070
1115
|
this.cancelTaskComplete();
|
|
1116
|
+
this.cancelReadySettle();
|
|
1071
1117
|
this.ptyProcess.kill(signal);
|
|
1072
1118
|
this.logger.info({ sessionId: this.id, signal }, "Killing PTY session");
|
|
1073
1119
|
}
|