diff --git a/extensions/rule-enforcer.ts b/extensions/rule-enforcer.ts index 27d6956..3596ece 100644 --- a/extensions/rule-enforcer.ts +++ b/extensions/rule-enforcer.ts @@ -212,62 +212,70 @@ export default function (pi: ExtensionAPI) { } // ── 30s Background-Check: Invariante erzwingen ─────────────────────────── + // WICHTIG: Der gesamte Rumpf ist in try/catch gekapselt. Pis Loader umschließt + // nur die synchrone Factory-Ausführung (loader.js:298-311); dieses Intervall + // feuert später und läge sonst AUSSERHALB von Pis Schutz — eine Exception hier + // wäre uncaught und könnte den Pi-Prozess beenden. Darum: niemals werfen. const interval = setInterval(() => { - const subagenten = laufendeSubagenten(); - if (subagenten.length === 0) return; // kein laufender Subagent ⇒ idle ist erlaubt - - const dialoge = subagenten.filter((s) => s.dialog); - const beschreibung = subagenten - .map( - (s) => - ` • ${s.name}${s.dialog ? " — WARTET AUF BESTÄTIGUNG" : " — läuft"}`, - ) - .join("\n"); - - // Passiver Pfad: greift, falls der Orchestrator gerade in einem Turn ist - schreibeAlert( - `[30s-Check] Laufende Subagenten — Orchestrator muss überwachen:\n${beschreibung}`, - ); - - // Idle-Erkennung: primär zeitbasiert (ctx-unabhängig) - const idleMs = Date.now() - letzteAktivitaet; - if (idleMs < IDLE_SCHWELLE_MS) return; // Orchestrator aktiv ⇒ nichts tun - - // Zusätzliches Veto, falls ctx zuverlässig "beschäftigt" meldet try { - if (letzterCtx && typeof letzterCtx.isIdle === "function") { - if (letzterCtx.isIdle() === false) return; - } - } catch {} + const subagenten = laufendeSubagenten(); + if (subagenten.length === 0) return; // kein laufender Subagent ⇒ idle ist erlaubt - const jetzt = Date.now(); - if (jetzt - letzteWeckung < WECK_MIN_ABSTAND_MS) return; - letzteWeckung = jetzt; + const dialoge = subagenten.filter((s) => s.dialog); + const beschreibung = subagenten + .map( + (s) => + ` • ${s.name}${s.dialog ? " — WARTET AUF BESTÄTIGUNG" : " — läuft"}`, + ) + .join("\n"); - const dringlich = dialoge.length > 0; - const dialogText = dialoge - .map((d) => `--- ${d.name} ---\n${d.dialog}`) - .join("\n"); - - try { - letzterCtx?.ui?.notify?.( - dringlich - ? "🚨 Subagent wartet auf Bestätigung — Orchestrator wird geweckt." - : "⏱️ Subagent läuft, Orchestrator war idle — wird geweckt.", - "warning", + // Passiver Pfad: greift, falls der Orchestrator gerade in einem Turn ist + schreibeAlert( + `[30s-Check] Laufende Subagenten — Orchestrator muss überwachen:\n${beschreibung}`, ); - } catch {} - try { - pi.sendUserMessage( - `🚨 [Auto-Weckung] Es läuft mindestens ein Subagent, aber du warst idle. ` + - `Das ist nicht erlaubt, solange Subagenten laufen.\n${beschreibung}\n` + - (dringlich - ? `\nEin Subagent wartet auf eine Bestätigung:\n${dialogText}\n→ Sofort watch_subagents aufrufen, den Dialog prüfen und nach Regel bestätigen ODER den Benutzer informieren.\n` - : ``) + - `→ Nimm die watch_subagents-Überwachung sofort wieder auf und halte sie, bis ALLE Subagenten fertig sind.`, - ); - } catch {} + // Idle-Erkennung: primär zeitbasiert (ctx-unabhängig) + const idleMs = Date.now() - letzteAktivitaet; + if (idleMs < IDLE_SCHWELLE_MS) return; // Orchestrator aktiv ⇒ nichts tun + + // Zusätzliches Veto, falls ctx zuverlässig "beschäftigt" meldet + try { + if (letzterCtx && typeof letzterCtx.isIdle === "function") { + if (letzterCtx.isIdle() === false) return; + } + } catch {} + + const jetzt = Date.now(); + if (jetzt - letzteWeckung < WECK_MIN_ABSTAND_MS) return; + letzteWeckung = jetzt; + + const dringlich = dialoge.length > 0; + const dialogText = dialoge + .map((d) => `--- ${d.name} ---\n${d.dialog}`) + .join("\n"); + + try { + letzterCtx?.ui?.notify?.( + dringlich + ? "🚨 Subagent wartet auf Bestätigung — Orchestrator wird geweckt." + : "⏱️ Subagent läuft, Orchestrator war idle — wird geweckt.", + "warning", + ); + } catch {} + + try { + pi.sendUserMessage( + `🚨 [Auto-Weckung] Es läuft mindestens ein Subagent, aber du warst idle. ` + + `Das ist nicht erlaubt, solange Subagenten laufen.\n${beschreibung}\n` + + (dringlich + ? `\nEin Subagent wartet auf eine Bestätigung:\n${dialogText}\n→ Sofort watch_subagents aufrufen, den Dialog prüfen und nach Regel bestätigen ODER den Benutzer informieren.\n` + : ``) + + `→ Nimm die watch_subagents-Überwachung sofort wieder auf und halte sie, bis ALLE Subagenten fertig sind.`, + ); + } catch {} + } catch { + // Defensive Notbremse: Das Intervall darf den Pi-Prozess NIE crashen. + } }, CHECK_INTERVAL_MS); pi.on("session_shutdown", async () => {