Automatisierungsvorschlaege nur mit n8n-Anbindung (#726)

An instance without n8n has no use for workflow proposals. New instance
setting automation.enabled ("this instance works with n8n"), off by default:
- off: automation_find/automation_propose refuse, no candidates are put to
  agents, and the escalation tool does not mention proposals at all;
- the switch sits in the settings card for external MCP servers; the
  approvals view says proposals are off instead of standing empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-10-01 20:29:19 +02:00
co-authored by Claude Opus 5.5
parent 184a507e3d
commit a17ac2957f
12 changed files with 124 additions and 4 deletions
@@ -698,6 +698,9 @@
"staging.rejected": "Aufruf wurde abgelehnt.",
"staging.rejectFailed": "Ablehnen fehlgeschlagen: {error}",
"staging.withLead": "Die Leitung {lead} darf dies bis {until} entscheiden, im Rahmen dessen, was ihre Einheit erlaubt. Du kannst vorher entscheiden; danach liegt es allein bei dir.",
"automation.disabled": "Diese Instanz arbeitet nicht mit n8n, deshalb schlagen Agenten keine Workflows vor. Schalte die n8n-Anbindung in den Einstellungen (Externe MCP-Server) ein, wenn du n8n betreibst.",
"mcpServers.n8n": "Diese Instanz arbeitet mit n8n",
"mcpServers.n8nHint": "Nur dann schlagen Agenten Workflows für wiederkehrende Routine vor, und nur dann werden ihnen solche Aufgaben vorgelegt. Aus: keine Vorschläge, keine Kandidaten.",
"automation.title": "Automatisierungsvorschläge",
"automation.hint": "Agenten schlagen n8n-Workflows für Routine vor, die sie immer wieder gleich erledigen. Lies, was der Workflow tut — jeden Knoten und jede Adresse, die er erreicht. Nimmst du an, importierst du das JSON von Hand in n8n, testest es und gibst es als MCP-Werkzeug frei; einen Knopf, der das für dich tut, gibt es mit Absicht nicht. Eine Ablehnung behält ihren Grund.",
"automation.none": "Kein Vorschlag wartet auf dich.",
@@ -698,6 +698,9 @@
"staging.rejected": "Call was rejected.",
"staging.rejectFailed": "Rejection failed: {error}",
"staging.withLead": "Its lead {lead} may decide this until {until}, within the limits its unit grants. You can decide it first; after that it is yours alone.",
"automation.disabled": "This instance does not work with n8n, so agents propose no workflows. Switch the n8n connection on in the settings (external MCP servers) if you run n8n.",
"mcpServers.n8n": "This instance works with n8n",
"mcpServers.n8nHint": "Only then do agents propose workflows for recurring routine, and only then are such tasks put to them. Off: no proposals, no candidates.",
"automation.title": "Automation proposals",
"automation.hint": "Agents propose n8n workflows for routine they do the same way again and again. Read what the workflow does — every node and every address it reaches. If you accept, import the JSON into n8n by hand, test it, and release it as an MCP tool; there is deliberately no button that does it for you. A rejection keeps its reason.",
"automation.none": "No proposal waits for you.",
@@ -86,8 +86,23 @@ export function erstelleMcpServerKarte() {
lade();
// #726: whether the instance works with n8n — only then do agents propose workflows.
const n8n = erstelleElement("input", { type: "checkbox" });
api("/staging/automations/settings").then((s) => { n8n.checked = !!s?.enabled; }).catch(() => {});
n8n.addEventListener("change", async () => {
try {
const r = await api("/staging/automations/settings", { method: "PUT", body: { enabled: n8n.checked } });
zeigeMeldung(r?.message ?? t("common.saved"), "erfolg", 6000);
} catch (err) {
n8n.checked = !n8n.checked;
zeigeMeldung(err.message, "gefahr", 8000);
}
});
return erstelleKarte(t("mcpServers.title"), [
erstelleElement("p", { klasse: "gedaempft text-klein", text: t("mcpServers.hint") }),
erstelleElement("label", { klasse: "gruppe-horizontal" }, n8n, erstelleElement("span", { text: t("mcpServers.n8n") })),
erstelleElement("p", { klasse: "gedaempft text-klein", text: t("mcpServers.n8nHint") }),
liste,
erstelleElement("h4", { text: t("mcpServers.add") }),
erstelleElement("div", { klasse: "formular-raster" },
@@ -431,6 +431,13 @@ export function erstelleAutomationsKarte() {
const box = erstelleElement("div", { klasse: "automationen" });
async function lade() {
// Without n8n there are no proposals — the card says so instead of standing empty.
let einstellung = null;
try { einstellung = await api("/staging/automations/settings"); } catch { einstellung = null; }
if (!einstellung?.enabled) {
box.replaceChildren(erstelleElement("p", { klasse: "gedaempft text-klein", text: t("automation.disabled") }));
return;
}
let liste = [];
try { liste = (await api("/staging/automations?status=proposed")) || []; } catch { liste = []; }
if (!liste.length) {