pty-manager 1.2.20 → 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;
@@ -277,6 +277,15 @@ var PTYSession = class extends EventEmitter {
277
277
  this.logger = logger || consoleLogger;
278
278
  this._stallDetectionEnabled = stallDetectionEnabled ?? false;
279
279
  this._stallTimeoutMs = config.stallTimeoutMs ?? defaultStallTimeoutMs ?? 8e3;
280
+ if (config.ruleOverrides) {
281
+ for (const [key, value] of Object.entries(config.ruleOverrides)) {
282
+ if (value === null) {
283
+ this._disabledRulePatterns.add(key);
284
+ } else {
285
+ this._ruleOverrides.set(key, value);
286
+ }
287
+ }
288
+ }
280
289
  }
281
290
  ptyProcess = null;
282
291
  outputBuffer = "";
@@ -288,6 +297,8 @@ var PTYSession = class extends EventEmitter {
288
297
  sessionRules = [];
289
298
  _firedOnceRules = /* @__PURE__ */ new Set();
290
299
  _lastBlockingPromptHash = null;
300
+ _ruleOverrides = /* @__PURE__ */ new Map();
301
+ _disabledRulePatterns = /* @__PURE__ */ new Set();
291
302
  // Stall detection
292
303
  _stallTimer = null;
293
304
  _stallTimeoutMs;
@@ -295,6 +306,9 @@ var PTYSession = class extends EventEmitter {
295
306
  _lastStallHash = null;
296
307
  _stallStartedAt = null;
297
308
  _lastContentHash = null;
309
+ // Task completion detection (idle detection when busy)
310
+ _taskCompleteTimer = null;
311
+ static TASK_COMPLETE_DEBOUNCE_MS = 1500;
298
312
  id;
299
313
  config;
300
314
  get status() {
@@ -470,10 +484,12 @@ var PTYSession = class extends EventEmitter {
470
484
  * word boundaries — e.g. "Do\x1b[5Cyou" becomes "Do you", not "Doyou".
471
485
  */
472
486
  stripAnsiForStall(str) {
473
- let result = str.replace(/\x1b\[\d*[CDABG]/g, " ");
487
+ let result = str.replace(/\x1b\[\d*[CDABGdEF]/g, " ");
474
488
  result = result.replace(/\x1b\[\d*(?:;\d+)?[Hf]/g, " ");
489
+ result = result.replace(/\x1b\[\d*[JK]/g, " ");
475
490
  result = result.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
476
- result = result.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷✻✶✳✢⏺←→↑↓]/g, " ");
491
+ result = result.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
492
+ result = result.replace(/[│╭╰╮╯─═╌║╔╗╚╝╠╣╦╩╬┌┐└┘├┤┬┴┼●○❯❮▶◀⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⣾⣽⣻⢿⡿⣟⣯⣷✻✶✳✢⏺←→↑↓⬆⬇◆◇▪▫■□▲△▼▽◈⟨⟩⌘⏎⏏⌫⌦⇧⇪⌥]/g, " ");
477
493
  result = result.replace(/ {2,}/g, " ");
478
494
  return result;
479
495
  }
@@ -510,6 +526,7 @@ var PTYSession = class extends EventEmitter {
510
526
  this.writeRaw(resp + "\r");
511
527
  }
512
528
  this.emit("blocking_prompt", promptInfo, true);
529
+ this.outputBuffer = "";
513
530
  } else {
514
531
  this.emit("blocking_prompt", promptInfo, false);
515
532
  }
@@ -530,6 +547,39 @@ var PTYSession = class extends EventEmitter {
530
547
  }
531
548
  }
532
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
+ // ─────────────────────────────────────────────────────────────────────────────
533
583
  // Lifecycle
534
584
  // ─────────────────────────────────────────────────────────────────────────────
535
585
  /**
@@ -591,10 +641,6 @@ var PTYSession = class extends EventEmitter {
591
641
  this.resetStallTimer();
592
642
  }
593
643
  this.emit("output", data);
594
- const blockingPrompt = this.detectAndHandleBlockingPrompt();
595
- if (blockingPrompt) {
596
- return;
597
- }
598
644
  if ((this._status === "starting" || this._status === "authenticating") && this.adapter.detectReady(this.outputBuffer)) {
599
645
  this._status = "ready";
600
646
  this._lastBlockingPromptHash = null;
@@ -604,6 +650,15 @@ var PTYSession = class extends EventEmitter {
604
650
  this.logger.info({ sessionId: this.id }, "Session ready");
605
651
  return;
606
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
+ }
607
662
  if (this._status !== "ready" && this._status !== "busy") {
608
663
  const loginDetection = this.adapter.detectLogin(this.outputBuffer);
609
664
  if (loginDetection.required && this._status !== "authenticating") {
@@ -679,6 +734,7 @@ var PTYSession = class extends EventEmitter {
679
734
  this.writeRaw(resp + "\r");
680
735
  }
681
736
  this._lastBlockingPromptHash = null;
737
+ this.outputBuffer = "";
682
738
  this.emit("blocking_prompt", promptInfo, true);
683
739
  return true;
684
740
  }
@@ -706,7 +762,10 @@ var PTYSession = class extends EventEmitter {
706
762
  * Session rules are checked first, then adapter rules.
707
763
  */
708
764
  tryAutoResponse() {
709
- const adapterRules = this.adapter.autoResponseRules || [];
765
+ const adapterRules = (this.adapter.autoResponseRules || []).filter((r) => !this._disabledRulePatterns.has(r.pattern.source)).map((r) => {
766
+ const override = this._ruleOverrides.get(r.pattern.source);
767
+ return override ? { ...r, ...override } : r;
768
+ });
710
769
  const allRules = [...this.sessionRules, ...adapterRules];
711
770
  if (allRules.length === 0) {
712
771
  return false;
@@ -822,6 +881,7 @@ var PTYSession = class extends EventEmitter {
822
881
  */
823
882
  send(message) {
824
883
  this._status = "busy";
884
+ this.emit("status_changed", "busy");
825
885
  this.resetStallTimer();
826
886
  const msg = {
827
887
  id: `${this.id}-msg-${++this.messageCounter}`,
@@ -932,6 +992,7 @@ var PTYSession = class extends EventEmitter {
932
992
  if (this.ptyProcess) {
933
993
  this._status = "stopping";
934
994
  this.clearStallTimer();
995
+ this.cancelTaskComplete();
935
996
  this.ptyProcess.kill(signal);
936
997
  this.logger.info({ sessionId: this.id, signal }, "Killing PTY session");
937
998
  }
@@ -1085,6 +1146,12 @@ var PTYManager = class extends EventEmitter2 {
1085
1146
  session.on("error", (error) => {
1086
1147
  this.emit("session_error", session.toHandle(), error.message);
1087
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
+ });
1088
1155
  session.on("stall_detected", (recentOutput, stallDurationMs) => {
1089
1156
  const handle = session.toHandle();
1090
1157
  this.emit("stall_detected", handle, recentOutput, stallDurationMs);
@@ -1938,6 +2005,24 @@ var BunCompatiblePTYManager = class extends EventEmitter3 {
1938
2005
  }
1939
2006
  break;
1940
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
+ }
1941
2026
  case "stall_detected": {
1942
2027
  const session = this.sessions.get(id);
1943
2028
  if (session) {