pty-manager 1.2.21 → 1.3.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 +36 -2
- package/dist/index.d.mts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +97 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +97 -7
- package/dist/index.mjs.map +1 -1
- package/dist/pty-worker.js +84 -7
- package/package.json +1 -1
package/dist/pty-worker.js
CHANGED
|
@@ -289,7 +289,7 @@ var SPECIAL_KEYS = {
|
|
|
289
289
|
};
|
|
290
290
|
var BRACKETED_PASTE_START = "\x1B[200~";
|
|
291
291
|
var BRACKETED_PASTE_END = "\x1B[201~";
|
|
292
|
-
var PTYSession = class extends import_events.EventEmitter {
|
|
292
|
+
var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
293
293
|
constructor(adapter, config, logger, stallDetectionEnabled, defaultStallTimeoutMs) {
|
|
294
294
|
super();
|
|
295
295
|
this.adapter = adapter;
|
|
@@ -327,6 +327,9 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
327
327
|
_lastStallHash = null;
|
|
328
328
|
_stallStartedAt = null;
|
|
329
329
|
_lastContentHash = null;
|
|
330
|
+
// Task completion detection (idle detection when busy)
|
|
331
|
+
_taskCompleteTimer = null;
|
|
332
|
+
static TASK_COMPLETE_DEBOUNCE_MS = 1500;
|
|
330
333
|
id;
|
|
331
334
|
config;
|
|
332
335
|
get status() {
|
|
@@ -466,6 +469,19 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
466
469
|
return;
|
|
467
470
|
}
|
|
468
471
|
this._lastStallHash = hash;
|
|
472
|
+
if (this._status === "busy" && this.adapter.detectTaskComplete?.(this.outputBuffer)) {
|
|
473
|
+
this._status = "ready";
|
|
474
|
+
this._lastBlockingPromptHash = null;
|
|
475
|
+
this.outputBuffer = "";
|
|
476
|
+
this.clearStallTimer();
|
|
477
|
+
this.emit("status_changed", "ready");
|
|
478
|
+
this.emit("task_complete");
|
|
479
|
+
this.logger.info(
|
|
480
|
+
{ sessionId: this.id },
|
|
481
|
+
"Task complete (adapter fast-path) \u2014 agent returned to idle prompt"
|
|
482
|
+
);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
469
485
|
const recentRaw = this.outputBuffer.slice(-2e3);
|
|
470
486
|
const recentOutput = this.stripAnsiForStall(recentRaw);
|
|
471
487
|
const stallDurationMs = this._stallStartedAt ? Date.now() - this._stallStartedAt : this._stallTimeoutMs;
|
|
@@ -502,10 +518,12 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
502
518
|
* word boundaries — e.g. "Do\x1b[5Cyou" becomes "Do you", not "Doyou".
|
|
503
519
|
*/
|
|
504
520
|
stripAnsiForStall(str) {
|
|
505
|
-
let result = str.replace(/\x1b\[\d*[
|
|
521
|
+
let result = str.replace(/\x1b\[\d*[CDABGdEF]/g, " ");
|
|
506
522
|
result = result.replace(/\x1b\[\d*(?:;\d+)?[Hf]/g, " ");
|
|
523
|
+
result = result.replace(/\x1b\[\d*[JK]/g, " ");
|
|
507
524
|
result = result.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
508
|
-
result = result.replace(/[
|
|
525
|
+
result = result.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
526
|
+
result = result.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷✻✶✳✢⏺←→↑↓⬆⬇◆◇▪▫■□▲△▼▽◈⟨⟩⌘⏎⏏⌫⌦⇧⇪⌥]/g, " ");
|
|
509
527
|
result = result.replace(/ {2,}/g, " ");
|
|
510
528
|
return result;
|
|
511
529
|
}
|
|
@@ -563,6 +581,39 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
563
581
|
}
|
|
564
582
|
}
|
|
565
583
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
584
|
+
// Task Completion Detection
|
|
585
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
586
|
+
/**
|
|
587
|
+
* Schedule a task_complete transition after a debounce period.
|
|
588
|
+
* If new non-whitespace output arrives before the timer fires,
|
|
589
|
+
* the timer is cancelled (by cancelTaskComplete in onData).
|
|
590
|
+
*/
|
|
591
|
+
scheduleTaskComplete() {
|
|
592
|
+
if (this._taskCompleteTimer) return;
|
|
593
|
+
this._taskCompleteTimer = setTimeout(() => {
|
|
594
|
+
this._taskCompleteTimer = null;
|
|
595
|
+
if (this._status !== "busy") return;
|
|
596
|
+
if (!this.adapter.detectReady(this.outputBuffer)) return;
|
|
597
|
+
this._status = "ready";
|
|
598
|
+
this._lastBlockingPromptHash = null;
|
|
599
|
+
this.outputBuffer = "";
|
|
600
|
+
this.clearStallTimer();
|
|
601
|
+
this.emit("status_changed", "ready");
|
|
602
|
+
this.emit("task_complete");
|
|
603
|
+
this.logger.info({ sessionId: this.id }, "Task complete \u2014 agent returned to idle prompt");
|
|
604
|
+
}, _PTYSession.TASK_COMPLETE_DEBOUNCE_MS);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Cancel a pending task_complete timer (new output arrived that
|
|
608
|
+
* doesn't match the idle prompt, so the agent is still working).
|
|
609
|
+
*/
|
|
610
|
+
cancelTaskComplete() {
|
|
611
|
+
if (this._taskCompleteTimer) {
|
|
612
|
+
clearTimeout(this._taskCompleteTimer);
|
|
613
|
+
this._taskCompleteTimer = null;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
566
617
|
// Lifecycle
|
|
567
618
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
568
619
|
/**
|
|
@@ -624,10 +675,6 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
624
675
|
this.resetStallTimer();
|
|
625
676
|
}
|
|
626
677
|
this.emit("output", data);
|
|
627
|
-
const blockingPrompt = this.detectAndHandleBlockingPrompt();
|
|
628
|
-
if (blockingPrompt) {
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
678
|
if ((this._status === "starting" || this._status === "authenticating") && this.adapter.detectReady(this.outputBuffer)) {
|
|
632
679
|
this._status = "ready";
|
|
633
680
|
this._lastBlockingPromptHash = null;
|
|
@@ -637,6 +684,15 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
637
684
|
this.logger.info({ sessionId: this.id }, "Session ready");
|
|
638
685
|
return;
|
|
639
686
|
}
|
|
687
|
+
if (this._status === "busy" && this.adapter.detectReady(this.outputBuffer)) {
|
|
688
|
+
this.scheduleTaskComplete();
|
|
689
|
+
} else {
|
|
690
|
+
this.cancelTaskComplete();
|
|
691
|
+
}
|
|
692
|
+
const blockingPrompt = this.detectAndHandleBlockingPrompt();
|
|
693
|
+
if (blockingPrompt) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
640
696
|
if (this._status !== "ready" && this._status !== "busy") {
|
|
641
697
|
const loginDetection = this.adapter.detectLogin(this.outputBuffer);
|
|
642
698
|
if (loginDetection.required && this._status !== "authenticating") {
|
|
@@ -859,6 +915,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
859
915
|
*/
|
|
860
916
|
send(message) {
|
|
861
917
|
this._status = "busy";
|
|
918
|
+
this.emit("status_changed", "busy");
|
|
862
919
|
this.resetStallTimer();
|
|
863
920
|
const msg = {
|
|
864
921
|
id: `${this.id}-msg-${++this.messageCounter}`,
|
|
@@ -969,6 +1026,7 @@ var PTYSession = class extends import_events.EventEmitter {
|
|
|
969
1026
|
if (this.ptyProcess) {
|
|
970
1027
|
this._status = "stopping";
|
|
971
1028
|
this.clearStallTimer();
|
|
1029
|
+
this.cancelTaskComplete();
|
|
972
1030
|
this.ptyProcess.kill(signal);
|
|
973
1031
|
this.logger.info({ sessionId: this.id, signal }, "Killing PTY session");
|
|
974
1032
|
}
|
|
@@ -1122,6 +1180,12 @@ var PTYManager = class extends import_events2.EventEmitter {
|
|
|
1122
1180
|
session.on("error", (error) => {
|
|
1123
1181
|
this.emit("session_error", session.toHandle(), error.message);
|
|
1124
1182
|
});
|
|
1183
|
+
session.on("status_changed", () => {
|
|
1184
|
+
this.emit("session_status_changed", session.toHandle());
|
|
1185
|
+
});
|
|
1186
|
+
session.on("task_complete", () => {
|
|
1187
|
+
this.emit("task_complete", session.toHandle());
|
|
1188
|
+
});
|
|
1125
1189
|
session.on("stall_detected", (recentOutput, stallDurationMs) => {
|
|
1126
1190
|
const handle = session.toHandle();
|
|
1127
1191
|
this.emit("stall_detected", handle, recentOutput, stallDurationMs);
|
|
@@ -1512,6 +1576,19 @@ manager.on("question", (handle, question) => {
|
|
|
1512
1576
|
question
|
|
1513
1577
|
});
|
|
1514
1578
|
});
|
|
1579
|
+
manager.on("session_status_changed", (handle) => {
|
|
1580
|
+
emit({
|
|
1581
|
+
event: "status_changed",
|
|
1582
|
+
id: handle.id,
|
|
1583
|
+
status: handle.status
|
|
1584
|
+
});
|
|
1585
|
+
});
|
|
1586
|
+
manager.on("task_complete", (handle) => {
|
|
1587
|
+
emit({
|
|
1588
|
+
event: "task_complete",
|
|
1589
|
+
id: handle.id
|
|
1590
|
+
});
|
|
1591
|
+
});
|
|
1515
1592
|
manager.on("stall_detected", (handle, recentOutput, stallDurationMs) => {
|
|
1516
1593
|
emit({
|
|
1517
1594
|
event: "stall_detected",
|
package/package.json
CHANGED