feat(yolo): bypass all sandboxing — iteration limit, memory gates, guard breaks

YOLO = all guardrails off. When s.isYolo() is true the loop:
- Skips MAX_LOOP_ITERATIONS stop (logs warning, keeps going)
- Skips memory pressure stop (logs warning, accepts OOM risk)
- Bypasses guard breaks (logs warning, continues to next unit)

Build mode respects all these gates. YOLO does not.

Also fix notify messages: YOLO = no sandboxing, not just 'no prompts'
(autonomous mode already skips prompts — YOLO removes the safety net).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Mikael Hugo 2026-05-09 20:00:56 +02:00
parent 6c132d5db0
commit d2eda0cc12
2 changed files with 39 additions and 23 deletions

View file

@ -479,13 +479,17 @@ export async function autoLoop(ctx, pi, s, deps) {
reason: "max-iterations",
iteration,
});
await deps.stopAuto(
ctx,
pi,
`Safety: loop exceeded ${MAX_LOOP_ITERATIONS} iterations — possible runaway`,
);
finishTurn("stopped", "manual-attention", "max-iterations");
break;
if (s.isYolo()) {
logWarning("dispatch", `YOLO: loop at ${iteration} iterations — continuing past safety limit`);
} else {
await deps.stopAuto(
ctx,
pi,
`Safety: loop exceeded ${MAX_LOOP_ITERATIONS} iterations — possible runaway`,
);
finishTurn("stopped", "manual-attention", "max-iterations");
break;
}
}
// ── Memory pressure check (#3331) ──
// Graceful shutdown before OOM killer sends SIGKILL.
@ -497,15 +501,19 @@ export async function autoLoop(ctx, pi, s, deps) {
"dispatch",
`Memory pressure: ${mem.heapMB}MB / ${mem.limitMB}MB (${Math.round(mem.pct * 100)}%) — stopping autonomous mode to prevent OOM kill`,
);
await deps.stopAuto(
ctx,
pi,
`Memory pressure: heap at ${mem.heapMB}MB / ${mem.limitMB}MB (${Math.round(mem.pct * 100)}%). ` +
`Stopping gracefully to prevent OOM kill after ${iteration} iterations. ` +
`Resume with /autonomous to continue from where you left off.`,
);
finishTurn("stopped", "timeout", "memory-pressure");
break;
if (s.isYolo()) {
logWarning("dispatch", "YOLO: continuing despite memory pressure — OOM risk accepted");
} else {
await deps.stopAuto(
ctx,
pi,
`Memory pressure: heap at ${mem.heapMB}MB / ${mem.limitMB}MB (${Math.round(mem.pct * 100)}%). ` +
`Stopping gracefully to prevent OOM kill after ${iteration} iterations. ` +
`Resume with /autonomous to continue from where you left off.`,
);
finishTurn("stopped", "timeout", "memory-pressure");
break;
}
}
}
if (!s.cmdCtx) {
@ -685,8 +693,12 @@ export async function autoLoop(ctx, pi, s, deps) {
unitId: iterData.unitId,
});
if (guardsResult.action === "break") {
finishTurn("stopped", "manual-attention", "guard-break");
break;
if (s.isYolo()) {
logWarning("dispatch", `YOLO: bypassing guard break for ${iterData.unitId}`);
} else {
finishTurn("stopped", "manual-attention", "guard-break");
break;
}
}
// ── Unit execution (shared with dev path) ──
await enforceMinRequestInterval(s, ic.prefs);
@ -970,8 +982,12 @@ export async function autoLoop(ctx, pi, s, deps) {
);
deps.uokObserver?.onPhaseResult("guard", guardsResult.action);
if (guardsResult.action === "break") {
finishTurn("stopped", "manual-attention", "guard-break");
break;
if (s.isYolo()) {
logWarning("dispatch", `YOLO: bypassing guard break for ${iterData.unitId}`);
} else {
finishTurn("stopped", "manual-attention", "guard-break");
break;
}
}
} else {
// ── Sidecar path: use values from the sidecar item directly ──

View file

@ -89,11 +89,11 @@ export default function steerableAutonomousExtension(api) {
}
if (enabled) {
const msg = wasAsk
? "🚀 YOLO — Build mode · no stops · no confirmations"
: "🚀 YOLO — no stops · no confirmations · deep model";
? "🚀 YOLO — Build mode · no git prompts · no confirmation dialogs"
: "🚀 YOLO — no git prompts · no confirmation dialogs · deep model";
ctx.ui.notify(msg, "success");
} else {
ctx.ui.notify("YOLO OFF — Build mode restored (may still pause at gates)", "info");
ctx.ui.notify("YOLO OFF — Build mode (git prompts restored)", "info");
}
},
});