pty-manager 1.2.22 → 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 CHANGED
@@ -10,6 +10,7 @@ PTY session manager with lifecycle management, pluggable adapters, and blocking
10
10
  - **Auto-response rules** - Automatically respond to known prompts with text or key sequences
11
11
  - **TUI menu navigation** - Navigate arrow-key menus via `selectMenuOption()` and key-sequence rules
12
12
  - **Stall detection** - Content-based stall detection with pluggable external classifiers
13
+ - **Task completion detection** - Adapter-level fast-path that short-circuits the LLM stall classifier when the CLI returns to its idle prompt
13
14
  - **Terminal attachment** - Attach to sessions for raw I/O streaming
14
15
  - **Special key support** - Send Ctrl, Alt, Shift, and function key combinations via `sendKeys()`
15
16
  - **Bracketed paste** - Proper paste handling with bracketed paste mode support
@@ -130,6 +131,11 @@ class MyCLIAdapter extends BaseCLIAdapter {
130
131
  return /ready>/.test(output);
131
132
  }
132
133
 
134
+ // Optional: high-confidence task completion detection
135
+ detectTaskComplete(output) {
136
+ return /done in \d+s/.test(output) && /ready>/.test(output);
137
+ }
138
+
133
139
  parseOutput(output) {
134
140
  return {
135
141
  type: 'response',
@@ -189,6 +195,8 @@ class PTYManager extends EventEmitter {
189
195
  | `blocking_prompt` | `SessionHandle, promptInfo, autoResponded` | Prompt detected |
190
196
  | `message` | `SessionMessage` | Parsed message received |
191
197
  | `question` | `SessionHandle, question` | Question detected |
198
+ | `stall_detected` | `SessionHandle, recentOutput, stallDurationMs` | Output stalled, needs classification |
199
+ | `task_complete` | `SessionHandle` | Agent finished task, returned to idle |
192
200
 
193
201
  ### SpawnConfig
194
202
 
@@ -435,9 +443,11 @@ Adapters can declare `usesTuiMenus: true` to indicate they use arrow-key menus i
435
443
  await session.selectMenuOption(2); // Sends Down, Down, Enter with 50ms delays
436
444
  ```
437
445
 
438
- ## Stall Detection
446
+ ## Stall Detection & Task Completion
439
447
 
440
- Content-based stall detection monitors sessions for output that stops changing. When a stall is detected, the session emits a `stall_detected` event with the buffered output for external classification.
448
+ Content-based stall detection monitors sessions for output that stops changing. When a stall is detected, the session first tries the adapter's `detectTaskComplete()` fast-path. If the adapter recognizes the output as a completed task (e.g. duration summary + idle prompt), it transitions directly to `ready` and emits `task_complete` — skipping the expensive LLM stall classifier entirely.
449
+
450
+ If the adapter doesn't recognize the output, the session falls back to emitting `stall_detected` for external classification.
441
451
 
442
452
  ```typescript
443
453
  // Enable stall detection with a pluggable classifier
@@ -460,6 +470,30 @@ const session = await manager.spawn({
460
470
  });
461
471
  ```
462
472
 
473
+ ### Adapter-Level Task Completion (Fast Path)
474
+
475
+ Adapters can implement `detectTaskComplete(output)` to recognize high-confidence completion patterns specific to their CLI. This avoids the latency and cost of an LLM classifier call.
476
+
477
+ ```typescript
478
+ class MyCLIAdapter extends BaseCLIAdapter {
479
+ // ...
480
+
481
+ detectTaskComplete(output: string): boolean {
482
+ // Match CLI-specific patterns that indicate work is done
483
+ return /completed in \d+s/.test(output) && /my-cli>/.test(output);
484
+ }
485
+ }
486
+ ```
487
+
488
+ The default `BaseCLIAdapter` implementation delegates to `detectReady()`. Coding agent adapters override this with CLI-specific patterns:
489
+
490
+ | Adapter | Completion Indicators |
491
+ |---------|----------------------|
492
+ | Claude Code | Turn duration (`Cooked for 3m 12s`) + `❯` prompt |
493
+ | Gemini CLI | `◇ Ready` window title, `Type your message` composer |
494
+ | Codex | `Worked for 1m 05s` separator + `›` prompt |
495
+ | Aider | `Aider is waiting for your input`, mode prompts with edit markers |
496
+
463
497
  ## Blocking Prompt Types
464
498
 
465
499
  The library recognizes these blocking prompt types:
package/dist/index.d.mts CHANGED
@@ -334,6 +334,15 @@ interface CLIAdapter {
334
334
  * Get prompt pattern to detect when CLI is waiting for input
335
335
  */
336
336
  getPromptPattern(): RegExp;
337
+ /**
338
+ * Optional: Detect if the CLI has completed a task and returned to its idle prompt.
339
+ * More specific than detectReady — matches high-confidence completion indicators
340
+ * (e.g. duration summaries, explicit "done" messages) alongside the idle prompt.
341
+ *
342
+ * Used as a fast-path in stall detection to avoid expensive LLM classifier calls.
343
+ * If not implemented, the stall classifier is used as the fallback.
344
+ */
345
+ detectTaskComplete?(output: string): boolean;
337
346
  /**
338
347
  * Optional: Validate that the CLI is installed and accessible
339
348
  */
@@ -774,6 +783,12 @@ declare abstract class BaseCLIAdapter implements CLIAdapter {
774
783
  * Subclasses should override for CLI-specific detection.
775
784
  */
776
785
  detectBlockingPrompt(output: string): BlockingPromptDetection;
786
+ /**
787
+ * Default task completion detection — delegates to detectReady().
788
+ * Subclasses should override to match high-confidence completion patterns
789
+ * (e.g. duration summaries) that short-circuit the LLM stall classifier.
790
+ */
791
+ detectTaskComplete(output: string): boolean;
777
792
  /**
778
793
  * Default input formatting - just return as-is
779
794
  */
package/dist/index.d.ts CHANGED
@@ -334,6 +334,15 @@ interface CLIAdapter {
334
334
  * Get prompt pattern to detect when CLI is waiting for input
335
335
  */
336
336
  getPromptPattern(): RegExp;
337
+ /**
338
+ * Optional: Detect if the CLI has completed a task and returned to its idle prompt.
339
+ * More specific than detectReady — matches high-confidence completion indicators
340
+ * (e.g. duration summaries, explicit "done" messages) alongside the idle prompt.
341
+ *
342
+ * Used as a fast-path in stall detection to avoid expensive LLM classifier calls.
343
+ * If not implemented, the stall classifier is used as the fallback.
344
+ */
345
+ detectTaskComplete?(output: string): boolean;
337
346
  /**
338
347
  * Optional: Validate that the CLI is installed and accessible
339
348
  */
@@ -774,6 +783,12 @@ declare abstract class BaseCLIAdapter implements CLIAdapter {
774
783
  * Subclasses should override for CLI-specific detection.
775
784
  */
776
785
  detectBlockingPrompt(output: string): BlockingPromptDetection;
786
+ /**
787
+ * Default task completion detection — delegates to detectReady().
788
+ * Subclasses should override to match high-confidence completion patterns
789
+ * (e.g. duration summaries) that short-circuit the LLM stall classifier.
790
+ */
791
+ detectTaskComplete(output: string): boolean;
777
792
  /**
778
793
  * Default input formatting - just return as-is
779
794
  */
package/dist/index.js CHANGED
@@ -486,6 +486,19 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
486
486
  return;
487
487
  }
488
488
  this._lastStallHash = hash;
489
+ if (this._status === "busy" && this.adapter.detectTaskComplete?.(this.outputBuffer)) {
490
+ this._status = "ready";
491
+ this._lastBlockingPromptHash = null;
492
+ this.outputBuffer = "";
493
+ this.clearStallTimer();
494
+ this.emit("status_changed", "ready");
495
+ this.emit("task_complete");
496
+ this.logger.info(
497
+ { sessionId: this.id },
498
+ "Task complete (adapter fast-path) \u2014 agent returned to idle prompt"
499
+ );
500
+ return;
501
+ }
489
502
  const recentRaw = this.outputBuffer.slice(-2e3);
490
503
  const recentOutput = this.stripAnsiForStall(recentRaw);
491
504
  const stallDurationMs = this._stallStartedAt ? Date.now() - this._stallStartedAt : this._stallTimeoutMs;
@@ -1594,6 +1607,14 @@ var BaseCLIAdapter = class {
1594
1607
  }
1595
1608
  return { detected: false };
1596
1609
  }
1610
+ /**
1611
+ * Default task completion detection — delegates to detectReady().
1612
+ * Subclasses should override to match high-confidence completion patterns
1613
+ * (e.g. duration summaries) that short-circuit the LLM stall classifier.
1614
+ */
1615
+ detectTaskComplete(output) {
1616
+ return this.detectReady(output);
1617
+ }
1597
1618
  /**
1598
1619
  * Default input formatting - just return as-is
1599
1620
  */