fix/extensions: rule-enforcer — setInterval gegen uncaught exceptions härten
Pis Loader (loader.js:298-311) kapselt nur die synchrone Factory-Ausführung. Das 30s-setInterval feuert später und läge außerhalb dieses Schutzes — eine Exception dort wäre uncaught und könnte den Pi-Prozess beenden. Daher: gesamter Intervall-Rumpf in try/catch. Empirisch verifiziert (Pi-SDK createAgentSession, agentDir=~/.pi/agent): - rule-enforcer.ts lädt mit 0 Fehlern (10/10 Extensions geladen) - absichtlich kaputte Test-Extension crasht Pi NICHT — Fehler isoliert, gesunde Extensions laden weiter, Session startet normal
This commit is contained in:
parent
115cc61627
commit
6371fb9f60
1 changed files with 58 additions and 50 deletions
|
|
@ -212,62 +212,70 @@ export default function (pi: ExtensionAPI) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 30s Background-Check: Invariante erzwingen ───────────────────────────
|
// ── 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 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 {
|
try {
|
||||||
if (letzterCtx && typeof letzterCtx.isIdle === "function") {
|
const subagenten = laufendeSubagenten();
|
||||||
if (letzterCtx.isIdle() === false) return;
|
if (subagenten.length === 0) return; // kein laufender Subagent ⇒ idle ist erlaubt
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
const jetzt = Date.now();
|
const dialoge = subagenten.filter((s) => s.dialog);
|
||||||
if (jetzt - letzteWeckung < WECK_MIN_ABSTAND_MS) return;
|
const beschreibung = subagenten
|
||||||
letzteWeckung = jetzt;
|
.map(
|
||||||
|
(s) =>
|
||||||
|
` • ${s.name}${s.dialog ? " — WARTET AUF BESTÄTIGUNG" : " — läuft"}`,
|
||||||
|
)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
const dringlich = dialoge.length > 0;
|
// Passiver Pfad: greift, falls der Orchestrator gerade in einem Turn ist
|
||||||
const dialogText = dialoge
|
schreibeAlert(
|
||||||
.map((d) => `--- ${d.name} ---\n${d.dialog}`)
|
`[30s-Check] Laufende Subagenten — Orchestrator muss überwachen:\n${beschreibung}`,
|
||||||
.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 {
|
// Idle-Erkennung: primär zeitbasiert (ctx-unabhängig)
|
||||||
pi.sendUserMessage(
|
const idleMs = Date.now() - letzteAktivitaet;
|
||||||
`🚨 [Auto-Weckung] Es läuft mindestens ein Subagent, aber du warst idle. ` +
|
if (idleMs < IDLE_SCHWELLE_MS) return; // Orchestrator aktiv ⇒ nichts tun
|
||||||
`Das ist nicht erlaubt, solange Subagenten laufen.\n${beschreibung}\n` +
|
|
||||||
(dringlich
|
// Zusätzliches Veto, falls ctx zuverlässig "beschäftigt" meldet
|
||||||
? `\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`
|
try {
|
||||||
: ``) +
|
if (letzterCtx && typeof letzterCtx.isIdle === "function") {
|
||||||
`→ Nimm die watch_subagents-Überwachung sofort wieder auf und halte sie, bis ALLE Subagenten fertig sind.`,
|
if (letzterCtx.isIdle() === false) return;
|
||||||
);
|
}
|
||||||
} catch {}
|
} 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);
|
}, CHECK_INTERVAL_MS);
|
||||||
|
|
||||||
pi.on("session_shutdown", async () => {
|
pi.on("session_shutdown", async () => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue