@songsid/agend 2.1.4-beta.25 → 2.1.4-beta.27

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.
@@ -10,7 +10,7 @@ export interface InstanceStatusData {
10
10
  costCents: number;
11
11
  }
12
12
  export interface AlertData {
13
- type: "hang" | "cost_warn" | "cost_limit" | "schedule_deferred" | "rotation" | "cancel" | "interactive_prompt" | "exit_restart" | "clear_confirm" | "tip";
13
+ type: "hang" | "cost_warn" | "cost_limit" | "schedule_deferred" | "rotation" | "cancel" | "interactive_prompt" | "exit_restart" | "clear_confirm" | "tip" | "login";
14
14
  instanceName: string;
15
15
  message: string;
16
16
  choices?: Choice[];
package/dist/daemon.d.ts CHANGED
@@ -385,6 +385,8 @@ export declare class Daemon extends EventEmitter {
385
385
  * instance is still auth-broken and must not resume raising alarms.
386
386
  */
387
387
  private authFailureUnresolved;
388
+ /** One login-screen auth report per spawn — the screen persists across polls. */
389
+ private loginScreenReported;
388
390
  /**
389
391
  * Let the next pause() proceed from a stuck pane. Set only for the auth-deferred
390
392
  * pause — see the pausePending consumption site for why waiting for idle there
package/dist/daemon.js CHANGED
@@ -9,6 +9,7 @@ import { clearPausedMarker, writePausedMarker } from "./pause-marker.js";
9
9
  import { TmuxManager, resolveTmuxLogicalSize } from "./tmux-manager.js";
10
10
  import { TranscriptMonitor } from "./transcript-monitor.js";
11
11
  import { createTranscriptSource } from "./transcript-sources.js";
12
+ import { LOGIN_FLOWS } from "./login-flows.js";
12
13
  import { ProgressAccumulator, summarizeProgress } from "./tool-progress.js";
13
14
  import { ContextGuardian } from "./context-guardian.js";
14
15
  import { IpcServer } from "./channel/ipc-bridge.js";
@@ -773,6 +774,8 @@ export class Daemon extends EventEmitter {
773
774
  * instance is still auth-broken and must not resume raising alarms.
774
775
  */
775
776
  authFailureUnresolved = false;
777
+ /** One login-screen auth report per spawn — the screen persists across polls. */
778
+ loginScreenReported = false;
776
779
  /**
777
780
  * Let the next pause() proceed from a stuck pane. Set only for the auth-deferred
778
781
  * pause — see the pausePending consumption site for why waiting for idle there
@@ -1241,9 +1244,13 @@ export class Daemon extends EventEmitter {
1241
1244
  return;
1242
1245
  this.mcpDeathNotifiedForPid = status.pid;
1243
1246
  this.lastMcpDeathNotifiedAt = Date.now();
1244
- const autoRestart = this.config.mcp_auto_restart !== false;
1245
- this.logger.error({ pid: status.pid, autoRestart }, "MCP server process is gone — instance has no agend tools");
1246
- this.emit("mcp_died", { name: this.name, pid: status.pid, autoRestart });
1247
+ // An auth-broken CLI cannot keep an MCP server alive, and restarting it
1248
+ // only loops back to the sign-in screen — hold the auto-restart until a
1249
+ // re-login (which restarts the instance and replaces this daemon anyway).
1250
+ const authSuspected = this.authFailureUnresolved;
1251
+ const autoRestart = this.config.mcp_auto_restart !== false && !authSuspected;
1252
+ this.logger.error({ pid: status.pid, autoRestart, authSuspected }, "MCP server process is gone — instance has no agend tools");
1253
+ this.emit("mcp_died", { name: this.name, pid: status.pid, autoRestart, authSuspected });
1247
1254
  if (autoRestart)
1248
1255
  this.armMcpRestartWhenIdle();
1249
1256
  }
@@ -2156,6 +2163,7 @@ export class Daemon extends EventEmitter {
2156
2163
  // auth pause — otherwise the instance would pause again the moment it idles.
2157
2164
  this.pausePending = false;
2158
2165
  this.authFailureUnresolved = false;
2166
+ this.loginScreenReported = false;
2159
2167
  if (this.pauseWakeState === "active")
2160
2168
  return;
2161
2169
  if (this.pauseWakeState === "pausing")
@@ -4169,6 +4177,24 @@ export class Daemon extends EventEmitter {
4169
4177
  // CLI is ready (pattern defined by each backend)
4170
4178
  if (this.backend.getReadyPattern().test(pane))
4171
4179
  return true;
4180
+ // A CLI parked at its own sign-in screen is an AUTH incident: no dialog
4181
+ // key can dismiss it and no ready pattern will ever appear. Report it as
4182
+ // an auth error (the lifecycle double-checks with the token-free probe)
4183
+ // instead of letting it decay into crash/hang/MCP-died noise, and stop
4184
+ // the retry loop — only re-login (see /login) ends this state.
4185
+ const loginScreen = LOGIN_FLOWS[this.config.backend ?? "claude-code"]?.loginScreenPattern;
4186
+ if (loginScreen?.test(pane) && !this.loginScreenReported) {
4187
+ this.loginScreenReported = true;
4188
+ this.authFailureUnresolved = true;
4189
+ this.logger.warn({ backend: this.config.backend }, "CLI is waiting at its sign-in screen — reporting auth error");
4190
+ this.emit("pty_error", {
4191
+ name: this.name,
4192
+ type: "auth_error",
4193
+ action: "pause",
4194
+ message: "CLI is waiting at its sign-in screen — credentials are missing or expired",
4195
+ });
4196
+ return true;
4197
+ }
4172
4198
  // Fatal: command not found (must match full phrase to avoid false positives
4173
4199
  // like Kiro's "agent X not found, using default")
4174
4200
  if (/command not found|: not found$/m.test(pane))