@songsid/agend 2.1.2-beta.40 → 2.1.2-beta.41
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/cli.js +23 -5
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +22 -0
- package/dist/daemon.js +71 -2
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.js +4 -0
- package/dist/fleet-manager.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +8 -0
- package/dist/instance-lifecycle.js +34 -5
- package/dist/instance-lifecycle.js.map +1 -1
- package/package.json +1 -1
package/dist/daemon.js
CHANGED
|
@@ -412,6 +412,68 @@ export function sanitizePaneTail(pane, lineCount = 5) {
|
|
|
412
412
|
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "")
|
|
413
413
|
.slice(0, 200));
|
|
414
414
|
}
|
|
415
|
+
const INTERACTIVE_PROMPT_PATTERNS = [
|
|
416
|
+
{ kind: "sudo_password", pattern: /^\s*\[sudo\]\s+password\s+for\s+[^:\n]+:\s*$/im },
|
|
417
|
+
// macOS sudo and several package installers use this exact no-echo prompt.
|
|
418
|
+
// Tail-only + stability gating is what makes the otherwise-generic word safe.
|
|
419
|
+
{ kind: "password", pattern: /^\s*(?:Password|Passphrase):\s*$/im },
|
|
420
|
+
{ kind: "confirmation", pattern: /^[^\n]{0,180}(?:\([Yy]\/[Nn]\)|\[[Yy]\/[Nn]\]|\((?:yes|no)\/(?:yes|no)\))\s*:?\s*$/im },
|
|
421
|
+
{ kind: "confirmation", pattern: /^\s*Are you sure[^\n]{0,160}\((?:yes\/no)(?:\/\[[^\]]+\])?\)\??\s*$/im },
|
|
422
|
+
{ kind: "press_enter", pattern: /^\s*(?:Please\s+)?Press (?:the )?Enter(?: key)?(?: to [^\n]{0,120})?[.:…]?\s*$/im },
|
|
423
|
+
];
|
|
424
|
+
/**
|
|
425
|
+
* Detect terminal prompts that require a human, but only after the pane tail is
|
|
426
|
+
* unchanged and no control-mode output has arrived for the full grace period.
|
|
427
|
+
* This prevents prose such as "the installer asks [Y/n]" from notifying while
|
|
428
|
+
* an agent is still writing it.
|
|
429
|
+
*/
|
|
430
|
+
export class InteractivePromptDetector {
|
|
431
|
+
stableMs;
|
|
432
|
+
signature = null;
|
|
433
|
+
stableSince = 0;
|
|
434
|
+
lastOutputAt = 0;
|
|
435
|
+
notifiedSignature = null;
|
|
436
|
+
constructor(stableMs = 10_000) {
|
|
437
|
+
this.stableMs = stableMs;
|
|
438
|
+
}
|
|
439
|
+
observe(pane, now = Date.now(), outputAt = 0) {
|
|
440
|
+
const tail = sanitizePaneTail(pane, 5);
|
|
441
|
+
const tailText = tail.join("\n");
|
|
442
|
+
let matched = null;
|
|
443
|
+
for (const candidate of INTERACTIVE_PROMPT_PATTERNS) {
|
|
444
|
+
const match = tailText.match(candidate.pattern);
|
|
445
|
+
if (!match)
|
|
446
|
+
continue;
|
|
447
|
+
matched = { kind: candidate.kind, prompt: match[0].trim().slice(0, 200) };
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
if (!matched) {
|
|
451
|
+
this.reset();
|
|
452
|
+
this.lastOutputAt = outputAt;
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
const signature = `${matched.kind}:${tailText}`;
|
|
456
|
+
const outputMoved = outputAt > this.lastOutputAt;
|
|
457
|
+
if (signature !== this.signature || outputMoved) {
|
|
458
|
+
this.signature = signature;
|
|
459
|
+
this.stableSince = now;
|
|
460
|
+
this.lastOutputAt = outputAt;
|
|
461
|
+
if (signature !== this.notifiedSignature)
|
|
462
|
+
this.notifiedSignature = null;
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
this.lastOutputAt = outputAt;
|
|
466
|
+
if (this.notifiedSignature === signature || now - this.stableSince < this.stableMs)
|
|
467
|
+
return null;
|
|
468
|
+
this.notifiedSignature = signature;
|
|
469
|
+
return matched;
|
|
470
|
+
}
|
|
471
|
+
reset() {
|
|
472
|
+
this.signature = null;
|
|
473
|
+
this.stableSince = 0;
|
|
474
|
+
this.notifiedSignature = null;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
415
477
|
export class Daemon extends EventEmitter {
|
|
416
478
|
name;
|
|
417
479
|
config;
|
|
@@ -528,6 +590,7 @@ export class Daemon extends EventEmitter {
|
|
|
528
590
|
firstDeliveryDelay = new FirstDeliveryDelay();
|
|
529
591
|
// PTY error pattern monitoring
|
|
530
592
|
errorMonitorTimer = null;
|
|
593
|
+
interactivePromptDetector = new InteractivePromptDetector();
|
|
531
594
|
/** Same 5-min gate the error monitor uses, so a dead MCP server alerts once. */
|
|
532
595
|
static MCP_DEATH_COOLDOWN_MS = 5 * 60_000;
|
|
533
596
|
lastMcpDeathNotifiedAt = 0;
|
|
@@ -1289,8 +1352,6 @@ export class Daemon extends EventEmitter {
|
|
|
1289
1352
|
return;
|
|
1290
1353
|
const patterns = this.backend?.getErrorPatterns?.() ?? [];
|
|
1291
1354
|
const dialogs = this.backend?.getRuntimeDialogs?.() ?? [];
|
|
1292
|
-
if (!patterns.length && !dialogs.length)
|
|
1293
|
-
return;
|
|
1294
1355
|
if (!this.tmux)
|
|
1295
1356
|
return;
|
|
1296
1357
|
if (!this.backend)
|
|
@@ -1305,6 +1366,13 @@ export class Daemon extends EventEmitter {
|
|
|
1305
1366
|
if (!alive)
|
|
1306
1367
|
return;
|
|
1307
1368
|
const pane = await this.tmux.capturePane();
|
|
1369
|
+
const interactivePrompt = this.interactivePromptDetector.observe(pane, Date.now(), this.instanceStateLastOutputAt);
|
|
1370
|
+
if (interactivePrompt) {
|
|
1371
|
+
this.logger.warn(interactivePrompt, "Interactive terminal prompt is waiting for human input");
|
|
1372
|
+
this.emit("interactive_prompt", { name: this.name, ...interactivePrompt });
|
|
1373
|
+
// A prompt is not an error and must not enter the PTY recovery gate.
|
|
1374
|
+
// Continue scanning real errors in this same snapshot.
|
|
1375
|
+
}
|
|
1308
1376
|
// Auto-dismiss runtime dialogs (e.g. Codex rate limit model switch)
|
|
1309
1377
|
for (const dialog of dialogs) {
|
|
1310
1378
|
if (!dialog.pattern.test(pane))
|
|
@@ -1955,6 +2023,7 @@ export class Daemon extends EventEmitter {
|
|
|
1955
2023
|
clearInterval(this.errorMonitorTimer);
|
|
1956
2024
|
this.errorMonitorTimer = null;
|
|
1957
2025
|
}
|
|
2026
|
+
this.interactivePromptDetector.reset();
|
|
1958
2027
|
this.stopInstanceStateMonitor();
|
|
1959
2028
|
this.transcriptMonitor?.stop();
|
|
1960
2029
|
this.guardian?.stop();
|