Add workflow-status telemetry view and make panel modes on-demand

Workflow-status: a new WorkflowProgressTracker singleton taps the
suppressed dynamic-workflow subagent events (those carrying
parent_tool_use_id) into per-session telemetry without altering the
existing guard; the snapshot is serialized into the new chains.workflow_info
column on FinalizeChain (ChainId-matched so a panel member of the same
session cannot clear it) and exposed via GET /api/sessions/{id}/workflow-status
(live -> persisted -> none). The header menu gains a 'Workflow-Status' entry
with a modal that live-polls every 2s while running and self-stops on close.

Panel on-demand: Fusion/MoA is now a transient per-request multiModelMode
flag (like plan/ultracode) instead of a persisted session column. Session
settings keep only the panel members + moa_layers; the mode <select> was
removed and the composer dropdown drives the mode per request.

Docs: ARCHITECTURE.md, ARCHITECTURE_QUICK.md and README.md updated for both
features plus the already-shipped z.ai/GLM provider, GLM 5.2+ effort
passthrough, web-search-prime MCP and the ultracode toggle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MelekiandClaude Opus 4.8 committed 2026-06-20 17:27:46 +02:00
1 parent 5b1802efee
commit b6e546f618
22 files changed
+591 -99

No files matched your search

+19 -46
View File
@@ -223,19 +223,18 @@ function handleStreamEvent(eventType, data) {
// der Betriebsmodus direkt gewählt wird. Der Button zeigt weiterhin das Symbol des aktiven
// Modus; im Menü ist der aktive Eintrag markiert.
//
// Plan-Modus und Ultracode sind transient (pro Request, nicht in der Session persistiert) und
// laufen ohne Panel → beim Wählen wird multiModelMode der Session auf 'off' gesetzt, damit kein
// zuvor gewähltes Fusion/MoA-Panel nebenherläuft (das Menü erlaubt direkte Sprünge, anders als
// der frühere Zyklus, der immer durch 'off' lief). Fusion/MoA werden in der Session persistiert
// und server-seitig aus ctx.MultiModelMode gelesen.
// ALLE Modi (Plan / Ultracode / Fusion / MoA) sind transient: sie gelten nur für die nächste
// Anfrage, werden NICHT in der Session persistiert und beim Chat-Wechsel auf 'off' zurückgesetzt.
// Der gewählte Modus wird pro Request als Flag mitgeschickt (planMode / ultracode / multiModelMode).
// Die Panel-MITGLIEDER (PanelAgentIds) bleiben dagegen in den Session-Einstellungen persistent.
//
// _planModeActive / _ultracodeActive bleiben als Compat-Variablen (sendMessage + modals.js lesen
// sie direkt).
// sie direkt); Fusion/MoA werden direkt aus _compositeMode abgeleitet.
// Metadaten je Modus – eine Quelle für Button-Render UND Menü-Aufbau: Reihenfolge im Menü,
// Icon (Button + Menü), Label, Button-Aktiv-Klasse, Input-Placeholder, kurze Beschreibung.
const _COMPOSITE_MODES = [
{ mode: 'off', icon: 'fa-ban', label: 'Aus', btnClass: null, placeholder: 'Nachricht...', desc: 'Normaler Einzel-Agent' },
{ mode: 'off', icon: 'fa-user', label: 'Normal', btnClass: null, placeholder: 'Nachricht...', desc: 'Normaler Einzel-Agent' },
{ mode: 'plan', icon: 'fa-map', label: 'Plan-Modus', btnClass: 'plan-mode-active', placeholder: 'Planen...', desc: 'Nur analysieren & planen (read-only)' },
{ mode: 'ultracode', icon: 'fa-sitemap', label: 'Ultracode', btnClass: 'ultracode-active', placeholder: 'Ultracode – als Workflow...', desc: 'Dynamischer Workflow (mehrere Subagenten)' },
{ mode: 'fusion', icon: 'fa-users', label: 'Fusion', btnClass: 'panel-mode-fusion', placeholder: 'Nachricht...', desc: 'Panel: Antworten fusionieren' },
@@ -253,14 +252,6 @@ let _ultracodeActive = false; // true genau wenn _compositeMode === 'ultracode
let _compositeMenuEl = null;
let _compositeMenuCloseHandler = null;
// Serialisierte Modus-Persistenz. Schnell hintereinander gewählte Modi feuern sonst ÜBERLAPPENDE
// PATCHes, deren Commit-Reihenfolge nicht garantiert ist → ein Zwischenwert kann den letzten
// überschreiben. Und ein direkt danach gesendeter Chat-Request lädt die Session, BEVOR der Modus
// committed ist → das Backend liest den alten Modus. Die Kette garantiert (a) Reihenfolge (letzte
// Wahl gewinnt) und (b) dass sendMessage das offene PATCH abwarten kann. Sie rejected nie (Fehler
// werden hier geschluckt).
let _modePersist = Promise.resolve();
function _compositeMeta(mode) {
for (var i = 0; i < _COMPOSITE_MODES.length; i++) {
if (_COMPOSITE_MODES[i].mode === mode) return _COMPOSITE_MODES[i];
@@ -283,20 +274,22 @@ function _renderCompositeBtn() {
if (input) input.placeholder = meta.placeholder;
}
// Initialisiert den Button aus der geladenen Session (bei jedem Chat-Wechsel aufrufen).
// Plan-Modus wird immer auf 'off' zurückgesetzt (transient).
function initCompositeMode(panelMode, hasAgents) {
// Initialisiert den Button bei jedem Chat-Wechsel. ALLE Modi (Plan/Ultracode/Fusion/MoA) sind
// transient (pro Anfrage) → der Button startet immer auf 'off'. `hasAgents` steuert nur den Hinweis,
// ob für Fusion/MoA überhaupt Panel-Agenten konfiguriert sind.
function initCompositeMode(hasAgents) {
_compositeHasAgents = !!hasAgents;
_compositeMode = (panelMode === 'fusion' || panelMode === 'moa') ? panelMode : 'off';
_compositeMode = 'off';
_planModeActive = false;
_ultracodeActive = false;
_renderCompositeBtn();
}
// Setzt den Modus direkt (aus dem Menü gewählt).
// Persistiert das EFFEKTIVE Panel der Session (off/fusion/moa): für plan/ultracode also 'off',
// damit kein zuvor gewähltes Panel nebenherläuft. Plan/Ultracode selbst sind transient (Request-Flags).
async function setCompositeMode(mode) {
// Setzt den Modus direkt (aus dem Menü gewählt). ALLE Modi sind transient (Request-Flags) –
// nichts wird mehr persistiert. Plan/Ultracode/Fusion/MoA wirken nur für die nächste Anfrage
// und werden beim Chat-Wechsel auf 'off' zurückgesetzt. Die Panel-Mitglieder bleiben dagegen
// persistent (in den Session-Einstellungen festgelegt).
function setCompositeMode(mode) {
if (mode === _compositeMode) return; // schon aktiv → nichts tun
if (_compositeMeta(mode).mode !== mode) return; // unbekannter Modus → ignorieren
@@ -305,23 +298,9 @@ async function setCompositeMode(mode) {
_ultracodeActive = (mode === 'ultracode');
_renderCompositeBtn();
// Effektiver Panel-Modus: nur Fusion/MoA aktivieren ein Panel, alles andere 'off'.
// Auf die serialisierte Kette legen, damit aufeinanderfolgende Wahlen in Reihenfolge committen
// (letzte gewinnt) und sendMessage das offene PATCH abwarten kann.
var panelMode = (mode === 'fusion' || mode === 'moa') ? mode : 'off';
var sid = getCurrentSessionId();
if (sid) {
_modePersist = _modePersist
.then(function () { return apiUpdateSession(sid, { multiModelMode: panelMode }); })
.catch(function (e) {
console.error('Modus speichern fehlgeschlagen:', e);
if (typeof showToast === 'function') showToast('Modus konnte nicht gespeichert werden', 'error');
});
}
if (typeof showToast === 'function') {
if (mode === 'off') {
showToast('Modus: Aus', 'info');
showToast('Modus: Normal', 'info');
} else if (mode === 'plan') {
showToast('Plan-Modus aktiv', 'info');
} else if (mode === 'ultracode') {
@@ -669,12 +648,7 @@ 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, _ultracodeActive));
await _runStream((signal) => apiChat(content, getCurrentSessionId(), signal, _planModeActive, _ultracodeActive, _compositeMode));
}
// ── Steering (Einwurf in die laufende Chain) ──
@@ -710,8 +684,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));
await _runStream((signal) => apiChat(text, getCurrentSessionId(), signal, _planModeActive, _ultracodeActive, _compositeMode));
}
// ── Choice Click Handler ──