Fix MoA single-round and writable read-only panel members
Two bugs surfaced when driving the composite toggle (off→plan→fusion→moa) in an all-CLI setup (no API keys): 1. MoA ran only 1 round instead of the configured 2. cycleCompositeMode fired the multiModelMode PATCH un-serialized and sendMessage did not await it, so a message sent right after toggling loaded the session before 'moa' was committed → the backend read the prior 'fusion' value → 1 round. Persisted totalLayers in the DB confirmed fusion (1) for all post-toggle chains. Serialize mode PATCHes into a promise chain (last click wins, in order) and await it in sendMessage/sendMessageDirect. 2. A read-only panel member could still write files. ClaudeCliService hard-coded --permission-mode acceptEdits, which auto-accepts Write/Edit regardless of --allowed-tools, so a member running with planMode=true (tools limited to Read/Glob/Grep) could still call write_file. Use 'plan' when planMode is set; this also closes the same latent hole for regular plan-mode users on the shared CLI path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
e3f89043c1
commit
2501096e19
2 files changed
+31
-9
No files matched your search
+24
-8
@@ -236,6 +236,14 @@ let _planModeActive = false; // true genau wenn _compositeMode === 'plan'
|
||||
|
||||
const _COMPOSITE_ORDER = ['off', 'plan', 'fusion', 'moa'];
|
||||
|
||||
// Serialisierte Modus-Persistenz. Schnelles Durchklicken (off→plan→fusion→moa) feuert sonst
|
||||
// ÜBERLAPPENDE PATCHes, deren Commit-Reihenfolge nicht garantiert ist → der zuletzt committete
|
||||
// Zwischenwert 'fusion' kann 'moa' überschreiben. Und ein direkt danach gesendeter Chat-Request
|
||||
// lädt die Session, BEVOR 'moa' committed ist → das Backend liest den alten Modus (Fusion → 1 Runde
|
||||
// statt MoA). Die Kette garantiert (a) Reihenfolge (letzter Klick gewinnt) und (b) dass sendMessage
|
||||
// das offene PATCH abwarten kann. Die Kette rejected nie (Fehler werden hier geschluckt).
|
||||
let _modePersist = Promise.resolve();
|
||||
|
||||
function _renderCompositeBtn() {
|
||||
var btn = $('#planModeBtn');
|
||||
if (!btn) return;
|
||||
@@ -281,17 +289,19 @@ async function cycleCompositeMode() {
|
||||
_planModeActive = (_compositeMode === 'plan');
|
||||
_renderCompositeBtn();
|
||||
|
||||
// Nur Fusion/MoA/Off brauchen einen Server-Call (Plan ist transient)
|
||||
// Nur Fusion/MoA/Off brauchen einen Server-Call (Plan ist transient). Auf die serialisierte
|
||||
// Kette legen, damit aufeinanderfolgende Klicks in Reihenfolge committen (letzter Klick gewinnt)
|
||||
// und sendMessage das offene PATCH abwarten kann.
|
||||
if (_compositeMode !== 'plan') {
|
||||
var sid = getCurrentSessionId();
|
||||
if (sid) {
|
||||
try {
|
||||
await apiUpdateSession(sid, { multiModelMode: _compositeMode });
|
||||
} catch (e) {
|
||||
console.error('Modus speichern fehlgeschlagen:', e);
|
||||
if (typeof showToast === 'function') showToast('Modus konnte nicht gespeichert werden', 'error');
|
||||
return;
|
||||
}
|
||||
var targetMode = _compositeMode;
|
||||
_modePersist = _modePersist
|
||||
.then(function () { return apiUpdateSession(sid, { multiModelMode: targetMode }); })
|
||||
.catch(function (e) {
|
||||
console.error('Modus speichern fehlgeschlagen:', e);
|
||||
if (typeof showToast === 'function') showToast('Modus konnte nicht gespeichert werden', 'error');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,6 +590,11 @@ async function sendMessage() {
|
||||
input.value = '';
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
// Ein evtl. noch offenes Modus-PATCH (Fusion/MoA/Aus aus dem Toggle) MUSS committed sein,
|
||||
// bevor der Chat-Request die Session lädt – sonst läuft die Chain mit dem alten Modus
|
||||
// (z.B. MoA → nur 1 Runde, weil das Backend noch 'fusion' liest).
|
||||
await _modePersist;
|
||||
|
||||
await _runStream((signal) => apiChat(content, getCurrentSessionId(), signal, _planModeActive));
|
||||
}
|
||||
|
||||
@@ -616,6 +631,7 @@ async function sendMessageDirect(text) {
|
||||
if (isStreaming || !text) return;
|
||||
addUserBubble(text, true);
|
||||
scrollDownForce();
|
||||
await _modePersist; // offenes Modus-PATCH abwarten (s. sendMessage)
|
||||
await _runStream((signal) => apiChat(text, getCurrentSessionId(), signal, _planModeActive));
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user