29d953a5eb8be34beea4faafa781be0332fc1d39
68
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
29d953a5eb |
Brett je Einheit, Stufe 3: Aufgaben sehen, wer sie sehen soll (#684)
- TaskBoard: das Brett einer Aufgabe wird nicht gespeichert, es folgt aus der Organisation - Einheit des Zustaendigen, sonst des Anlegers. Offene Aufgaben ziehen damit beim Umzug mit. Sichtbar: das eigene Brett, fuer eine Leitung alle darunter, und was man selbst angelegt hat (nach oben Gereichtes bleibt im Blick). - TaskItem.CreatedBy, in der Datei als created_by und in der DB. Ein Import aus einer Datei ohne das Feld behaelt, was die DB weiss. - Taskboard-Tool: task_list, task_find und die Dublettenpruefung sehen nur die eigenen Bretter - eine aehnliche Buchhaltungsaufgabe haelt keine Marketingaufgabe auf und bekommt keinen Kommentar von dort. task_update und task_comment antworten auf eine fremde Aufgabe wie auf eine unbekannte. - TaskboardService.ListAsync mit Sichtfilter: erst filtern, dann kappen. - WebUI: jede Aufgabe nennt ihr Brett, ein Filter waehlt eines aus. - Konzept (Abschnitt 3) und Einrichtungshandbuch nachgetragen. Ohne Einheiten aendert sich nichts - ein Brett fuer alle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
609298dd7a |
Ablage je Einheit, Stufe 2: Verwaltung und Organisationsseite (#684)
- OrgAdministration: eine neue Einheit bekommt ihren Bereich (Mitglieder: die Einheit samt Untereinheiten, schreiben); reservierte Namen und belegte Bereichs-Ids werden als Einheiten-Id abgelehnt. Uebergreifende Bereiche anlegen (mit Eigentuemer), Name/Zweck/geschuetzte Ordner aendern, Mitgliederliste ersetzen, nur leere Bereiche loeschen, nie den eigenen einer Einheit. Eine geloeschte Einheit verlaesst alle Mitgliederlisten; ihr Bereich geht nur, wenn er leer ist, sonst bleibt er ohne Eigentuemer. Nachruesten des eigenen Bereichs fuer Einheiten von vor #684. Alles im AuditLog. - API unter /api/organisation/workspaces; die Uebersicht liefert je Bereich Mitglieder, Dateizahl und - von der Instanz berechnet - wer hineinkommt. - Organisationsseite: Bereiche unter der Struktur, Bearbeitung mit Mitglieder-Editor, "Wer hineinkommt", Bereichskarte an der Einheit, Bereiche am Agenten. Sprachschluessel en/de. - Die Nachschau nach einem Absturz bekommt die Organisation mit, sonst fände sie einen Aufruf in einem benannten Bereich nicht. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ff20ce2563 |
Ablage je Einheit, Stufe 1: benannte Arbeitsbereiche mit Mitgliedern (#684)
- WorkspaceConfig in der Instanzkonfiguration: Id, Name, Zweck, Eigentuemer-Einheit, Mitglieder (Einheit oder Agent, Stufe Read/Write/ Manage, Untereinheiten ein/aus), geschuetzte Pfade. Das Recht steht am Bereich, nicht am Agenten. - OrgChart: AccessTo, WorkspacesFor, UnitWorkspace; Problems nennt ungueltige Ids und tote Verweise. WorkspaceLayout: Bereiche liegen unter Agents/SharedWorkspaces/<id>/, neben dem unveraenderten SharedWorkspace. - FileRW: workspace ist ein Name - personal, shared (wie bisher ueber sharedAccessLevel), unit (eigene Einheit) oder eine Bereichs-Id. Ein unbekannter und ein nicht erlaubter Bereich bekommen dieselbe Antwort. Loeschen braucht Manage wie frueher Admin. Der #662-Hinweis sucht in allen erlaubten Bereichen. - Mit Einheiten ist shared:/tasks/ fuer FileRW gesperrt: Aufgaben laufen ueber das Brett, sonst umginge eine direkt geschriebene Datei Brett- und Adressbuchpruefung. Ohne Einheiten unveraendert. - Organisationsabschnitt im Prompt nennt die Bereiche mit Stufe und Zweck. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b2b8a34d9f |
Lagebild: Einheiten als verbundene Karten, Strukturbaum mit Suche und Filter (#711)
Rueckmeldung zur Teamuebersicht: wirkte veraltet, und die Verbindungen zwischen den Einheiten fehlten - zugeklappte Container versteckten ganze Ebenen, man haette sich bis zum Problem durchklicken muessen. - Jede Einheit ist immer eine Karte, verbunden als Organisationsbaum (ELK layered, orthogonale Linien, eckige Anschluesse). Keine Ebene wird versteckt. Zugeklappt wird nur die Agentenliste grosser Einheiten: sie zeigt, wer Aufmerksamkeit braucht, und "+n weitere". - Eine Linie traegt die Farbe des dringlichsten Zustands darunter - eine rote Linie fuehrt von oben zum Problem. - Links ein Strukturbaum mit Suche (Agent, Einheit, Modell) und Zustandsfilter; Treffer werden hervorgehoben, der Weg dorthin geoeffnet, der Rest abgeblendet. Enter springt zum ersten Treffer. - Leinwand mit Punktraster, Verschieben und Zoomen, schwebende Leisten (Live-Verbindungen, Einpassen, Zoom). Live-Verbindungen laufen von Zeile zu Zeile, gebuendelt mit Zaehler. - Hausstil bleibt (Radius 0, App-Akzent), hell und dunkel, bis Handybreite. - Cytoscape und cytoscape-elk entfernt: ein Canvas-Knoten kann keine Agentenliste, Suche oder den Hausstil tragen. Karten sind jetzt HTML. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
920d3fed64 |
Lagebild, Stufe 1: Einheiten als aufklappbare Container (#711)
- TeamOverview.RollUp: je Einheit die Zustaende aller Agenten darin und
darunter, offene Freigaben und der dringlichste Zustand (Urgency:
haengt > Problem > Freigabe > Budget > arbeitet > frei). Gezaehlt wird in
der Instanz, die Seite zeichnet nur.
- /api/team liefert Einheiten und je Agent Einheit und Leitung.
- team.js: Einheiten als Container, zugeklappt als Kasten mit
Zusammenfassung ("6 agents · ⚠ 1 · ✋ 1 · ▶ 2") in der Farbe des
dringlichsten Zustands. Kanten laufen zum sichtbaren Stellvertreter und
werden gebuendelt (×n); was in einer zugeklappten Einheit bleibt, wird
nicht gezeichnet. Bis 15 Agenten alles offen, darueber nur die oberste
Ebene; der Zustand je Instanz im localStorage.
- Seitenleiste fuer Einheiten: Pfad, Ziel, Mitglieder (Leitung zuerst),
Untereinheiten, Auf-/Zuklappen, Link zur Organisation.
- Layout: rectpacking trifft das Seitenverhaeltnis nur grob; es werden
bis zu vier Vorgaben probiert und die zur Flaeche passendste behalten.
- Sprachschluessel en/de.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
5b8656d969 |
Tests: Verbindungspools nur der eigenen Datenbank leeren statt aller
Die zufaelligen roten Laeufe der vollen Suite (jedes Mal ein anderer Test, zuletzt PromptHistoryTests mit einer reinen Funktion) hatten eine Ursache: Zwanzig Testklassen riefen beim Aufraeumen SqliteConnection.ClearAllPools() auf. Das ist prozessweit und nahm einem parallel laufenden Test die Verbindung mitten im Oeffnen weg - ObjectDisposedException in ApplyPragmas. - SqliteStorage.ReleasePooledConnections(pfad) leert nur die Pools der Datenbanken unter einer Datei oder einem Verzeichnis. - BackupService oeffnet die Quelle fuer VACUUM INTO ohne Pool: eine einmalige Kopie soll die Datei danach nicht offen halten. - PoolReleaseTests: die freigegebene Datenbank laesst sich loeschen, die andere arbeitet weiter. Vier volle Laeufe hintereinander gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e4ecf2d5ca |
Einheiten, Stufe 4: API und WebUI-Seite "Organisation" (#683)
- /api/organisation: Baum (Einheiten mit Pfad, Tiefe, eigener und wirksamer Obergrenze, Vorgaben; Agenten mit Einheit, Leitung, effektiver Leitung, Werkzeugen, Budget), Probleme der Struktur, bekannte Werkzeuge. Anlegen, Aendern, Loeschen, Leitung, Umzug von Agent und Einheit, Obergrenze, Sammelwerte mit Vorschau. Umzug und Obergrenze kennen preview=true; die Entscheidung trifft allein OrgAdministration. - Neue Agenten koennen ueber die API gleich in eine Einheit gelegt werden. - WebUI #/organisation: links der Baum (oberste Ebene, Einheiten, Leitungen mit Stern, Mitglieder), rechts die Bearbeitung. Eine Begruendung fuer das Audit gilt fuer jede Aenderung. Wo etwas verloren gehen kann, zuerst die Vorschau: laufende Agenten und Probleme halten an, wegfallende Werkzeuge muessen in einem Dialog bestaetigt werden. Sammelwerte als Tabelle mit Ausreissern markiert und abgewaehlt. Bei aktivierter Begrenzung ist vorausgewaehlt, was von oben ohnehin erlaubt ist. Sprachpakete en/de. Geprueft im Browser mit einem Pruefgestell (API durch Attrappe ersetzt): Baum, Auswahl, Obergrenze mit Bestaetigungsdialog, Sammelwert-Vorschau mit Ausreisser und das Schreiben nur der gewaehlten Agenten. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
68a432fec6 |
Einheiten, Stufe 3: Verwaltung mit Umzug, Rechte-Obergrenze und Sammelwerten (#683)
OrgAdministration (App), damit WebUI und Desktop dieselben Regeln anwenden: - Einheiten anlegen, umbenennen, Ziel setzen, loeschen (nur leer); Leitung setzen (nur ein Mitglied). - Agent umziehen: haelt fest, seit wann und woher (UnitSinceUtc, PreviousUnitId); eine Leitung, die geht, laesst ihre Einheit ohne Leitung. Einheit umziehen: nie unter sich selbst; die Mitglieder bleiben in ihrer Einheit, ihre Position aendert sich, und das wird ihnen gesagt. - Kein Umzug unter einem laufenden Agenten. Das Konzept sagt "bei gestoppter Instanz", damit niemand mitten im Lauf andere Rechte bekommt. Die Verwaltung laeuft in der Instanz und kann sie nicht stoppen; sie lehnt ab, solange ein betroffener Agent laeuft. Das dient demselben Zweck, nur enger. - Rechte-Obergrenze: beim Schreiben geprueft (SetToolEnabled, SetToolSettings, und als Sicherheitsnetz Save() fuer den Desktop, der Werkzeuge direkt umschaltet). Obergrenze senken und in eine Obergrenze umziehen zeigen in der Vorschau, welche Werkzeuge wegfallen, und brauchen dafuer eine ausdrueckliche Bestaetigung. - Sammelwerte (Tagesbudget, Tokens, Modell, whenExhausted) mit Vorschau fuer die Einheit samt Untereinheiten: ein Wert je Agent oder ein verteilter Gesamtbetrag im Verhaeltnis der bisherigen Werte. Ausreisser (Faktor 3 zum Median der anderen) sind markiert und standardmaessig nicht gewaehlt. Geschrieben wird nur, was gewaehlt ist; die Einheit merkt sich den Wert nur als Vorgabe fuer neue Mitglieder. - Ein neuer Agent, der direkt in eine Einheit gelegt wird, bekommt deren Vorgaben; ein umgezogener behaelt seine Werte. - Jede Strukturaenderung steht mit Begruendung im Audit (Quelle "admin", Werkzeug "Organisation"). Der Organisationsabschnitt nennt der Leitung jetzt auch, wer die Einheit kuerzlich verlassen hat und wohin: Die abgebende Leitung wird unterrichtet, nicht nur die aufnehmende. Tests: OrgAdministrationTests (16). 1.333 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
626fc8d593 |
Einheiten, Stufe 2: das Adressbuch durchsetzen und jedem Agenten seinen Platz sagen (#683)
Durchgesetzt vom Code, nicht nur im Prompt beschrieben: - Nachricht und Spawn an einen Agenten ausserhalb des Adressbuchs werden vor jedem Modellaufruf abgelehnt; die Antwort nennt den offenen Weg (die eigene Leitung, sonst @human). - list_agents zeigt nur noch das Adressbuch. - task_create und task_update pruefen die Zuweisung an einen Agenten gegen dasselbe Adressbuch; sonst liesse es sich ueber eine Aufgabe umgehen. - Die Delegationstiefe folgt der Tiefe des Baums (Tiefe + 1), der eingestellte Wert bleibt Untergrenze. Unterrichtet werden, wie das Konzept verlangt: - OrgBriefing erzeugt einen Abschnitt "# Organisation" im Systemprompt: Einheit mit Pfad und Ziel, seit wann (mit Hinweis, wenn frisch), die effektive Leitung oder "@human", als Leitung die Untereinheiten und neue Mitglieder, und wen man sonst erreicht. Er ist immer wahr, weil er aus dem Baum kommt; ein Umzug aendert ihn von selbst. - In einem laufenden Chat wird nur dieser Abschnitt ersetzt; der uebrige Systemprompt bleibt, wie er war (#193). - Ohne Einheiten gibt es keinen Abschnitt und keine Einschraenkung. Bestehende Instanzen behalten ihren Prompt und ihren Prompt-Cache. AppHost setzt den Organisationsbaum als Provider (frisch je Frage, eine Aenderung gilt ab dem naechsten Lauf) und protokolliert Probleme der Struktur beim Start. Tests: AddressBookEnforcementTests (10), AssignmentReachTests (4). 1.317 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d014a20c38 |
Einheiten, Stufe 1: Organisationsbaum und Adressbuch als reine Rechnung (#683)
- UnitConfig (Id, Name, ParentId, LeaderAgentId, Goal, ToolCeiling, Defaults) in der Instanzkonfiguration neben den Agenten; AgentConfig.UnitId und UnitSinceUtc. Bewusst Konfiguration statt Datenbank: Die Werkzeugrechte der Agenten stehen schon dort, die Wahrheit ueber einen Agenten bleibt an einem Ort. Strukturaenderungen gehen ins Audit (Stufe 3). - OrgChart: Pfade, Tiefe, Leitung, effektive Leitung (die naechste Leitung darueber, wenn die eigene Einheit keine hat oder man sie selbst leitet), Adressbuch, Delegationstiefe aus dem Baum (Tiefe + 1, der eingestellte Wert bleibt Untergrenze), Rechte-Obergrenze als Schnittmenge entlang des Pfades, und eine Liste der Probleme (unbekannte Verweise, Zyklen, Leitung ist kein Mitglied, Werkzeuge ueber der Obergrenze). - Adressbuch: eigene Einheit und effektive Leitung; als Leitung die Leitungen der Untereinheiten (bei leitungslosen Untereinheiten deren Mitglieder), der Geschwistereinheiten und die eigene Leitung; die oberste Ebene und die Leitungen der obersten Einheiten sehen sich gegenseitig. Jede Linie ist in beide Richtungen begehbar. Ohne Einheiten sieht jeder jeden, wie bisher. Tests: OrgChartTests (20) am Onlinehandel-Beispiel aus dem Konzept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
f627918729 |
Lagebild, Stufe 0: Teamuebersicht in der WebUI (#711)
Wer arbeitet, wer hat in den letzten Sekunden wen erreicht, und wo klemmt es, auf einen Blick. Stufe 0 kommt ohne Einheiten aus; mit #683 werden die Agenten zu aufklappbaren Containern, und die Ansicht faellt mit #688 zusammen. Kern: - AgentActivity: fuehrt die laufenden Laeufe (Beginn, Quelle, Aufgabe, Aufrufer) und die letzten Verbindungen (begrenzt auf 500), meldet Start, Ende und Verbindung als Ereignis. Vorher war nur ein Lauf ueber ChatAsync "laufend"; ein frischer Lauf (@new) war unsichtbar, und niemand hielt fest, wer wen gerufen hatte. - Die Engine meldet beide Einstiege an; "arbeitet" beginnt hinter dem Agententor. Anwendung: - TeamOverview stellt den Lagebericht zusammen. Die Zustaende entscheidet der Code aus erfassten Tatsachen: laufender Lauf, offene Freigabe, Status des letzten Belegs, Laufzeit gegen den Median des Agenten (haengt ab dem Dreifachen, fruehestens nach 5 Minuten, ohne Verlauf nach 15). Rangfolge: haengt, wartet auf Freigabe, arbeitet; Problem und Budget nur, solange nichts Neues laeuft. Web: - GET /api/team, Ereignis "activity" (started, finished, link); das Ereignis "run" traegt jetzt die Agenten-Id. - Ansicht #/team mit Cytoscape.js und ELK (bei Bedarf geladen): Layout aus der Struktur statt von Hand, Farbe und Symbol je Zustand, Kanten verblassen nach 30 s, Klick oeffnet eine Seitenleiste (laufende Laeufe, Freigaben, letzter Lauf im Wortlaut) mit "Freigaben oeffnen" vorne, danach Chat und Laeufe. Sprachpakete en/de. - Ein Layout, das lief, solange die Flaeche noch 0 px breit war, wird neu gerechnet, sobald sie eine Breite hat (im Pruefgestell gefunden). Geprueft im Browser mit einem Pruefgestell (API und Ereignisstrom durch Attrappen ersetzt): sieben Agenten in allen Zustaenden, Seitenleiste, Live-Kante und Verblassen. Tests: AgentActivityTests (8), TeamOverviewTests (14). 1.283 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
991a901cab |
WebUI: Cytoscape.js, ELK und cytoscape-elk eingecheckt (#711)
Fuer die Teamuebersicht (Lagebild, #711), auf Freigabe des Nutzers. Als Dateien im wwwroot statt ueber ein CDN, weil die Zielmaschine womoeglich kein Internet hat (WebUI-Leitfaden). - cytoscape 3.34.3 (MIT), elkjs 0.9.3 (EPL-2.0), cytoscape-elk 2.3.0 (MIT) - aus der npm-Registry, Tarballs gegen dist.integrity (SHA-512) geprueft - elkjs bewusst 0.9.3: cytoscape-elk 2.3.0 verlangt ^0.9.3 - Lizenztexte daneben, vendor/README.md mit Version, Herkunft und SHA-256 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
32383119da |
Budgetende, Stufe 4: den Engpass melden, bevor er eintritt (#716)
- TaskSchedule.OccurrenceTimesBetween liefert die Zeitpunkte der restlichen Laeufe; OccurrencesBetween zaehlt sie nur noch. - BudgetHeadroom implementiert IBudgetOutlook: Die restlichen geplanten Laeufe des Tages werden in zeitlicher Reihenfolge durchgegangen, je Agent mit Grenze und fuer die Instanz, bis zum ersten Lauf, der das Budget verbraucht vorfaende. Das spiegelt die Pruefung beim Start eines Laufs. Spaetwerk zaehlt nicht mit, es laeuft ohnehin nur im Spielraum. - Der Scanner schaut alle 30 Minuten voraus und nennt je Agent und Tag einmal, wann das Budget ausgeht und wie viele der geplanten Laeufe dann ausfallen, solange noch Zeit ist, nachzulegen oder zu entscheiden, was wartet. Die Meldung steht vorerst im Protokoll (Warnung). Der Weg zum Menschen ueber einen Kanal gehoert zu #646, die Anzeige ins Lagebild (#711). Tests: 4 neu fuer die Vorschau. 1.261 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9cf9a2cfc2 |
Budgetende, Stufe 3: Nachholen nur im Spielraum des Tages, keine Kappung bei 500 (#716)
- TaskSchedule.OccurrencesBetween zaehlt, wie oft eine Aufgabe bis zu einem Zeitpunkt noch laeuft, einschliesslich eines jetzt faelligen Laufs. - BudgetHeadroom (ILateRunGate): Ein mitgenommener oder nachzuholender Lauf startet nur, wenn er in den Spielraum passt: Grenze minus Verbrauch minus Reservierung minus Bedarf der restlichen geplanten Laeufe bis Mitternacht minus 10 % Reserve. Das gilt je Agent und je Instanz, fuer Kosten und Tokens. Anderes Spaetwerk zaehlt nicht als Bedarf, sonst blockierten sich mitgenommene Aufgaben gegenseitig. - Der Scanner fragt den Spielraum nur fuer Spaetwerk und protokolliert, wenn es warten muss. Die Engine gibt ihren BudgetGuard heraus, damit Scanner und Engine dieselben Reservierungen sehen. - Ersetzt den pauschalen 25-%-Anteil aus dem Konzept (dort nachgezogen): Ein fester Anteil ist an vollen Tagen zu viel, an ruhigen zu wenig. Drei weitere stille Kappungen bei 500 beseitigt (ListAllAsync, ungekappt): Kostenprognose (Aufgaben ueber 500 fehlten), Waisenabgleich beim Import und task_find aus #674, wo Dubletten unter aelteren Aufgaben mit niedriger Prioritaet unsichtbar wurden. Tests: BudgetHeadroomTests (9), Scanner-Pforte (1), Dublette hinter 500 Aufgaben (1). 1.257 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8a83a67fb4 |
Budgetende, Stufe 2: laufende Laeufe reservieren ihr Budget (#716)
Der offene Rest aus #682: Jeder Lauf pruefte das Budget einzeln gegen den bisher erfassten Verbrauch. Laufende Laeufe waren unsichtbar, gleichzeitig startende passierten die Pruefung gemeinsam und ueberzogen gemeinsam. - BudgetGuard.ReserveAsync prueft und reserviert unter einer Sperre. Die Reservierung ist der Mittelwert der letzten 20 Laeufe des Agenten (Laeufe ohne Verbrauch zaehlen nicht mit) und gilt, bis der Verbrauch des Laufs erfasst ist. - Gegen die Grenze zaehlt: verbraucht + in laufenden Laeufen reserviert. Die eigene Schaetzung eines Laufs zaehlt bewusst nicht mit; ein einzelner Lauf wird weiter nur abgelehnt, wenn das Budget schon verbraucht ist. Sonst fiele eine kleine Aufgabe wegen eines grossen Durchschnitts aus. - Gilt fuer Kosten- und Tokengrenze, je Agent und je Instanz. - Die Ablehnungsmeldung nennt den reservierten Anteil, sonst wirkte "0,60 von 1,00 USD verbraucht" als Ablehnungsgrund falsch. - IUsageRepository.GetRecentForAgentAsync, mit leerer Vorgabe im Interface, damit bestehende Attrappen unberuehrt bleiben. Tests: BudgetReservationTests (8 neu), darunter zehn gleichzeitige Starts, von denen genau vier passieren. 1.246 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5f3a85595b |
Budgetende, Stufe 1: Ausfall vermerken, Regel je Agent, mitnehmen statt anrennen (#716)
Bisher teilte sich eine Budgetablehnung den Ausgang NichtGelaufen mit dem Herunterfahren. Einmalige Aufgaben wurden den Rest des Tages alle 30 Minuten erneut abgelehnt (jedes Mal ein Beleg) und liefen nach Mitternacht still zu Lasten des neuen Tages. Wiederkehrende verfielen still. - TaskLauf.KeinBudget als eigener Ausgang; der Dispatcher bildet BudgetExceeded darauf ab. - Vier Felder an der Aufgabe: BudgetMissCount, LastBudgetMissAt, DeferredUntil, CatchUpPending (Spalten werden fuer bestehende Datenbanken nachgeruestet). - Einmalige Aufgaben werden mitgenommen: Der Termin bleibt erhalten, faellig ist die Aufgabe wieder ab der naechsten lokalen Mitternacht, nicht vorher. - Wiederkehrende Aufgaben folgen der Regel des Agenten (budget.whenExhausted): expire vermerkt den Ausfall und laesst den Termin verfallen; catchUpOnce fasst alle verpassten Termine zu einem Nachholauf am naechsten Tag zusammen. Ein regulaer faelliger Termin deckt das Nachholen mit ab, es bleibt bei einem Lauf. - Der neue Tag hat Vorfahrt: Mitgenommenes und Nachholaeufe kommen nach allem regulaer Faelligen, unabhaengig von der Prioritaet. - Beim ersten Takt eines neuen Tages eine Zusammenfassung je Agent im Log. - Sichtbar: Die Aufgabenkarte der WebUI zeigt Ausfaelle, Mitnahme und Nachholen; die Regel ist im Desktop je Agent einstellbar. Nebenbei sind "Next:"/"Last:" auf der Karte ins Sprachpaket gewandert. Stufen 2 bis 4 (Reservierung, Spielraum statt pauschalem Anteil, Vorbeugen) folgen. Tests: BudgetMissTests (9 neu). 1.238 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8df5d004e4 |
Scanner: Laeufe deckeln, stille Kappung bei 500 beseitigen (#682)
Drei Fehler im Taktgeber des Taskboards, zwei gemeldet, einer beim Bau gefunden: 1. Unbegrenzte Parallelitaet. Jede faellige Aufgabe startete sofort. Bei Cron-Terminen auf runden Zeiten hiess das: alle Agenten gleichzeitig, und alle Laeufe passierten die Budgetpruefung gemeinsam, weil sie je Lauf vor dem Start laeuft. Neu: maxParallelTaskRuns je Instanz (Vorgabe 4). Der Rest wartet, sortiert nach Prioritaet und dann nach Faelligkeit. 2. Stille Kappung. Die Kandidaten kamen ueber ListAsync mit Limit 500; darueber fielen Termine lautlos aus dem Takt. Neu: ListRunnableAsync liefert alle Aufgaben auf todo, ungekappt. Muessen Aufgaben warten, steht das im Log, allerdings nur, wenn sich die Zahl aendert. 3. Der Takt wartete auf jeden Lauf, den er startete. Ein zehnminuetiger Lauf hielt damit jeden anderen Termin zehn Minuten an, und mit einem Deckel haetten die freien Plaetze leer gestanden. Neu: Die Schleife startet und wartet nicht; DisposeAsync wartet stattdessen auf die laufenden Laeufe. Beansprucht wird nur, was sofort starten kann. Alles andere bleibt auf todo, sonst liefe beim Warten der Lease ab und die Aufgabe wuerde ein zweites Mal beansprucht. Ein Cron-Termin geht dadurch nicht verloren, weil DueOccurrence bis zum letzten Lauf zuruecksucht. Ein von Hand gestarteter Lauf wartet auf keinen Platz, belegt aber einen. Bleibt offen: Bis zu maxParallelTaskRuns Laeufe koennen die Budgetpruefung weiterhin gemeinsam passieren. Die Luecke ist damit begrenzt, aber nicht geschlossen; dafuer braeuchte es eine Reservierung vor dem Lauf. Tests: ScannerCapacityTests (9 neu). 1.229 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ad71682c2d |
i18n: Englisch als Hauptsprache, Sprachpakete fuer Desktop und WebUI (#661)
Umbau durch Gemini Flash, Durchsicht und Nacharbeit durch Claude. Umfang: - Desktop: LocalizationManager (JSON-Pakete, Rueckfall auf Englisch, Umschalten zur Laufzeit), Localize-Markup-Erweiterung, Sprachwahl in den Einstellungen (AppSettings.Language, Vorgabe "en"); 317 Schluessel je Sprache - WebUI: alle Ansichten auf t()/data-i18n, 656 Schluessel je Sprache - Agenten-Werkzeuge: Beschreibungen, Schemata und Meldungen auf Englisch - Tests an die englischen Meldungen angepasst, keine Pruefung abgeschwaecht Nacharbeit bei der Durchsicht: - ~25 Kommentarbloecke mit Begruendungen waren ersatzlos entfallen, darunter sicherheitsrelevante (NTFS-Datenstrom beim shared:-Praefix, Bremse gegen Agentenschleifen, doppelter POST beim Anlegen einer Instanz). Wieder eingesetzt: auf Englisch, wo die Datei umgestellt ist, sonst im Wortlaut. - Drei WebUI-Schluessel fehlten (logs.paused, instances.instanceOfflineError, instances.noInstanceActiveError). Weil t() bei fehlendem Schluessel den Schluessel selbst liefert, griff der "|| err.message"-Rueckfall nie — die Oberflaeche haette den Schluesselnamen angezeigt. Geprueft: Schluesselgleichheit en/de, Platzhalter je Schluessel, jeder im Code benutzte Schluessel vorhanden, node --check fuer alle Module, 1.220 Tests gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
060dc6a6ad |
FileRW list sortiert und gedeckelt (#673), Taskboard task_find (#674)
Die Betriebskontrolle im Testbetrieb meldete Stundendateien als fehlend, die es gab: 105 Eintraege in der Reihenfolge des Dateisystems, vierzeilig als eingeruecktes JSON. list liefert jetzt die 50 zuletzt geaenderten Eintraege, neueste zuerst, eine Zeile je Eintrag, mit Gesamtzahl im Kopf; pattern, sort, order und limit steuern das. Dabei aufgefallen (#675): Directory.GetFiles wertet Trennzeichen im Muster aus - pattern "../*" fuehrte an der Pfadpruefung vorbei aus dem Verzeichnis heraus, in list wie in read_many. Muster mit '/', '\' oder '..' werden jetzt abgewiesen. Und derselbe Befund ist keine zweite Aufgabe (#674): task_find beantwortet die Frage vor dem Anlegen, task_create legt bei gleichbedeutendem Titel einer offenen Aufgabe keine zweite an, sondern vermerkt das erneute Auftreten an der bestehenden. force=true geht trotzdem, Erledigtes blockiert nichts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
24e457b11f |
FileRW: Sammel-Lesen (#663) und Hinweis auf den anderen Workspace (#662)
#663: Neue Aktion read_many liest mehrere Dateien in einem Aufruf - entweder paths (Liste) oder path (Verzeichnis) mit pattern. Gedeckelt auf 50 Dateien und 200.000 Zeichen; was nicht mehr hineinpasst, wird namentlich genannt, damit der Agent gezielt nachholen kann. Anlass: Der Tagesabschluss im Testbetrieb las 24 Stundendateien einzeln und starb zweimal an der Schrittgrenze - die soll Endlosschleifen abfangen, nicht ehrliche Arbeit. #662: Schlaegt read oder list fehl, weil der Pfad im anderen Workspace liegt, sagt die Fehlermeldung das - aber nur, wenn der Agent diesen Workspace auch benutzen darf. Guenstige Modelle vergassen workspace "shared" regelmaessig und verbrauchten mit jedem Rateversuch einen Schritt. Dateipruefung, Endungen und Workspace-Grenze gelten in read_many unveraendert; eine verbotene Endung wird uebergangen statt den Lauf zu kippen. Version 0.1.14. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
996330a77e |
Prompt history: list preview shows the message as written, not the engine's time line (#660)
Version 0.1.13. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5adf4c1ca6 |
Prompt History und Verbrauchsauswertung (#660), englische Hauptsprache (#661)
Prompt history: every run records its transcript (task, each model step with tokens and cost, tool calls and results, engine follow-ups) - bounded in size, scrubbed of recognisable secrets, system prompts stored once per text. Retention in days per instance (default 14) and per agent (0 = never, -1 = forever); an hourly janitor deletes what is past it, "never" also purges. Usage analysis over run receipts: filter by period, agent, model, event, status, cost and free text; sort by every column; totals over the whole filter; hourly or daily series grouped by agent, model or event; markers for budget denials and model changes; run detail with receipt, transcript, tool calls and approvals. WebUI: Analysis > Token usage > Runs / Timeline, run detail, Prompt History with retention settings. New code, comments and texts in English; texts come from language packs (i18n/en.json, de.json) via modules/i18n.js. CLAUDE.md records English as the main language. Version 0.1.12. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
2f0361278b |
Freigaben buendeln statt Weckflut (#643), Freigabemodell ueber Werkzeugrechte (#641)
#643: Entscheidungen wecken den Agenten nur noch bei Ablehnung oder gescheiterter Ausfuehrung, gesammelt in einer Aufgabe je Agent (5 min Ruhefenster), frisch per @new:agent statt im fortgefuehrten Chatverlauf. Eine einmalige Aufgabe, deren Lauf wegen Budget oder Herunterfahren gar nicht stattfand, bleibt faellig (Wiedervorlage nach 30 min) statt mit gesetztem Marker liegenzubleiben. #641: Rechte stehen in der Werkzeugkonfiguration des Agenten (erlaubt/verboten). Erlaubtes laeuft, Nicht-Erlaubtes wird einzeln vorgelegt statt verweigert, Verbotenes gar nicht erst. "Gleichartiges kuenftig erlauben" erteilt eine widerrufbare Dauerfreigabe je Agent/Werkzeug/Operation; nach drei gleichartigen Freigaben schlaegt die Oberflaeche sie vor. Aenderungen an Agenten bleiben ausnahmslos freigabepflichtig. Database: Operation ist das SQL-Schluesselwort; die eigene Zugriffspruefung greift nur noch ohne Freigabestelle (AgentToolContext.FreigabeGeprueft). WebUI: Rechte-Tabelle je Werkzeug, Dauerfreigaben mit Widerruf. Version 0.1.11. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f74e49ba35 |
Freigabe-Hygiene (#631, #579, #609, #610) und JSON-Filter fuer WebFetch (#632)
#631: Derselbe Vorschlag (gleicher Agent, gleiches Werkzeug, gleiche Argumente unabhaengig von Schluesselreihenfolge) wird nicht erneut angelegt - der Agent erfaehrt die bestehende Nummer. Offene Vorschlaege verfallen nach 24 h (Status Expired). #609: Werkzeuge koennen den Freigabeschluessel aus dem Inhalt bestimmen (IAgentTool.StagingAktion). Database: SELECT/SHOW/DESCRIBE -> query, INSERT/UPDATE/DELETE -> write, DDL -> schema; write und schema sind freigabepflichtig. Ausgefuehrt wird nach Statement-Art, schreibende Statements melden die Zeilenzahl. #579: Database-Vorpruefung (Zugriffsebene, Tabellenfreigabe, SqlGuard) - was das Werkzeug ablehnen wuerde, kommt nicht mehr in die Freigabeliste. #610: SqlGuard ueberspringt IF [NOT] EXISTS und erkennt DESCRIBE/DESC <tabelle>. #632: WebFetch fetch mit filter liefert aus JSON/JSONP nur passende Eintraege; beim Blaettern durch grosses JSON empfiehlt der Hinweis den filter. Version 0.1.10. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f5731c50f8 |
Budgetsperre sichtbar machen (#608), Gedaechtnisabruf kuerzt statt wegzulassen (#607)
#608: Ein wegen erschoepften Budgets abgelehnter Lauf hinterlaesst jetzt einen Beleg (Status BudgetExceeded) an Aufgabe und Verlauf; die Log-Warnung kommt einmal je Agent, Grenze und Tag. Die Kostenprognose zaehlt Ablehnungen nicht als Lauf und warnt, wenn die geplanten Aufgaben eines Agenten sein Tagesbudget uebersteigen (API + Verbrauchsansicht). #607: Passt schon der erste Treffer nicht in den Abruf, wird er gekuerzt (Anfang und Ende bleiben) statt ganz zu fehlen. remember weist auf gleichnamige Eintraege ohne Schluessel und auf Eintraege nahe der Abrufgrenze hin. Version 0.1.9. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b0997b5334 |
FehlschlagWache: NUL-Trenner als Escape statt Rohbyte, damit Git die Datei als Text fuehrt (#581)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
779077a347 |
Kompaktierung: kalibriert pruefen, mit Abstand kompaktieren, Zusammenfassung verbuchen (#605)
Im Dauerbetrieb lief die Kompaktierung bei jedem Schritt an und blieb wirkungslos: Stufe 1 pruefte mit der rohen Schaetzung, kompaktierte nur knapp unter die Schwelle und brach dabei jedes Mal den Prompt-Cache; der Zusammenfassungs-Aufruf fehlte im Verbrauch. Ziel jetzt min(Schwelle, 55 % des Limits), Usage des Aufrufs geht in den Lauf. Version 0.1.8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7dbcc44226 |
WebFetch Stufe 1: Hauptinhalt als kompaktes Markdown, Abschnitte statt Abschneiden (#602)
- HtmlExtraktor (AngleSharp): Selektor > article (>= 40 % des Seitentexts) > main > Textdichte, Seitenrahmen und Stoerbloecke entfernt, Ueberschriften/Listen/Tabellen/Code als Markdown, Linkziele nur mit links=true, Metadaten (Titel, Sprache, kanonische URL, Datum) - WebInhalt: JSON/Text/XML unveraendert, Rueckfall auf Volltext bei zu wenig Hauptinhalt, mode=full, nextOffset fuer lange Inhalte, JSON kompakt ohne Unicode-Escapes - RSS: limit, Beschreibungen gekappt; dataAsOf nicht mehr aus dem Date-Header Gemessen: tagesschau-Artikel 16.000 (abgeschnitten) -> 9.635 Zeichen vollstaendig. Version 0.1.7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0b6ea73ec1 |
Entscheidungen aus dem Testbetrieb umgesetzt (#572): #577 #579 #581 #589 #193 #205 #594 #595 #597 #598 #599 #588
- Engine: leere Antworten nachfassen, Aufgaben enden mit Ergebniszeile (EmptyResponse/Incomplete), gleiche Fehlschlaege erkennen (RepeatedFailure), Zeitzeile vor Chatnachrichten, Werkzeugergebnisse gerahmt, Vorpruefung vor dem Staging, Belegkosten wie RunUsage - Staging: gescheiterte Freigaben als Failed; AgentEditor-Aenderungen freigabepflichtig, Selbstbearbeitung gesperrt (allowSelfEdit) - Taskboard: Termine beim Anlegen pruefen, naechster/letzter Lauf, pausieren, Sofortlauf, Kostenprognose, Hinweis auf wachsenden Kontext; Jobs-Reiter und /api/jobs entfernt - Release: Instanzmanager samt Weboberflaeche im Paket, setup.json fragt manager.json ab, Token nicht mehr ins systemd-Journal Version 0.1.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8ab9d99abe |
Cron: Bereich mit Schritt (7-22/3) lesen, ungueltige Ausdruecke im Scanner melden (#597)
Der Nachrichten-Digest im Dauerbetrieb der Test-VM lief nie: CronExpression verstand 'von-bis/schritt' nicht, TaskSchedule verschluckte den Fehler. Version 0.1.5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6581fd0187 |
Befunde aus dem Testbetrieb auf der Linux-VM behoben (#572)
- #578: Abgebrochene Laeufe (Schrittgrenze, Zeitueberschreitung, Abbruch, Ausnahme) tragen ihre bezahlten Token - vorher 0 in RunUsage, fuer das Budget unsichtbar. - #580: WebFetch prueft die aufgeloeste Zieladresse beim Verbinden; ein erlaubter Name, der auf 127.0.0.1 aufloest, wird gesperrt. - #575: Dateien entstehen unter Unix mit 0600 (AtomicFile, Protokolle, FileRW-Anhaengen, state.db), Protokollverzeichnisse mit 0700. - #576: SIGTERM/SIGINT beenden auch mit Fenster geordnet. - #593: OpenRouter-Statusdienst wird gestartet. - #589: Auftragsnachricht nennt aktuelle Zeit, Zeitzone und Termin. - #581: Unvollstaendiges Argument-JSON erreicht das Werkzeug nicht mehr; das Modell bekommt eine verstaendliche Rueckmeldung statt Parsertext. - #591: Weboberflaeche liest die Felder, die API und Ereignisstrom liefern (Freigaben, Protokoll, Taskboard mit Termin, Werkzeug-Einstellungen, Tokennutzung); Ereignisstrom in camelCase. - #592: Chat zeigt Nachrichten nicht mehr doppelt. - #536 (teilweise): A2A-Antworten tragen Absender und Empfaenger, Parser wirft nicht mehr; Datums- und Volltextfilter in Agenten-Chats. - deploy/publish.py: Packager-Pfad net10.0, Schalter --agent-file. - Version 0.1.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e2567a466f |
Sichtung offener Werkzeugaufrufe beim Start verdrahtet (#554)
AppHost.StartAsync ruft jetzt CrashRecovery. Ein geordnetes Ende hinterlaesst nichts; ist etwas da, ist die Instanz gestorben, waehrend Werkzeuge liefen. Bewusst nur sichten und protokollieren, nicht ausfuehren. Ob die Wirkung eines Aufrufs eintrat, weiss auch dieser Dienst nicht - ein automatisches "dann eben nochmal" versendete im Zweifel die Mail ein zweites Mal. Scheitert die Sichtung selbst, haelt sie den Start nicht auf. Eine Instanz, die wegen einer misslungenen Nachschau nicht hochkommt, waere schlimmer als eine, die mit einer Wissensluecke startet. Der Aufruf sitzt in StartAsync statt in BuildCore: BuildCore ist synchron, und die Signatur zu aendern haette durch alle Aufrufer gezogen. Blockendes Warten in einer Fensteranwendung waere die schlechtere Loesung gewesen. Build ohne Warnungen, 912 Tests, keine Regression. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b568a45de3 |
Wiederaufnahme nach Absturz, Nachschau und Auswertung (#553-#556)
#553 FileRW schreibt atomar. Die Vorkehrung lag seit #241 im selben Repository und wurde ueberall benutzt - Einstellungen, Sicherungen, Chat-Zustand, Identity.md - nur der Weg, ueber den Agenten ihre Arbeitsergebnisse ablegen, ging daran vorbei. Damit stand das Verhaeltnis auf dem Kopf: geschuetzt war das Eigene, ungeschuetzt das Ergebnis. append bleibt bewusst nicht atomar; das ginge nur, indem die ganze Datei neu geschrieben wird, und bei einer grossen Protokolldatei waere das der falsche Handel. #554 Wiederaufnahme. Neuer Eintrag AuditStatus.Started, geschrieben VOR der Wirkung und als einziger nicht "best effort" - auf ihn stuetzt sich alles Weitere, also laeuft der Aufruf gar nicht erst, wenn er nicht durchkommt. Neue Kennzeichnung IsRepeatable, getrennt von IsParallelSafe: Das eine heisst "stoert keinen gleichzeitigen Aufruf", das andere "richtet zweimal keinen Schaden an". CrashRecovery sichtet beim Start, was offen blieb, und stuft ein - fuehrt aber nichts aus. Es sichtet und berichtet; entscheiden soll der Aufrufer. Dabei kippte eine alte Regel: Bisher wurde ein Abbruch bewusst NICHT protokolliert. Das war richtig, solange es keinen Eintrag vor dem Aufruf gab. Jetzt saehe sonst jeder Benutzerabbruch beim naechsten Start aus wie ein Absturz - bis niemand mehr hinsieht. Deshalb AuditStatus.Cancelled. #555 Optionale Zustandspruefung IToolStateProbe, umgesetzt in FileRW. Unknown ist eine vollwertige Antwort, kein Fehlschlag: Ob ein SMTP-Server angenommen hat, laesst sich nicht feststellen, und ein Werkzeug, das hier raet, waere schlimmer als eines, das schweigt. Partial gilt ausdruecklich nicht als geklaert - einen halb angehaengten Text sollte niemand automatisch geraderuecken. #556 Auswertung. ListForAgentAsync und SummarizeToolUsageAsync. Die Dauer wird nur ueber abgeschlossene Aufrufe gemittelt; zaehlten die Started-Eintraege mit ihrer Dauer 0 mit, saehe jedes Werkzeug doppelt so schnell aus, wie es ist. Nebenbei: SqliteStorage kann jetzt Spalten nachruesten. CREATE TABLE IF NOT EXISTS allein genuegte nicht - bei einer bestehenden Datenbank tut die Anweisung nichts, und erst das naechste INSERT waere gescheitert, im Betrieb beim Anwender. Vier Audit-Tests angepasst: Ein ausgefuehrter Aufruf erzeugt jetzt zwei Eintraege statt einem. Ein Aufruf, der gar nicht erst laeuft - verweigert, unbekannt, vorgelegt - hat weiterhin genau einen; der Beginn wird erst nach Rechtepruefung und Staging geschrieben. 23 neue Tests, 912 gesamt, keine Regression. Build ohne Warnungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fb4b218e45 |
Abbruch hinterlaesst keinen unbeantworteten Werkzeugaufruf mehr (#552)
Wurde ein Chat abgebrochen, waehrend ein Werkzeug lief, blieb im Kontext eine assistant-Nachricht mit tool_calls stehen, auf die keine Antwort folgte. Diese Folge lehnt die Schnittstelle mit HTTP 400 ab - der Agent war danach nicht mehr ansprechbar. Der Schaden wurde dabei dauerhaft gemacht: Jeder catch-Block ruft AddChatEntry, das ruft PersistChatState, und das schreibt den Kontext atomar auf die Platte. Das atomare Schreiben war hier keine Rettung, sondern die Garantie, dass die kaputte Fassung heil ankommt. Ein Neustart half nicht, weil LoadPersistedChats sie zurueckholte. Ausloeser war durchweg Normalbetrieb: AbortChat durch den Benutzer, LoopGuard.Timeout, oder eine Instanz, die waehrend eines Werkzeuglaufs geordnet herunterfaehrt. Die Antworten gehen jetzt aus einem finally raus. Auf jedem Ausgangspfad bekommt jeder Aufruf seine Antwort, bei Abbruch eine Ersatzantwort, die auch sagt, was passiert ist - eine leere waere formal gueltig, liesse das Modell aber raetseln. Zwei Tests: einer auf die Form des Kontexts, einer auf das, worauf es ankommt - dass ein zweiter Chat danach wieder durchlaeuft. Der erste schlug vor der Behebung fehl. 543 Tests in Core, keine Regression. Build ohne Warnungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ea3844006f |
Harness gegen stillen Verlust gehaertet (#547-#551)
Fuenf Befunde mit einem gemeinsamen Fehlermodus: Keiner erzeugt eine Fehlermeldung. Der Lauf wird fertig, das Ergebnis sieht plausibel aus, und nur die Qualitaet ist schlechter. #547 Der Begleittext zum Werkzeugaufruf ging verloren. AssistantWithToolCalls baute eine neue Nachricht aus nur den Aufrufen; der Text, den das Modell daneben geschrieben hatte, fiel weg. Damit verlor das Modell zwischen zwei Schritten seinen eigenen Plan, und im Audit stand, dass ein Werkzeug lief, nicht warum. #548 finish_reason wurde nirgends gelesen. Eine an der Ausgabegrenze abgeschnittene Antwort galt als fertiges Ergebnis. Das trifft besonders Laeufe im Hintergrund, wo niemand zusieht. Neuer Status Truncated: Das Teilergebnis geht weiter, aber gekennzeichnet. #549 reasoning_details wurde verworfen. Bei Werkzeugnutzung muss der Denkverlauf unveraendert zurueck, sonst beginnt ein Thinking-Modell jedes Mal von vorn. Wird jetzt als rohes JSON durchgereicht - ohne Auslegung, weil Reihenfolge und Signatur daran haengen. #550 Die Anfrage verlangt jetzt einen Anbieter, der alle mitgeschickten Parameter beherrscht. Ohne das durfte ein Anbieter cache_control, reasoning oder sogar tools stillschweigend uebergehen - der Lauf lieferte dann eine gewoehnliche Textantwort, wo ein Werkzeugaufruf hingehoert haette. Dazu usage.include: der abgerechnete Betrag schlaegt die Eigenrechnung, die als Rueckfallebene bleibt. Denktiefe je Agent einstellbar, Vorgabe off. #551 Werkzeuge eines Zuges laufen gleichzeitig, sofern sie sich dafuer freigeben. Die Vorgabe von IsParallelSafe ist false, weil die falsche Antwort hier nicht auffaellt: Zwei schreibende Aufrufe auf derselben Datei scheitern nicht zuverlaessig, sondern gelegentlich. Freigegeben ist bisher nur WebFetch - es liest ausschliesslich. Die Reihenfolge der Ergebnisse folgt den Aufrufen, nicht der Fertigstellung; daran haengen die tool_call_id-Zuordnung und der Cache-Praefix. 27 neue Tests, 887 gesamt, keine Regression. Build ohne Warnungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fb185ca84d |
Sicherungen mit dem Lizenzschluessel verschluesselt, optional per FTP (#530)
Eine Sicherung des Managers ist jetzt als Ganzes mit dem Lizenzschluessel verschluesselt. Beim Zurueckspielen wird der eigene Schluessel probiert; passt er nicht, fragt die Oberflaeche nach dem Lizenzschluessel der Quellinstallation. Der Lizenzschluessel ist der eine Wert, den jede Installation ohnehin hat, und er liegt im Manager, nicht neben den Archiven. Damit faellt der Grund weg, aus dem die taegliche Sicherung bisher keine Zugangsdaten enthielt: Eine Passphrase haette gespeichert werden muessen, und neben den Sicherungen waere sie wirkungslos gewesen. Jetzt reisen die Zugangsdaten mit, und eine Sicherung uebersteht den Maschinenwechsel ohne Passphrase. LicenseBackupCipher: eigener Behaelter .clawdbak. Kopf mit Schluesselprobe, das Manifest als eigener Abschnitt, das ZIP in 1-MiB-Stuecken mit AES-256-GCM und Endmarke. PBKDF2 aus Lizenzschluessel und festem Salt, der Schluessel normiert. Kein RAR (braucht das lizenzpflichtige Herstellerprogramm), kein ZIP mit Kennwort (kann .NET nicht schreiben, ZipCrypto ist gebrochen, und mit abgeleitetem Schluessel braechte 7-Zip-Vertraeglichkeit nichts). Zurueckspielen: Die Schluesselprobe laeuft VOR dem Anhalten der Instanz. Passt der Schluessel nicht, antwortet der Endpunkt mit 422 und dem Code fremde_lizenz, und ein Fenster verlangt den Schluessel der Quelle. ManagerBackups: Gefuehrte Instanzen sichern sich nicht mehr selbst, weil nur der Manager den Schluessel hat; er liest ihre Einstellungen aus InstanceSettings.json. Einmal am Tag auch ueber einen Neustart des Managers hinweg. Ohne Lizenzschluessel wird nichts gesichert, auch nicht ersatzweise unverschluesselt. FTP (FluentFTP, schon im Loesungsbaum): TLS wo angeboten, requireTls erzwingt es; Upload unter .part-Namen und danach umbenannt; Aufraeumen auf dem Server je Instanz; ein gescheiterter Upload laesst die lokale Sicherung stehen. Das Kennwort liegt verschluesselt in manager.json und geht nie an die Oberflaeche. Welche Datei zu welcher Instanz gehoert, entscheidet ein Zeitstempel nach dem Namen und nicht das Praefix - Test und Test_2 werden nicht mehr verwechselt. Grenze, bewusst und dokumentiert (Konzept 9.10): Wer Lizenzschluessel und Archiv hat, hat die Zugangsdaten. Der Schluessel ist dem Hersteller bekannt und gilt fuer alle Instanzen einer Installation. Nicht ueber die Oberflaeche abgenommen: Fenster fuer die fremde Lizenz und FTP gegen einen echten Server stehen als Block H in #537. 860 Tests gruen, 0 Warnungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
99f8a89fd0 |
Schluesselbund: ein Hauptschluessel, mehrfach verpackt (#190)
SecretKeyStore.Wrap band den Hauptschluessel unter Windows per DPAPI ans Benutzerkonto und gab ihn sonst ungewrappt zurueck - eine Zeile, die man beim Lesen ueberspringt und die auf dem Zielsystem "im Klartext" bedeutet. Dahinter steht jetzt ein SecretKeyRing: derselbe Hauptschluessel, mehrfach verpackt, je Fach ein ISecretProvider (dpapi, passphrase, file). Fuer Aufrufer aendert sich nichts - GetOrCreateKey gibt weiterhin 32 Byte zurueck. Genau dafuer war die Naht da. WARUM MEHRERE FAECHER UND NICHT EIN BESSERES Ein Schluessel an der Maschine ueberlebt keinen Umzug; einer an einer Passphrase verlangt bei jedem Start jemanden, der tippt - und Dauerbetrieb ohne Menschen ist der Zweck (#214). Der Bund entscheidet sich nicht: Im Alltag oeffnet das Maschinenfach ohne Rueckfrage, nach einem Umzug die Passphrase, und beide fuehren zu DEMSELBEN Schluessel. Alle enc:v2-Werte bleiben lesbar. Und deshalb darf die Schluesseldatei jetzt ueberhaupt in eine Sicherung: Sobald ein Wiederherstellungsfach da ist, steht der Hauptschluessel in keiner Fassung im Klartext darin. Nicht zu verwechseln mit der Sicherung mit Passphrase: Die schluesselt jeden WERT einzeln ins Archiv um, fuer den Transport einer Instanz. Das Wiederherstellungs- fach schluesselt den HAUPTSCHLUESSEL um, damit eine bestehende Installation weiterlaeuft - unbeaufsichtigt ueber CLAWD_SECRET_PASSPHRASE. Die Unterscheidung steht in docs/Roadmap.md bei #190. Beide Altformate werden uebernommen: 32 rohe Byte werden zum Fach file, ein DPAPI-Block zum Fach dpapi. Derselbe Schluessel, nur anders verpackt. Zwei Zusagen, mit Tests festgehalten: - Geht KEIN Fach auf, wird abgebrochen statt ein neuer Schluessel erzeugt. Ein neuer Hauptschluessel machte jeden bestehenden Wert wortlos unlesbar. - Ein Umzug legt den Schluessel NICHT im Klartext ab, nur damit der naechste Start bequemer wird. Beim automatischen Weg wird nur hinzugefuegt, nie weggenommen; weggenommen wird nur in SetRecoveryPassphrase, wo ein Mensch gerade getippt hat. #190 bleibt offen: Unter Linux greift weiterhin das Fach file. Neu ist, dass es beim Start als Warnung im Protokoll steht statt unsichtbar zu sein. Womit das Maschinenfach dort gefuellt wird, ist Entscheidung #180 - diese Schnittstelle ist die Stelle, an der die Antwort eingesetzt wird. 834 Tests gruen, 0 Warnungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e3403087c2 |
Handschlag: --managed hebelt die Lizenzpruefung nicht mehr aus (#535)
Mit E-M3 wanderte die Lizenzpruefung in den Manager: Eine gefuehrte Instanz prueft nicht mehr selbst, weil der Manager es zentral getan hat. Woran sie "gefuehrt" erkannte, war der Schalter --managed - und ein Schalter ist keine Erkennung. ClawdDotNet --managed startete damit eine Instanz ganz ohne Lizenzpruefung. Vor E-M3 gab es die Luecke nicht; sie ist mit der Zentralisierung entstanden. ManagerHandshake unterschreibt Instanzverzeichnis und Ablauf mit HMAC-SHA256. An die Signierstelle kommt der Manager nur, wenn seine Lizenzpruefung bestanden hat - StartInstanceAsync weist vorher mit StartRefusal.NoLicense ab. Dass die Unterschrift existiert, ist der Beweis. - Gebunden an das Verzeichnis, und geprueft wird gegen das tatsaechliche, nicht gegen das behauptete: Sonst unterschriebe man das Argument statt der Sache. - Fuenf Minuten gueltig, der Ablauf geht in die Unterschrift ein. - Ueber die Umgebung statt ueber die Befehlszeile: Eine Befehlszeile steht in jeder Prozessliste. - Ungueltig heisst "nicht gefuehrt", nicht Abbruch. Dann prueft die Instanz ihre Lizenz selbst - der Weg von vor E-M3, also kein neuer Fehlerfall. AppHost.IsManaged ist jetzt der gepruefte Befund. CanSwitchInstance und der Wechseldialog haengen daran statt am Schalter. Die Grenze steht ausdruecklich im Konzept (9.9): Der Schluessel liegt in jeder ausgelieferten Programmdatei. Es geht um den gewoehnlichen Weg an der Lizenz vorbei, nicht um jemanden mit Werkzeugen und Willen. Abgenommen an echten Prozessen, Unterschrift fuer den Positivfall unabhaengig in Python gerechnet: ohne Unterschrift "Lizenz erforderlich"; mit gueltiger kommt die Instanz hoch; Unterschrift fuer ein fremdes Verzeichnis und hochgedrehter Ablauf scheitern beide. 820 Tests gruen, 0 Warnungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
dcffcdcff5 |
Instanzmanager: Weboberflaeche kennt Instanzen, geordnetes Ende, Puls (#528, #531)
Weboberflaeche (#531): X-Instanz an der einen fetch-Stelle, Instanzauswahl in der Kopfzeile, Verwaltungsansicht mit Anlegen/Starten/Anhalten/Fenster, Herkunft der Ereignisse, Sicherungs- und Einstellungsansicht. 402/409/502 werden eigen behandelt. Sicherungen im Manager (#530): Auflisten, Anlegen, Wiederherstellen, Loeschen. Das Zurueckspielen haelt die Instanz vorher an und startet sie danach wieder. Geordnetes Ende (Konzept 9.8). Zwei Befunde waren der Anlass: Der Manager konnte eine Instanz MIT FENSTER nie geordnet beenden. Control.ShutdownRequested war nur in HeadlessRun verdrahtet, nicht in der Avalonia-Anwendung - und ein Fenster ist die Vorgabe. Die Bitte wurde mit 202 beantwortet, niemand hoerte zu, und der Manager schoss den Prozess nach Ablauf seiner Frist jedes Mal ab. InstanceSupervisor.StopAsync hielt Socket-Schweigen fuer einen Todesnachweis und meldete "beendet", ohne den Prozess anzufassen. Bei einer verstummten Instanz ist der Socket von vornherein still - der Neustart haette einen zweiten Prozess auf dieselbe state.db gesetzt. Genau der Schaden, gegen den #528 gebaut ist. - ShutdownCoordinator: erst die Tuer schliessen, dann auf offene Arbeiten warten, Frist zwei Minuten. Vorher brach TaskScanner.DisposeAsync mitten in ProcessAsync ab, sodass die Aufgabe bis zum Lease-Ablauf auf "running" stehenblieb. - GET /api/instanz/puls hinter dem Socket-Gate: Phase und offene Arbeiten. Weiter gefragt statt gemeldet - ein Puls, den die Instanz von sich aus schickt, fehlt genau dann, wenn sie haengt. - Eskalationsleiter: drei Durchgaenge ohne Antwort, einmal um ein Ende bitten, nach 30s beenden und den Vollzug abwarten, gemeldet als Absturz. - EnsureDeadAsync vor jedem Start. Weiteres aus dem Review: - .gitignore verankert: "Instances/" passte auch auf src/ClawdDotNet.Core/Instances/. InstanceEndpoint.cs und InstanceLayout.cs waren dadurch nie eingecheckt. - Das WebUI-Token stand im Protokoll des Managers. Konsole ja, Datei nein. - Ein Klick auf "Instanz anlegen" loeste requestSubmit() zweimal aus. - [Obsolete] auf WebServerPort wieder entfernt: sechs Warnungen, die niemand abstellen konnte, solange E-M5 nicht fertig ist. - Die Wiederherstellung bricht nicht mehr ab, wenn der Browser weggeht. - Entwicklungspfade zum Instanzmanager aus Segmenten statt mit Rueckstrichen, und der Port kommt aus manager.json statt fest aus der Zeile. Offen und gemeldet: --managed hebelt die Lizenzpruefung aus (#535). 806 Tests gruen, 0 Warnungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7d4e8b3096 |
Autostart durch Manager, mit Sicherungsautomat (#528)
Entschieden vom Benutzer am 2026-09-08. Der Schalter heisst autostartByManager und steht in InstanceSettings.json - bei der Instanz, nicht beim Manager. ZWEI DINGE IN EINEM SCHALTER, weil es dieselbe Zusage ist: Der Manager startet diese Instanz, wenn er selbst hochkommt, und er startet sie wieder, wenn sie abstuerzt. Ohne den Schalter ruehrt er nichts an - ein Manager, der nach einem Neustart der Maschine alles hochfaehrt, was er findet, startet auch das, was jemand mit Absicht angehalten hat. Ausgeschaltet vorbelegt. DER SICHERUNGSAUTOMAT ist der eigentliche Inhalt. Wieder hochzufahren ist die einfache Haelfte; die schwierige ist zu erkennen, wann man es lassen muss. Nach drei Neustarts in zehn Minuten (maxRestartsInWindow, restartWindowMinutes) setzt der Manager aus und meldet es. Die Instanz bleibt dann aus, bis jemand sie von Hand startet - und dieser Start raeumt den Zaehler ab, denn wer von Hand startet, hat entweder etwas geaendert oder weiss, was er tut. Das Zeitfenster wandert: Eine Instanz, die einmal im Monat abstuerzt und wieder hochkommt, laeuft nie in die Grenze. Nur bei einem Absturz, nicht wenn eine Instanz verstummt. Eine stumme Instanz lebt noch; sie neu zu starten hiesse, sie erst abzuschiessen, und ob der Manager das von sich aus darf, ist nicht entschieden. Als offene Frage aufgeschrieben - genau dieser Zustand war der Anlass des Vorhabens. Der Schalter steht bei der Instanz, weil er eine Eigenschaft dieser Instanz ist und weil die Instanzdatei dem Instanzprozess gehoert: Sie vom Manager schreiben zu lassen, waehrend die Instanz laeuft, waeren zwei Schreiber auf einer Datei. Der Manager liest ihn nur; geaendert wird er auf der Einstellungsseite der Instanz. EIN DEFEKT, den erst diese Tests gezeigt haben: StartAsync gab "laeuft schon" zurueck, wenn die Aufsicht die Instanz fuer laufend hielt - ohne nachzusehen. Zwischen zwei Aufsichtsdurchgaengen liegen aber Sekunden, und in denen kann sie wegbrechen. Wer in dieser Luecke "starten" verlangte, bekam einen Erfolg, hinter dem nichts stand, und der naechste Durchgang meldete einen Absturz, den niemand mehr erwartete. Jetzt wird gefragt, bevor zugestimmt wird. UND EIN TESTDOUBLE, das die Wirklichkeit nicht abbildete: Der falsche Socket antwortete unabhaengig davon, ob ein Prozess lief. Damit war jeder Testaufbau, der die Antwort vor dem Hochfahren hinterlegte, stillschweigend ein anderer Test als beabsichtigt - eine Antwort sagt dem Manager "laeuft bereits", er uebernimmt, und der Starter wird nie angefasst. Dreimal passiert. Der falsche Socket haengt jetzt am falschen Starter. Sicherungen im Manager sind als #530 zurueckgestellt, mit Ausloeser. Vorab entschieden ist dort nur eines: Zurueckspielen ausschliesslich bei inaktiver Instanz. 19 neue Tests. Build ohne Warnungen, 793 Tests gruen, 8 uebersprungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
db7abf699f |
Instanzmanager Stufe 5: Wechseln heisst nach vorn holen (#528, E-M2)
Vorher hiess "Instanz wechseln": Der laufende Prozess faehrt sich herunter und startet sich mit einer anderen Instanz neu. Wer wechselte, beendete zwangslaeufig die Instanz, aus der heraus er es tat - und genau dabei hat sich die Anwendung am 2026-09-08 aufgehaengt (#529). Jetzt laufen alle, und es kommt eine nach vorn. Neu: POST /api/instanz/fenster, wieder ausschliesslich ueber den Socket. Die Instanz holt ihr Fenster SELBST nach vorn; der Manager kennt kein Fenstersystem und soll keines kennen. Kopflos laeuft die Bitte ins Leere, und das ist die richtige Antwort. Das letzte Wort hat der Fenstermanager - Windows laesst einen Hintergrundprozess den Vordergrund nicht einfach an sich reissen; der kurze Ausflug ueber Topmost half im Versuch, unter Linux entscheidet der Fenstermanager. Mehr als bitten kann eine Anwendung nicht. ShutdownSignal ist zu InstanceControl geworden: beenden und Fenster sind zwei Bitten desselben Absenders ueber denselben Weg. Beenden gilt einmal, das Fenster beliebig oft - jeder Wechsel auf diese Instanz ist einer. --managed schaltet den Wechsel im Instanzfenster ab. Der Manager gibt es jedem Instanzprozess mit; das Fenster graut seinen Instanz-Schalter aus und erklaert auf Nachfrage, wo gewechselt wird. Zwei Schloesser an derselben Tuer: Der Knopf ist aus, und der Weg dahinter prueft es noch einmal - denn hinter ihm wartet ein Selbst-Neustart. instanceWindows in manager.json, vorbelegt auf true: Jede Instanz behaelt ihr Fenster. Auf einer Maschine ohne Bildschirmsitzung gehoert false hin. ABNAHME an echten Fenstern: Instanz ueber den Manager gestartet, PID 18128, Fenstertitel "ClawdDotNet", Befehlszeile mit --managed. Fenster von aussen minimiert (IsIconic True), dann POST /instanzen/Instance-Test/fenster: IsIconic False, im Vordergrund - und PID unveraendert 18128. Nichts neu gestartet. Das ist die Abnahmebedingung dieser Stufe. Build ohne Warnungen, 780 Tests gruen, 8 uebersprungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a511480b77 |
Instanzmanager Stufe 4: eine Adresse, ein Zugang, alle Instanzen (#528, E-M5)
Der Managerprozess. ClawdDotNet.Instanzmanager ist eine Konsolenanwendung: Lizenz pruefen, uebernehmen was laeuft, Weboberflaeche aufhaengen, warten. Beim Herunterfahren beendet er keine Instanz. Zwei neue Projekte. ClawdDotNet.Manager.Web traegt die Weboberflaeche - ohne jeden Verweis auf ClawdDotNet.Web, denn dort haengt App und daran sechzehn Werkzeugprojekte. ClawdDotNet.ManagerApp ist der Einstiegspunkt. wwwroot ist aus der Instanz in den Manager gezogen. DER SCHNITT DER HTTP-FLAECHE: Unter /api/verwaltung steht, was den Manager angeht - auflisten, anlegen, starten, anhalten. Alles andere unter /api geht unveraendert an eine Instanz. Er leitet weiter, er implementiert nicht. Welche Instanz gemeint ist, sagt die Kopfzeile X-Instanz; laeuft genau eine, ist sie gemeint, und laufen mehrere, wird nachgefragt statt geraten - eine Anfrage an die falsche Instanz ist schlimmer als eine abgelehnte. Der zusammengefuehrte Ereignisstrom haelt je laufender Instanz eine Zuleitung und schreibt an jedes Ereignis, von wem es kommt. Ergaenzt statt verpackt: Ein Umschlag waere einfacher gewesen, haette aber jede Ansicht der Weboberflaeche zum Umschreiben gezwungen. ABNAHME gegen laufende Prozesse: 401 ohne Token, Oberflaeche ohne Token 200, Instanz ueber den Manager gestartet und angehalten, /api/info und /api/status unverfaelscht durchgereicht, /events/logs mit "instanz":"Instance-Test" an jeder Zeile. Manager abgeschossen: Instanz laeuft weiter. Manager neu gestartet: "1 Instanzen bekannt, 1 laufen bereits" - uebernommen. E-M6 damit in beide Richtungen belegt. DREI DEFEKTE, die erst der Lauf gezeigt hat: 1. Der Manager hing an einer Konsolenabfrage nach dem Lizenzschluessel. Ein Test auf Console.IsInputRedirected reicht dafuer NICHT: Er beantwortet "ist die Eingabe umgeleitet", nicht "kann jemand antworten" - ein Prozess ganz ohne Konsole, der gefaehrlichste Fall, ist nicht umgeleitet. Der Manager fragt jetzt gar nicht. 2. Eine unlesbare manager.json wurde stillschweigend durch Vorgaben ersetzt: falsches Instanzverzeichnis, anderer Port, Lizenzfrage - und nichts sagte, woran es lag. LoadProblem meldet den Grund jetzt. 3. Der Instanzprozess erbte CLAWD_CONFIG_DIR vom Manager und legte sich in dessen Konfigurationsverzeichnis eine eigene Settings.json an. Eine Umgebungsvariable vererbt sich, ein Argument nicht - der Manager nimmt sein Verzeichnis jetzt ueber --config. SharedLoggerProvider ist nach Core gezogen: Es gibt jetzt zwei Webhosts, die dasselbe brauchen, und sie duerfen einander nicht kennen. NOCH OFFEN in dieser Stufe: Die Weboberflaeche kennt noch keine Instanzen - Instanzauswahl, X-Instanz und die Verwaltungsansicht sind Arbeit an wwwroot. Und webServerPort steht noch in der Instanz. Build ohne Warnungen, 775 Tests gruen, 8 uebersprungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cb56175d20 |
Instanzmanager: Lizenz und Sub-Token wandern in den Manager (#528, E-M3)
Neues Projekt ClawdDotNet.Licensing. LicenseGate, LicenseWatch, LicenseInfo und ILicensePrompt sind aus ClawdDotNet.App dorthin gezogen, weil zwei Prozesse dieselbe Lizenzpruefung brauchen und keiner von beiden den anderen kennen darf. In Core konnte es nicht landen - dann zoege jedes der sechzehn Werkzeugprojekte das Deploymentcenter-SDK mit. Dabei ist die Kopplung an SettingsManager verschwunden. LicenseGate kennt jetzt nur ILicenseStore: Serveradresse lesen, Schluessel lesen und schreiben, speichern. Die Anwendung fuehrt ihn in Settings.json, der Manager in manager.json. Der Torwaechter prueft Schluessel; Dateien verwaltet er nicht. Auch die Anwendungsversion kommt als Parameter statt aus einem globalen Zugriff - diese Schicht weiss nicht, welche Anwendung sie benutzt. ManagerCore ist die Klammer: eigene Einstellungen, Lizenz, Register, Meldeweg. Drei Regeln: - Ohne gueltige Lizenz startet er keine Instanz. Laufende werden trotzdem in die Liste genommen - man soll sie sehen und anhalten koennen. - Ein Widerruf haelt alle an. Der einzige Fall, in dem der Manager das von sich aus tut. Ein Serverausfall gehoert ausdruecklich nicht dazu; das SDK trennt Urteil und gescheiterten Versuch, und nur das Urteil kommt hier an. Sonst naehme ein Netzproblem saemtliche Installationen mit. - Ohne hinterlegte Lizenzpruefung sperrt er nichts, sonst gaebe es eine Henne-Ei-Sperre, bevor jemand einen Schluessel eintragen kann. Sub-Token je Instanz: Der Manager haelt das Master-Token und tauscht es beim ANLEGEN einer Instanz gegen ein eigenes (watchdog:ping, bugtracker:report, einzeln widerrufbar). Beim Anlegen und nicht beim ersten Start, weil die Instanzdatei dann zweifelsfrei niemandem gehoert - sie spaeter zu schreiben, waehrend der Instanzprozess laeuft, hiesse zwei Schreiber auf einer Datei. Fuer Instanzen, die es vor dem Manager schon gab, wird es vor dem Start nachgeholt. Danach fragt er ueber whoami nach, ob das Token an ein Projekt gebunden ist (SDK 2.8.1). Ein fehlgeschlagener Tokenbezug haelt die Instanz nicht auf. NOCH NICHT VOLLSTAENDIG: Die Instanzen pruefen die Lizenz weiterhin selbst. Das laesst sich erst abschalten, wenn eine Instanz feststellen kann, ob ein Manager da ist - vorher entstuende eine Fassung ganz ohne Durchsetzung. Und ein Testfehler, der zweimal derselbe war: Die Socket-Antwort war hinterlegt, BEVOR der Manager hochfuhr. Damit sagt man "diese Instanz laeuft bereits", der Manager uebernimmt sie, der Starter wird nie angefasst - und der Test prueft etwas anderes, als er behauptet. Es gibt jetzt eine Hilfsmethode, die das ausschliesst. Build ohne Warnungen, 774 Tests gruen, 8 uebersprungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
72b0764d1a |
Instanzmanager: Register, Einstellungen und stellvertretende Meldung (#528)
Der zweite Teil von Stufe 3. Die Aufsicht ueber eine einzelne Instanz stand schon; jetzt gibt es die Klammer darum und den Weg nach draussen. InstanceRegistry haelt je Instanz eine Aufsicht. Zwei Zusagen, beide aus E-M6: Beim Start wird UEBERNOMMEN, nicht gestartet - ein Manager, der beim Hochkommen alles hochfaehrt, was er findet, startet nach einem Neustart der Maschine auch das, was jemand mit Absicht angehalten hat. Und beim Beenden wird nichts beendet: Wer den Manager schliesst, will den Manager schliessen. Einzige Ausnahme ist StopAllAsync fuer einen Grund, der alle betrifft - und der heisst Lizenzwiderruf. SupervisionReporter macht aus einem Befund eine Bugtracker-Meldung, stellvertretend: mit Prozesskennung, Abschiedscode und dem Ausschnitt aus dem Instanzprotokoll, der noch auf der Platte liegt. Der Idempotenzschluessel ist stabil je Instanz und Art - stuerzt dieselbe Instanz wiederholt ab, zaehlt der Server hoch, statt den Bugtracker mit Zwillingen zu fuellen. Ein leerer Protokollauszug ist dabei die aussagekraeftigste Zeile der Meldung und wird ausdruecklich als solche geschrieben. Genau das war der Zustand am 2026-09-08: Der Prozess wurde erschlagen, bevor sein Schreibpuffer geleert war, und hinterliess nichts. manager.json traegt die Felder aus Abschnitt 6 des Konzepts und sonst nichts. Lizenzschluessel, Master-Token und Web-Token liegen darin verschluesselt und zur Laufzeit im Klartext - dieselbe Bauart wie die Anwendungseinstellungen, mit Absicht. Unsinnige Aufsichtswerte werden eingefangen, weil die Datei von Hand bearbeitbar ist. InstanceLayout ist nach Core gezogen (ResolveLogDirectory, ResolveBackupDirectory). Der Manager braucht das Protokollverzeichnis einer Instanz fuer den Auszug in der Meldung und darf ClawdDotNet.App nicht kennen; dieselbe Rechnung an zwei Stellen zu fuehren waere die schlechtere Antwort. AppHost leitet nur noch weiter. EIN DEFEKT, den erst die eigenen Tests gezeigt haben: Eine angehaltene Instanz erschien im Register als "Instance-Alpha" statt "Alpha". Die Aufsicht kennt den Namen erst, wenn die Instanz einmal geantwortet hat, und eine angehaltene tut das nie - waehrend genau diese Ansicht der Manager anzeigt. Das Register fuehrt den Namen jetzt aus InstanceSettings.json mit. Und ein Testdouble, das zu viel behauptet hat: Der falsche Socket antwortete fuer jedes Verzeichnis dieselbe Instanz. Das laesst Tests gruen werden, die im Betrieb zwei Instanzen verwechseln wuerden; er antwortet jetzt je Verzeichnis. 29 neue Tests. Build ohne Warnungen, 766 Tests gruen, 8 uebersprungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4ef6d714a8 |
Instanzmanager Stufe 3: Prozessaufsicht, Start und Stopp (#528)
Der Anlass des ganzen Vorhabens: Am 2026-09-08 hat sich eine Instanz aufgehaengt und wurde von Windows beendet. Zu diesem Vorgang steht keine einzige Zeile in den Instanzprotokollen, kein Bugtracker-Eintrag, kein Watchdog-Alarm - weil Meldeweg und Watchdog in genau dem Prozess lebten, ueber den sie haetten berichten sollen. Ab jetzt sieht jemand anders hin. Neu ist ClawdDotNet.Manager mit genau EINEM Projektverweis: Core. Das ist keine Sparsamkeit, sondern die halbe Architektur - App schleppt sechzehn Werkzeugprojekte hinter sich her, und "keine Werkzeuge im Manager" soll eine Eigenschaft des Bauplans sein und keine Absichtserklaerung. DIE ENTSCHEIDUNG, AN DER ALLES HAENGT: Die Aufsicht fragt den Socket, nicht die Prozessliste. Die Prozessliste sagt, dass etwas da ist. Sie sagt nicht, ob es noch arbeitet - und genau das war der Zustand, den niemand bemerkt hat. Ein Durchgang kennt drei Antworten: Socket antwortet, sie laeuft. Socket schweigt und der Prozess ist weg, Absturz mit Abschiedscode. Socket schweigt und der Prozess lebt, verstummt - aber erst nach mehreren Fehlversuchen in Folge, denn ein Alarm bei jedem Aussetzer ist nach einer Woche der Alarm, den niemand mehr liest. Ein Start gilt erst, wenn die Instanz antwortet. Zwischen "der Prozess laeuft" und "die Instanz laeuft" liegen Datenbank, Agenten und Kestrel; wer nur auf Process.Start wartet, haelt eine Instanz fuer gestartet, die gleich an einer kaputten Konfiguration scheitert. Angehalten wird durch Bitten. Neu auf der Instanzseite: POST /api/instanz/beenden, ausschliesslich ueber den Socket - ueber TCP gibt es den Endpunkt nicht, auch mit gueltigem Token nicht, und er antwortet dort 404 statt 403. Die Lebensdauer einer Instanz gehoert dem Manager; eine Browsersitzung soll den Prozess, der sie bedient, nicht beenden koennen. Ausgeloest wird der Wunsch erst nach dem Absenden der Antwort (Response.OnCompleted), sonst bekaeme der Manager statt einer Zusage einen Verbindungsabbruch und hielte ihn fuer einen Fehlschlag. Wiederfinden nach einem Managerausfall (E-M6): Die Zustandsdatei instance-state.json ist der Hinweis, der Socket ist der Beweis. Erst wird gefragt, wer da antwortet; nur bei gleicher Instanzkennung wird die Prozesskennung aus der Datei angefasst. Andersherum fuehrt eine wiederverwendete Nummer nach einem Neustart der Maschine geradewegs dazu, ein fremdes Programm zu beaufsichtigen und beim Anhalten zu beenden. Selbsttaetiger Neustart ist bewusst NICHT gebaut. Die Abnahmebedingung lautet "merkt es und meldet es", und zum Neustarten trifft das Konzept keine Entscheidung; eine Instanz in einer Schleife wieder hochzufahren, die jedes Mal abstuerzt, waere die falsche Vorgabe fuer etwas, das niemand entschieden hat. Als offene Frage aufgeschrieben. ABNAHME, mit echten Prozessen gemessen: Manager startet Instanz Test in 0,7 s, identifiziert sie ueber die Named Pipe, schreibt die Zustandsdatei (pid 54476, id d68af6fd). Prozess von aussen abgeschossen. Naechster Durchgang: Crashed, Code -1, meldenswert, Zustandsdatei weggeraeumt. Dazu 20 Tests gegen gesteuerte Prozesse - ein Absturz zum gewuenschten Zeitpunkt und eine wiederverwendete Prozesskennung sind mit einem echten Programm nicht herzustellen. Noch offen in dieser Stufe: Instanzen anlegen, Lizenz im Manager, Sub-Token je Instanz, und der Managerprozess selbst. Build ohne Warnungen, 737 Tests gruen, 8 uebersprungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b78e5d844f |
InstanceDirectoryManager nach Core verschoben (Vorarbeit #528 Stufe 3)
Reiner Umzug, keine Verhaltensaenderung. Der Instanzmanager braucht die Verzeichnisstruktur einer Instanz - anlegen, auflisten, Konfiguration lesen -, aber er darf ClawdDotNet.App nicht referenzieren: Dort haengen sechzehn Werkzeugprojekte dran (Telegram, Mail, FTP, Rocket.Chat, ...), und der Manager soll ausdruecklich keines davon kennen. Ein Projektverweis wuerde sie nicht ausfuehren, aber laden - und die Zusage "keine Werkzeuge im Manager" waere nur noch eine Absichtserklaerung statt einer Eigenschaft des Bauplans. Das Wissen selbst gehoert ohnehin nach Core: Es beschreibt, wie eine Instanz auf der Platte liegt, und InstanceConfig und AgentConfig liegen dort schon. Verschoben nach Core/Instances: InstanceDirectoryManager, InstanceInfo, TokenUsageRecord/TokenUsageFile, AgentListItem/AgentListFile. In ClawdDotNet.App/Models bleiben JobHistoryEntry und die Werkzeug-Ansichtsmodelle, die dort richtig liegen. Build ohne Warnungen, 717 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
373e1f4caf |
Deploymentcenter-Anbindung auf SDK 2.8.2 nachgezogen (#218)
Der Changelog seit 2.6.0 nennt sechs Fassungen, 16 action_items und fuenf brechende Aenderungen. Davon trifft fast nichts diese Anwendung - und das ist ein Befund, kein Versaeumnis: - finding_status (brechend, 2.7.0): kein Aufrufer. Weder /api/supervisor/v1/observe noch /status werden hier angesprochen. Vorgemerkt fuer #528 Stufe 3 - wer den Manager baut, liest den Befundzustand von Anfang an aus diesem Feld. - Nachpruefungs-Status awaiting_verification, in_verification, fix_insufficient (brechend, 2.6.1): Die Anwendung wertet bugtracker_items.status nirgends aus. Betrifft den Arbeitsablauf der Agenten, und der steht in CLAUDE.md bereits richtig beschrieben. - Meldungen ohne project_slug (brechend, 2.8.1): BugtrackerClient und ErrorReporter schicken project_slug und build_version seit jeher mit. - Leseweg ohne project_slug und schmales dc_roadmap (brechend, 2.8.1): betrifft den MCP-Adapter, nicht den Anwendungscode. Geblieben ist die eine Zusage, die man nicht sieht, wenn man sie verletzt: Seit 2.8.1 erbt ein Watchdog-Monitor das Projekt seines Ping-Tokens. Ohne Bindung landen die Befunde im Scheinprojekt "default", und eine Abfrage nach dem eigenen Projekt liefert fuer eine laufende, fleissig meldende Instanz null Befunde - die Aufsicht sieht aus wie "nichts auffaellig". Das ist genau die Falle, in die hier schon einmal Meldungen gelaufen sind. Deshalb neu: TokenProvisioner.WhoAmIAsync gegen GET /api/tokens/v1/whoami (SDK 2.7.1) und der Typ TokenIdentity mit IsProjectBound. DeploymentcenterService fragt beim Start das eigene Melde-Token, protokolliert das Projekt und warnt, wenn keines dransteht. Bewusst nur eine Warnung: Monitoring, das bei unklarer Rechtelage gar nicht erst anlaeuft, ist genau dann still, wenn man es braucht. Faellt die Auskunft aus - aelterer Server, kein Netz -, bleibt es bei Debug. Gemessen statt geraten: TokenProvisioner schickt keinen project_slug mit, und ob der Server die Bindung vom Master-Token erbt, steht in keiner Beschreibung, die vorliegt. Ein Sub-Token dafuer eigens auf dem Produktivserver auszustellen waere zu viel Nebenwirkung gewesen; die erste Instanz, die eines zieht, sagt es jetzt selbst ins Protokoll. #176 ist erledigt und die Regel in CLAUDE.md gestrichen: dc_roadmap ohne project_slug liefert jetzt das eigene Projekt (nachgemessen: 51 Eintraege, alle clawddotnet). Wer projektuebergreifend lesen will, schickt project_slug=all. dc_link_project auf 2.8.2 gesetzt, CLAUDE.md-Zeile hochgesetzt. Build ohne Warnungen, 717 Tests gruen, 8 uebersprungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8686d13999 |
Instanzmanager Stufe 2: Kestrel auf dem Socket, Instanz kopflos (#528)
Eine Instanz ist jetzt ohne Port ansprechbar. InstanceEndpoint liefert die Adresse: unter Linux <Instanz>/manager.sock, unter Windows eine Named Pipe, deren Name aus einem SHA-256-Fingerabdruck des Instanzpfades entsteht. Zwei Instanzen bekommen damit zwangslaeufig zwei Adressen, ohne dass jemand etwas vergeben muss - genau der Unterschied zur Vorbelegung 7001, um die sich zwei Instanzen stillschweigend streiten. Der Socket ist immer da, auch ohne Weboberflaeche. Er gehoert nicht zu #494, sondern ist die Steuerflaeche der Instanz: webUiEnabled schaltet den TCP-Zugang, nicht den Socket. WebUiHost heisst deshalb jetzt InstanceHttpHost - ein Kestrel, zwei Zugaenge. Ueber TCP gilt Token-Pflicht, ueber den Socket nicht: Dort steht die Grenze am Dateisystem (0600 unter Linux, CurrentUserOnly bei der Named Pipe, explizit gesetzt statt auf die Vorgabe vertraut). Wer die Socketdatei oeffnen darf, sieht ohnehin in das Verzeichnis, in dem alles liegt, was ein Token schuetzen wuerde. Woran die beiden Zugaenge auseinandergehalten werden, ist der Punkt, an dem ein Irrtum teuer waere. Bei Socket-Verbindungen stehen in HttpContext.Connection keine IP-Adressen - daraus zu schliessen "keine IP, also Socket" waere eine Vermutung an der Stelle, an der ein Torwaechter steht, und sie irrt in die falsche Richtung: Im Zweifel liesse sie eine TCP-Anfrage ohne Token durch. Der Socket-Zuhoerer haengt deshalb jeder Verbindung selbst eine Marke an (LocalTransport, Verbindungs- Zwischenschicht ueber ListenOptions.Use); wo sie fehlt, gilt Token-Pflicht. Ein belegter Port kostet nur noch die Weboberflaeche. Vorher gab TryStart in dem Fall null zurueck und die ganze HTTP-Flaeche war weg. Seit dem Socket ist das die falsche Rangfolge: Ihn zu verlieren, weil eine zweite Instanz laeuft, hiesse die Instanz unerreichbar zu machen - in genau dem Fall, fuer den es den Manager gibt. Kopfloser Betrieb ueber --headless --instance <Pfad>. Die Verzweigung sitzt in Program.Main vor BuildAvaloniaApp, damit auf einem Server ohne Bildschirmsitzung kein Fenstersystem angefasst wird. Gefragt wird nichts: Instanz aus dem Argument, Lizenz ueber das bereits vorhandene NonInteractiveLicensePrompt, Telegram-Anmeldung aus. Nachgemessen, nicht angenommen: Kopflose Instanz auf Instance-Test gestartet, kein Fenster, und eine rohe HTTP/1.1-Anfrage aus einem fremden Prozess (PowerShell, direkt auf die Pipe) bekam 200 OK auf /api/info, ohne Token. Das ist die Abnahmebedingung fuer Stufe 2. Korrektur zur eigenen Ankuendigung: webServerPort entfaellt nicht mit Stufe 2, sondern mit Stufe 4. Solange kein Manager die Weboberflaeche traegt, hiesse Wegnehmen, sie ersatzlos abzuschalten. Zwei Luecken bleiben offen und gehoeren auf die Linux-Testmaschine: Die Socketdatei-Faelle sind unter Windows gegenstandslos und werden dort mit sichtbarer Begruendung uebersprungen - der Unix-Domain-Socket selbst ist auf der Entwicklungsmaschine also ungeprueft, obwohl er die Zielplattform ist. Und das geordnete Ende auf SIGTERM liess sich hier nicht ausloesen: Windows kennt das Signal fuer einen fensterlosen Prozess nicht. Build ohne Warnungen, 714 Tests gruen, 8 uebersprungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
facd21459c |
Instanzmanager Stufe 1: Protokolle und Sicherungen je Instanz (#528)
Mehrere Instanzen sollen unabhaengig voneinander laufen. Heute tun sie das nicht: Sie teilen sich Protokollverzeichnis, Sicherungsordner und Sicherungszeitplan. In einer gemeinsamen Startup.log standen abwechselnd Zeilen zweier Instanzen, ohne Kennzeichnung, welche gerade spricht -- im Mehrinstanzbetrieb ist damit jede Fehlersuche Ratearbeit. Genau in dieser Lage ist der Aufhaenger aus #529 entstanden. Fuenf Schluessel wandern aus AppSettings nach InstanceSettings.json: logDirectory, backupDirectory, backupKeepCount, autoBackupEnabled, autoBackupTime. Leer heisst jetzt <Instanz>/Logs beziehungsweise <Instanz>/Backups; ein relativer Wert zaehlt weiterhin gegen das Instanzverzeichnis, nicht gegen das Arbeitsverzeichnis des Prozesses (#375 bleibt gewahrt). autoBackupEnabled und autoBackupTime standen in der Planung nicht, sind aber mitgegangen: Ein Schalter, der die taegliche Sicherung fuer eine Instanz einschaltet und sie damit fuer alle einschaltet, ist derselbe Fehler wie ein Ordner fuer alle -- und zwei von vier Sicherungsfeldern zu verschieben hinterliesse eine Einstellungsseite, auf der die Haelfte anwendungsweit gilt. Keine Uebernahme der Altwerte, aber eine Ansage: SettingsManager. AbandonedKeys meldet die aufgegebenen Schluessel, solange sie noch in Settings.json stehen, und der Start protokolliert einmal, was nicht mehr gilt und wohin diese Instanz stattdessen schreibt. Ein anwendungsweiter Pfad, in alle Instanzen kopiert, waere wieder ein Ordner fuer alle. webServerPort bleibt vorerst stehen. Es entfaellt mit Stufe 2, sobald Kestrel auf dem Socket hoert; vorher wegzunehmen hiesse, die Weboberflaeche ohne Ersatz abzuschalten. Korrektur an der eigenen Befundlage: "Instanz A wirft die Sicherungen von Instanz B weg" war zu breit. Beide Rotationen filtern bereits nach dem Praefix backup_<Instanzname>_. Der reale Defekt ist enger, und ein Praefix ist keine Grenze: backup_Test_2_*.zip faengt mit backup_Test_ an, also zaehlt Test im gemeinsamen Ordner die Archive von Test_2 mit. Belegt in BackupSchedulerTests, das den alten Zustand eigens wiederherstellt. Nachgeprueft an der laufenden Installation, nicht nur im Test: Nach dem Umbau schreibt Instance-Test nach Instances/Instance-Test/Logs, das alte gemeinsame Verzeichnis bekommt keine Zeile mehr, und die Warnung zu den aufgegebenen Schluesseln erscheint genau einmal. Enthaelt ausserdem, was seit der Weboberflaeche liegengeblieben war: - #526: Instanzgeheimnisse wurden beim Speichern verschluesselt und beim Laden nie entschluesselt. LoadInstanceConfig hatte kein ConfigSecrets.Unprotect; ConfigLoader.LoadAsync macht es richtig, wird zur Laufzeit aber gar nicht aufgerufen. Der OpenRouter-Schluessel ging so verschluesselt an die API. - Ein offener Ereignisstrom hielt das Beenden fuenf Sekunden auf. Die Stroeme hingen nur an RequestAborted, das beim geordneten Herunterfahren nicht feuert; jetzt zusaetzlich an ApplicationStopping. Der Fang in WebUiHost gilt dazu ohne Bedingung auf RequestAborted -- mit ihr waere jeder offene Strom beim Beenden als Fehler im Bugtracker gelandet. - Nach der Anmeldung blieb die Weboberflaeche leer: #anmeldung { display:flex } schlaegt als ID-Selektor das hidden-Attribut. - docs/Instanzmanager-Konzept.md: Befund, die Entscheidungen E-M1 bis E-M6, der Schnitt zwischen Manager und Instanz und die sechs Stufen. Build ohne Warnungen, 709 Tests gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
928da7e6af |
Standardport fuer WebUI auf 7001 geaendert
- InstanceConfig.WebServerPort von 8080 auf 7001 vorbelegt - Dokumentation und WebUiOptionsTests nachgezogen |