pty-manager 1.2.21 → 1.2.22
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/dist/index.d.mts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +76 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +76 -7
- package/dist/index.mjs.map +1 -1
- package/dist/pty-worker.js +71 -7
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -405,6 +405,8 @@ interface PTYSessionEvents {
|
|
|
405
405
|
exit: (code: number) => void;
|
|
406
406
|
error: (error: Error) => void;
|
|
407
407
|
stall_detected: (recentOutput: string, stallDurationMs: number) => void;
|
|
408
|
+
status_changed: (status: SessionStatus) => void;
|
|
409
|
+
task_complete: () => void;
|
|
408
410
|
}
|
|
409
411
|
/**
|
|
410
412
|
* Special key mappings to escape sequences
|
|
@@ -433,6 +435,8 @@ declare class PTYSession extends EventEmitter {
|
|
|
433
435
|
private _lastStallHash;
|
|
434
436
|
private _stallStartedAt;
|
|
435
437
|
private _lastContentHash;
|
|
438
|
+
private _taskCompleteTimer;
|
|
439
|
+
private static readonly TASK_COMPLETE_DEBOUNCE_MS;
|
|
436
440
|
readonly id: string;
|
|
437
441
|
readonly config: SpawnConfig;
|
|
438
442
|
constructor(adapter: CLIAdapter, config: SpawnConfig, logger?: Logger, stallDetectionEnabled?: boolean, defaultStallTimeoutMs?: number);
|
|
@@ -501,6 +505,17 @@ declare class PTYSession extends EventEmitter {
|
|
|
501
505
|
* Called by the manager after onStallClassify resolves.
|
|
502
506
|
*/
|
|
503
507
|
handleStallClassification(classification: StallClassification | null): void;
|
|
508
|
+
/**
|
|
509
|
+
* Schedule a task_complete transition after a debounce period.
|
|
510
|
+
* If new non-whitespace output arrives before the timer fires,
|
|
511
|
+
* the timer is cancelled (by cancelTaskComplete in onData).
|
|
512
|
+
*/
|
|
513
|
+
private scheduleTaskComplete;
|
|
514
|
+
/**
|
|
515
|
+
* Cancel a pending task_complete timer (new output arrived that
|
|
516
|
+
* doesn't match the idle prompt, so the agent is still working).
|
|
517
|
+
*/
|
|
518
|
+
private cancelTaskComplete;
|
|
504
519
|
/**
|
|
505
520
|
* Start the PTY session
|
|
506
521
|
*/
|
|
@@ -612,6 +627,8 @@ interface PTYManagerEvents {
|
|
|
612
627
|
message: (message: SessionMessage) => void;
|
|
613
628
|
question: (session: SessionHandle, question: string) => void;
|
|
614
629
|
stall_detected: (session: SessionHandle, recentOutput: string, stallDurationMs: number) => void;
|
|
630
|
+
session_status_changed: (session: SessionHandle) => void;
|
|
631
|
+
task_complete: (session: SessionHandle) => void;
|
|
615
632
|
}
|
|
616
633
|
declare class PTYManager extends EventEmitter {
|
|
617
634
|
private sessions;
|
package/dist/index.d.ts
CHANGED
|
@@ -405,6 +405,8 @@ interface PTYSessionEvents {
|
|
|
405
405
|
exit: (code: number) => void;
|
|
406
406
|
error: (error: Error) => void;
|
|
407
407
|
stall_detected: (recentOutput: string, stallDurationMs: number) => void;
|
|
408
|
+
status_changed: (status: SessionStatus) => void;
|
|
409
|
+
task_complete: () => void;
|
|
408
410
|
}
|
|
409
411
|
/**
|
|
410
412
|
* Special key mappings to escape sequences
|
|
@@ -433,6 +435,8 @@ declare class PTYSession extends EventEmitter {
|
|
|
433
435
|
private _lastStallHash;
|
|
434
436
|
private _stallStartedAt;
|
|
435
437
|
private _lastContentHash;
|
|
438
|
+
private _taskCompleteTimer;
|
|
439
|
+
private static readonly TASK_COMPLETE_DEBOUNCE_MS;
|
|
436
440
|
readonly id: string;
|
|
437
441
|
readonly config: SpawnConfig;
|
|
438
442
|
constructor(adapter: CLIAdapter, config: SpawnConfig, logger?: Logger, stallDetectionEnabled?: boolean, defaultStallTimeoutMs?: number);
|
|
@@ -501,6 +505,17 @@ declare class PTYSession extends EventEmitter {
|
|
|
501
505
|
* Called by the manager after onStallClassify resolves.
|
|
502
506
|
*/
|
|
503
507
|
handleStallClassification(classification: StallClassification | null): void;
|
|
508
|
+
/**
|
|
509
|
+
* Schedule a task_complete transition after a debounce period.
|
|
510
|
+
* If new non-whitespace output arrives before the timer fires,
|
|
511
|
+
* the timer is cancelled (by cancelTaskComplete in onData).
|
|
512
|
+
*/
|
|
513
|
+
private scheduleTaskComplete;
|
|
514
|
+
/**
|
|
515
|
+
* Cancel a pending task_complete timer (new output arrived that
|
|
516
|
+
* doesn't match the idle prompt, so the agent is still working).
|
|
517
|
+
*/
|
|
518
|
+
private cancelTaskComplete;
|
|
504
519
|
/**
|
|
505
520
|
* Start the PTY session
|
|
506
521
|
*/
|
|
@@ -612,6 +627,8 @@ interface PTYManagerEvents {
|
|
|
612
627
|
message: (message: SessionMessage) => void;
|
|
613
628
|
question: (session: SessionHandle, question: string) => void;
|
|
614
629
|
stall_detected: (session: SessionHandle, recentOutput: string, stallDurationMs: number) => void;
|
|
630
|
+
session_status_changed: (session: SessionHandle) => void;
|
|
631
|
+
task_complete: (session: SessionHandle) => void;
|
|
615
632
|
}
|
|
616
633
|
declare class PTYManager extends EventEmitter {
|
|
617
634
|
private sessions;
|
package/dist/index.js
CHANGED
|
@@ -306,7 +306,7 @@ var SPECIAL_KEYS = {
|
|
|
306
306
|
};
|
|
307
307
|
var BRACKETED_PASTE_START = "\x1B[200~";
|
|
308
308
|
var BRACKETED_PASTE_END = "\x1B[201~";
|
|
309
|
-
var PTYSession = class extends import_events.EventEmitter {
|
|
309
|
+
var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
310
310
|
constructor(adapter, config, logger, stallDetectionEnabled, defaultStallTimeoutMs) {
|
|
311
311
|
super();
|
|
312
312
|
this.adapter = adapter;
|
|
@@ -344,6 +344,9 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
344
344
|
_lastStallHash = null;
|
|
345
345
|
_stallStartedAt = null;
|
|
346
346
|
_lastContentHash = null;
|
|
347
|
+
// Task completion detection (idle detection when busy)
|
|
348
|
+
_taskCompleteTimer = null;
|
|
349
|
+
static TASK_COMPLETE_DEBOUNCE_MS = 1500;
|
|
347
350
|
id;
|
|
348
351
|
config;
|
|
349
352
|
get status() {
|
|
@@ -519,10 +522,12 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
519
522
|
* word boundaries — e.g. "Do\x1b[5Cyou" becomes "Do you", not "Doyou".
|
|
520
523
|
*/
|
|
521
524
|
stripAnsiForStall(str) {
|
|
522
|
-
let result = str.replace(/\x1b\[\d*[
|
|
525
|
+
let result = str.replace(/\x1b\[\d*[CDABGdEF]/g, " ");
|
|
523
526
|
result = result.replace(/\x1b\[\d*(?:;\d+)?[Hf]/g, " ");
|
|
527
|
+
result = result.replace(/\x1b\[\d*[JK]/g, " ");
|
|
524
528
|
result = result.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
525
|
-
result = result.replace(/[
|
|
529
|
+
result = result.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
530
|
+
result = result.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷✻✶✳✢⏺←→↑↓⬆⬇◆◇▪▫■□▲△▼▽◈⟨⟩⌘⏎⏏⌫⌦⇧⇪⌥]/g, " ");
|
|
526
531
|
result = result.replace(/ {2,}/g, " ");
|
|
527
532
|
return result;
|
|
528
533
|
}
|
|
@@ -580,6 +585,39 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
580
585
|
}
|
|
581
586
|
}
|
|
582
587
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
588
|
+
// Task Completion Detection
|
|
589
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
590
|
+
/**
|
|
591
|
+
* Schedule a task_complete transition after a debounce period.
|
|
592
|
+
* If new non-whitespace output arrives before the timer fires,
|
|
593
|
+
* the timer is cancelled (by cancelTaskComplete in onData).
|
|
594
|
+
*/
|
|
595
|
+
scheduleTaskComplete() {
|
|
596
|
+
if (this._taskCompleteTimer) return;
|
|
597
|
+
this._taskCompleteTimer = setTimeout(() => {
|
|
598
|
+
this._taskCompleteTimer = null;
|
|
599
|
+
if (this._status !== "busy") return;
|
|
600
|
+
if (!this.adapter.detectReady(this.outputBuffer)) return;
|
|
601
|
+
this._status = "ready";
|
|
602
|
+
this._lastBlockingPromptHash = null;
|
|
603
|
+
this.outputBuffer = "";
|
|
604
|
+
this.clearStallTimer();
|
|
605
|
+
this.emit("status_changed", "ready");
|
|
606
|
+
this.emit("task_complete");
|
|
607
|
+
this.logger.info({ sessionId: this.id }, "Task complete \u2014 agent returned to idle prompt");
|
|
608
|
+
}, _PTYSession.TASK_COMPLETE_DEBOUNCE_MS);
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Cancel a pending task_complete timer (new output arrived that
|
|
612
|
+
* doesn't match the idle prompt, so the agent is still working).
|
|
613
|
+
*/
|
|
614
|
+
cancelTaskComplete() {
|
|
615
|
+
if (this._taskCompleteTimer) {
|
|
616
|
+
clearTimeout(this._taskCompleteTimer);
|
|
617
|
+
this._taskCompleteTimer = null;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
583
621
|
// Lifecycle
|
|
584
622
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
585
623
|
/**
|
|
@@ -641,10 +679,6 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
641
679
|
this.resetStallTimer();
|
|
642
680
|
}
|
|
643
681
|
this.emit("output", data);
|
|
644
|
-
const blockingPrompt = this.detectAndHandleBlockingPrompt();
|
|
645
|
-
if (blockingPrompt) {
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
682
|
if ((this._status === "starting" || this._status === "authenticating") && this.adapter.detectReady(this.outputBuffer)) {
|
|
649
683
|
this._status = "ready";
|
|
650
684
|
this._lastBlockingPromptHash = null;
|
|
@@ -654,6 +688,15 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
654
688
|
this.logger.info({ sessionId: this.id }, "Session ready");
|
|
655
689
|
return;
|
|
656
690
|
}
|
|
691
|
+
if (this._status === "busy" && this.adapter.detectReady(this.outputBuffer)) {
|
|
692
|
+
this.scheduleTaskComplete();
|
|
693
|
+
} else {
|
|
694
|
+
this.cancelTaskComplete();
|
|
695
|
+
}
|
|
696
|
+
const blockingPrompt = this.detectAndHandleBlockingPrompt();
|
|
697
|
+
if (blockingPrompt) {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
657
700
|
if (this._status !== "ready" && this._status !== "busy") {
|
|
658
701
|
const loginDetection = this.adapter.detectLogin(this.outputBuffer);
|
|
659
702
|
if (loginDetection.required && this._status !== "authenticating") {
|
|
@@ -876,6 +919,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
876
919
|
*/
|
|
877
920
|
send(message) {
|
|
878
921
|
this._status = "busy";
|
|
922
|
+
this.emit("status_changed", "busy");
|
|
879
923
|
this.resetStallTimer();
|
|
880
924
|
const msg = {
|
|
881
925
|
id: `${this.id}-msg-${++this.messageCounter}`,
|
|
@@ -986,6 +1030,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
986
1030
|
if (this.ptyProcess) {
|
|
987
1031
|
this._status = "stopping";
|
|
988
1032
|
this.clearStallTimer();
|
|
1033
|
+
this.cancelTaskComplete();
|
|
989
1034
|
this.ptyProcess.kill(signal);
|
|
990
1035
|
this.logger.info({ sessionId: this.id, signal }, "Killing PTY session");
|
|
991
1036
|
}
|
|
@@ -1139,6 +1184,12 @@ var PTYManager = class extends import_events2.EventEmitter {
|
|
|
1139
1184
|
session.on("error", (error) => {
|
|
1140
1185
|
this.emit("session_error", session.toHandle(), error.message);
|
|
1141
1186
|
});
|
|
1187
|
+
session.on("status_changed", () => {
|
|
1188
|
+
this.emit("session_status_changed", session.toHandle());
|
|
1189
|
+
});
|
|
1190
|
+
session.on("task_complete", () => {
|
|
1191
|
+
this.emit("task_complete", session.toHandle());
|
|
1192
|
+
});
|
|
1142
1193
|
session.on("stall_detected", (recentOutput, stallDurationMs) => {
|
|
1143
1194
|
const handle = session.toHandle();
|
|
1144
1195
|
this.emit("stall_detected", handle, recentOutput, stallDurationMs);
|
|
@@ -1992,6 +2043,24 @@ var BunCompatiblePTYManager = class extends import_events3.EventEmitter {
|
|
|
1992
2043
|
}
|
|
1993
2044
|
break;
|
|
1994
2045
|
}
|
|
2046
|
+
case "status_changed": {
|
|
2047
|
+
const session = this.sessions.get(id);
|
|
2048
|
+
if (session) {
|
|
2049
|
+
session.status = event.status;
|
|
2050
|
+
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
2051
|
+
this.emit("session_status_changed", session);
|
|
2052
|
+
}
|
|
2053
|
+
break;
|
|
2054
|
+
}
|
|
2055
|
+
case "task_complete": {
|
|
2056
|
+
const session = this.sessions.get(id);
|
|
2057
|
+
if (session) {
|
|
2058
|
+
session.status = "ready";
|
|
2059
|
+
session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
2060
|
+
this.emit("task_complete", session);
|
|
2061
|
+
}
|
|
2062
|
+
break;
|
|
2063
|
+
}
|
|
1995
2064
|
case "stall_detected": {
|
|
1996
2065
|
const session = this.sessions.get(id);
|
|
1997
2066
|
if (session) {
|