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.mjs CHANGED
@@ -268,7 +268,7 @@ var SPECIAL_KEYS = {
268
268
  };
269
269
  var BRACKETED_PASTE_START = "\x1B[200~";
270
270
  var BRACKETED_PASTE_END = "\x1B[201~";
271
- var PTYSession = class extends EventEmitter {
271
+ var PTYSession = class _PTYSession extends EventEmitter {
272
272
  constructor(adapter, config, logger, stallDetectionEnabled, defaultStallTimeoutMs) {
273
273
  super();
274
274
  this.adapter = adapter;
@@ -306,6 +306,9 @@ var PTYSession = class extends EventEmitter {
306
306
  _lastStallHash = null;
307
307
  _stallStartedAt = null;
308
308
  _lastContentHash = null;
309
+ // Task completion detection (idle detection when busy)
310
+ _taskCompleteTimer = null;
311
+ static TASK_COMPLETE_DEBOUNCE_MS = 1500;
309
312
  id;
310
313
  config;
311
314
  get status() {
@@ -481,10 +484,12 @@ var PTYSession = class extends EventEmitter {
481
484
  * word boundaries — e.g. "Do\x1b[5Cyou" becomes "Do you", not "Doyou".
482
485
  */
483
486
  stripAnsiForStall(str) {
484
- let result = str.replace(/\x1b\[\d*[CDABG]/g, " ");
487
+ let result = str.replace(/\x1b\[\d*[CDABGdEF]/g, " ");
485
488
  result = result.replace(/\x1b\[\d*(?:;\d+)?[Hf]/g, " ");
489
+ result = result.replace(/\x1b\[\d*[JK]/g, " ");
486
490
  result = result.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
487
- result = result.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷✻✶✳✢⏺←→↑↓]/g, " ");
491
+ result = result.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
492
+ result = result.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷✻✶✳✢⏺←→↑↓⬆⬇◆◇▪▫■□▲△▼▽◈⟨⟩⌘⏎⏏⌫⌦⇧⇪⌥]/g, " ");
488
493
  result = result.replace(/ {2,}/g, " ");
489
494
  return result;
490
495
  }
@@ -542,6 +547,39 @@ var PTYSession = class extends EventEmitter {
542
547
  }
543
548
  }
544
549
  // ─────────────────────────────────────────────────────────────────────────────
550
+ // Task Completion Detection
551
+ // ─────────────────────────────────────────────────────────────────────────────
552
+ /**
553
+ * Schedule a task_complete transition after a debounce period.
554
+ * If new non-whitespace output arrives before the timer fires,
555
+ * the timer is cancelled (by cancelTaskComplete in onData).
556
+ */
557
+ scheduleTaskComplete() {
558
+ if (this._taskCompleteTimer) return;
559
+ this._taskCompleteTimer = setTimeout(() => {
560
+ this._taskCompleteTimer = null;
561
+ if (this._status !== "busy") return;
562
+ if (!this.adapter.detectReady(this.outputBuffer)) return;
563
+ this._status = "ready";
564
+ this._lastBlockingPromptHash = null;
565
+ this.outputBuffer = "";
566
+ this.clearStallTimer();
567
+ this.emit("status_changed", "ready");
568
+ this.emit("task_complete");
569
+ this.logger.info({ sessionId: this.id }, "Task complete \u2014 agent returned to idle prompt");
570
+ }, _PTYSession.TASK_COMPLETE_DEBOUNCE_MS);
571
+ }
572
+ /**
573
+ * Cancel a pending task_complete timer (new output arrived that
574
+ * doesn't match the idle prompt, so the agent is still working).
575
+ */
576
+ cancelTaskComplete() {
577
+ if (this._taskCompleteTimer) {
578
+ clearTimeout(this._taskCompleteTimer);
579
+ this._taskCompleteTimer = null;
580
+ }
581
+ }
582
+ // ─────────────────────────────────────────────────────────────────────────────
545
583
  // Lifecycle
546
584
  // ─────────────────────────────────────────────────────────────────────────────
547
585
  /**
@@ -603,10 +641,6 @@ var PTYSession = class extends EventEmitter {
603
641
  this.resetStallTimer();
604
642
  }
605
643
  this.emit("output", data);
606
- const blockingPrompt = this.detectAndHandleBlockingPrompt();
607
- if (blockingPrompt) {
608
- return;
609
- }
610
644
  if ((this._status === "starting" || this._status === "authenticating") && this.adapter.detectReady(this.outputBuffer)) {
611
645
  this._status = "ready";
612
646
  this._lastBlockingPromptHash = null;
@@ -616,6 +650,15 @@ var PTYSession = class extends EventEmitter {
616
650
  this.logger.info({ sessionId: this.id }, "Session ready");
617
651
  return;
618
652
  }
653
+ if (this._status === "busy" && this.adapter.detectReady(this.outputBuffer)) {
654
+ this.scheduleTaskComplete();
655
+ } else {
656
+ this.cancelTaskComplete();
657
+ }
658
+ const blockingPrompt = this.detectAndHandleBlockingPrompt();
659
+ if (blockingPrompt) {
660
+ return;
661
+ }
619
662
  if (this._status !== "ready" && this._status !== "busy") {
620
663
  const loginDetection = this.adapter.detectLogin(this.outputBuffer);
621
664
  if (loginDetection.required && this._status !== "authenticating") {
@@ -838,6 +881,7 @@ var PTYSession = class extends EventEmitter {
838
881
  */
839
882
  send(message) {
840
883
  this._status = "busy";
884
+ this.emit("status_changed", "busy");
841
885
  this.resetStallTimer();
842
886
  const msg = {
843
887
  id: `${this.id}-msg-${++this.messageCounter}`,
@@ -948,6 +992,7 @@ var PTYSession = class extends EventEmitter {
948
992
  if (this.ptyProcess) {
949
993
  this._status = "stopping";
950
994
  this.clearStallTimer();
995
+ this.cancelTaskComplete();
951
996
  this.ptyProcess.kill(signal);
952
997
  this.logger.info({ sessionId: this.id, signal }, "Killing PTY session");
953
998
  }
@@ -1101,6 +1146,12 @@ var PTYManager = class extends EventEmitter2 {
1101
1146
  session.on("error", (error) => {
1102
1147
  this.emit("session_error", session.toHandle(), error.message);
1103
1148
  });
1149
+ session.on("status_changed", () => {
1150
+ this.emit("session_status_changed", session.toHandle());
1151
+ });
1152
+ session.on("task_complete", () => {
1153
+ this.emit("task_complete", session.toHandle());
1154
+ });
1104
1155
  session.on("stall_detected", (recentOutput, stallDurationMs) => {
1105
1156
  const handle = session.toHandle();
1106
1157
  this.emit("stall_detected", handle, recentOutput, stallDurationMs);
@@ -1954,6 +2005,24 @@ var BunCompatiblePTYManager = class extends EventEmitter3 {
1954
2005
  }
1955
2006
  break;
1956
2007
  }
2008
+ case "status_changed": {
2009
+ const session = this.sessions.get(id);
2010
+ if (session) {
2011
+ session.status = event.status;
2012
+ session.lastActivityAt = /* @__PURE__ */ new Date();
2013
+ this.emit("session_status_changed", session);
2014
+ }
2015
+ break;
2016
+ }
2017
+ case "task_complete": {
2018
+ const session = this.sessions.get(id);
2019
+ if (session) {
2020
+ session.status = "ready";
2021
+ session.lastActivityAt = /* @__PURE__ */ new Date();
2022
+ this.emit("task_complete", session);
2023
+ }
2024
+ break;
2025
+ }
1957
2026
  case "stall_detected": {
1958
2027
  const session = this.sessions.get(id);
1959
2028
  if (session) {