@songsid/agend 2.1.4-beta.30 → 2.1.4-beta.31

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/daemon.js CHANGED
@@ -776,6 +776,8 @@ export class Daemon extends EventEmitter {
776
776
  authFailureUnresolved = false;
777
777
  /** One login-screen auth report per spawn — the screen persists across polls. */
778
778
  loginScreenReported = false;
779
+ /** model_error seen mid-turn: notify only if it survives to the idle screen. */
780
+ pendingModelErrorKey = null;
779
781
  /**
780
782
  * Let the next pause() proceed from a stuck pane. Set only for the auth-deferred
781
783
  * pause — see the pausePending consumption site for why waiting for idle there
@@ -1815,6 +1817,19 @@ export class Daemon extends EventEmitter {
1815
1817
  this.logger.warn({ errorType: this.lastDetectedErrorType }, "PTY error recovery deadline expired — resuming error scan");
1816
1818
  this.clearErrorRecoveryGate();
1817
1819
  }
1820
+ // A model_error deferred mid-turn: the turn is over (or the pane left
1821
+ // "working"), so decide now — still on screen means the CLI gave up and
1822
+ // the user must act; gone means a retry got past it and nobody needs to know.
1823
+ if (this.pendingModelErrorKey && this.instanceState !== "working") {
1824
+ const key = this.pendingModelErrorKey;
1825
+ this.pendingModelErrorKey = null;
1826
+ const ep = patterns.find(candidate => Daemon.errorPatternKey(candidate) === key);
1827
+ if (ep && ep.pattern.test(pane)) {
1828
+ this.emitErrorPattern(ep, key, pane, now);
1829
+ return;
1830
+ }
1831
+ this.logger.debug({ errorKey: key }, "Deferred model_error cleared before idle — dropped");
1832
+ }
1818
1833
  // State: monitoring — count-based new-error detection over the full pane.
1819
1834
  for (const ep of patterns) {
1820
1835
  const key = Daemon.errorPatternKey(ep);
@@ -1842,30 +1857,49 @@ export class Daemon extends EventEmitter {
1842
1857
  continue;
1843
1858
  }
1844
1859
  this.lastErrorCount.set(key, count);
1845
- // skipRecoveryWait: this error self-recovers (e.g. a timeout — Kiro is
1846
- // back at its prompt immediately). Keep monitoring so the next occurrence
1847
- // can fire without waiting for a possibly startup-only ready pattern.
1848
- if (!ep.skipRecoveryWait) {
1849
- this.errorWaitingForRecovery = true;
1850
- this.errorDetectedAt = now;
1851
- this.errorRecoveryDeadlineAt = now + Daemon.ERROR_RECOVERY_TIMEOUT_MS;
1852
- this.activeErrorPatternKey = key;
1853
- this.lastDetectedErrorType = ep.type;
1860
+ // Kiro's `auto` model picks a concrete model per turn and reroutes around
1861
+ // a temporarily unavailable one on its own — telling the user to run
1862
+ // /model would ask them to fix something the CLI is already handling.
1863
+ if (ep.type === "model_error" && ep.action === "notify" && this.config.model === "auto") {
1864
+ this.logger.debug({ errorType: ep.type }, "model_error suppressed — model 'auto' reroutes on its own");
1865
+ continue;
1854
1866
  }
1855
- this.lastErrorNotifiedAt.set(key, now);
1856
- if (ep.action === "failover")
1857
- this.lastFailoverAt = now;
1858
- const message = this.resolveErrorMessage(pane, ep);
1859
- // Remember an auth failure for as long as it is unresolved. An auth-broken
1860
- // CLI cannot finish the turn it is holding, so every other detector in this
1861
- // scan would otherwise keep reporting the same dead instance forever.
1862
- if (ep.type === "auth_error")
1863
- this.authFailureUnresolved = true;
1864
- this.logger.warn({ errorType: ep.type, action: ep.action }, `PTY error detected: ${message}`);
1865
- this.emit("pty_error", { name: this.name, ...ep, message });
1867
+ // A mid-turn model error is often a transient the CLI retries past. Hold
1868
+ // the notification until the turn ends and notify only if the error is
1869
+ // still on the idle screen (see the deferred check above).
1870
+ if (ep.type === "model_error" && ep.action === "notify" && this.instanceState === "working") {
1871
+ this.pendingModelErrorKey = key;
1872
+ this.logger.debug({ errorType: ep.type }, "model_error deferred — instance is mid-turn, re-checking at idle");
1873
+ continue;
1874
+ }
1875
+ this.emitErrorPattern(ep, key, pane, now);
1866
1876
  break; // Only handle first unsuppressed new error per scan
1867
1877
  }
1868
1878
  }
1879
+ /** Arm the recovery gates and emit one detected error pattern. */
1880
+ emitErrorPattern(ep, key, pane, now) {
1881
+ // skipRecoveryWait: this error self-recovers (e.g. a timeout — Kiro is
1882
+ // back at its prompt immediately). Keep monitoring so the next occurrence
1883
+ // can fire without waiting for a possibly startup-only ready pattern.
1884
+ if (!ep.skipRecoveryWait) {
1885
+ this.errorWaitingForRecovery = true;
1886
+ this.errorDetectedAt = now;
1887
+ this.errorRecoveryDeadlineAt = now + Daemon.ERROR_RECOVERY_TIMEOUT_MS;
1888
+ this.activeErrorPatternKey = key;
1889
+ this.lastDetectedErrorType = ep.type;
1890
+ }
1891
+ this.lastErrorNotifiedAt.set(key, now);
1892
+ if (ep.action === "failover")
1893
+ this.lastFailoverAt = now;
1894
+ const message = this.resolveErrorMessage(pane, ep);
1895
+ // Remember an auth failure for as long as it is unresolved. An auth-broken
1896
+ // CLI cannot finish the turn it is holding, so every other detector in this
1897
+ // scan would otherwise keep reporting the same dead instance forever.
1898
+ if (ep.type === "auth_error")
1899
+ this.authFailureUnresolved = true;
1900
+ this.logger.warn({ errorType: ep.type, action: ep.action }, `PTY error detected: ${message}`);
1901
+ this.emit("pty_error", { name: this.name, ...ep, message });
1902
+ }
1869
1903
  /**
1870
1904
  * Notification text for a detected pattern. `formatMessage` patterns build it
1871
1905
  * from the match so the user gets the specifics (e.g. which period and what