dsh-switchman 1.8.1 → 1.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.de.md CHANGED
@@ -14,7 +14,7 @@ Wer lange mit DSH arbeitet, ärgert sich über zwei Dinge. Erstens werden Sessio
14
14
 
15
15
  dsh-switchman ist ein Plugin für [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH). Nach der Installation wandelt sich das Hauptmodell vom Selbermacher zum Disponenten: Wasserstand messen, Spur wählen, Aufgaben vergeben, Abnahme prüfen. Es ist kein neues Modell, sondern ein Dispositionsregelwerk an DSH plus eine Einstellungsseite — und es tut acht Dinge:
16
16
 
17
- **1. Kontext-Wasserstand: lange Sessions platzen nicht.** Jede Runde zählt die Session-Tokens in Echtzeit; drei Stufen schrauben sich hoch: 80k (soft, einstellbar) mahnt „zeit zu delegieren“; 120k (hard) strafft das Einzellese-Budget und lenkt auf den Abschluss; 150k (force) übergibt automatisch — die Session wird zur Ablage geforkt, der Kontext komprimiert, der Nachfolger geweckt und läuft weiter, ohne dass die Aufgabe abreißt. Auch Hintergrund-Subagents, die im Moment der Übergabe noch laufen, gehen nicht verloren: id, Aufgabe und Report-Pfad landen im Übergabedokument; die fortgesetzte Session weiß, wo sie Reports abholt, und vergibt nichts doppelt. Jeder Subagent trägt eine eigene harte Obergrenze und verlässt nach seinem HANDOFF-Summary die Bühne. Passt dir die automatische Intervention nicht, übernimm jederzeit von Hand: `/ctx-pause` stoppt alle Wasserstands-Aktionen (die Messung läuft weiter, die Live-Wasserstandszeile am Ende springt auf paused — es wird nur nicht mehr gehandelt), `/ctx-resume` holt alles jederzeit zurück — nach einem DSH-Neustart greift die Intervention auch von selbst wieder, ein temporäres Loslassen, kein Dauer-Aus; `/ctx-handover` wartet nicht, bis der Wasserstand oben ist, und startet dieselbe Übergabe auf Zuruf: Session zur Sicherung forken, Kontext verdichten, Fortsetzung wecken (die Session wird erst an eine Leerlauf-Grenze geführt, das Ergebnis kann daher ein paar Minuten dauern).
17
+ **1. Kontext-Wasserstand: lange Sessions platzen nicht.** Jede Runde zählt die Session-Tokens in Echtzeit; drei Stufen schrauben sich hoch: 80k (soft, einstellbar) mahnt „zeit zu delegieren“; 120k (hard) strafft das Einzellese-Budget und lenkt auf den Abschluss; 150k (force) übergibt automatisch — das Übergabedokument wird geschrieben (das append-only Sitzungslog ist dabei die vollständige Sicherung), der Kontext komprimiert, die Session geweckt und läuft weiter, ohne dass die Aufgabe abreißt. Auch Hintergrund-Subagents, die im Moment der Übergabe noch laufen, gehen nicht verloren: id, Aufgabe und Report-Pfad landen im Übergabedokument; die fortgesetzte Session weiß, wo sie Reports abholt, und vergibt nichts doppelt. Jeder Subagent trägt eine eigene harte Obergrenze und verlässt nach seinem HANDOFF-Summary die Bühne. Passt dir die automatische Intervention nicht, übernimm jederzeit von Hand: `/ctx-pause` stoppt alle Wasserstands-Aktionen (die Messung läuft weiter, die Live-Wasserstandszeile am Ende springt auf paused — es wird nur nicht mehr gehandelt), `/ctx-resume` holt alles jederzeit zurück — nach einem DSH-Neustart greift die Intervention auch von selbst wieder, ein temporäres Loslassen, kein Dauer-Aus; `/ctx-handover` wartet nicht, bis der Wasserstand oben ist, und startet dieselbe Übergabe auf Zuruf: Übergabedokument schreiben, Kontext verdichten, Session zum Weiterlaufen wecken (die Session wird erst an eine Leerlauf-Grenze geführt, das Ergebnis kann daher ein paar Minuten dauern).
18
18
 
19
19
  **2. Sechs Dispatch-Pools: das richtige Modell für jede Arbeit.** Economy (Massen-Kleinkram), Mechanical (Schablonen-Umformung), Main (tägliche Codearbeit), Hard (schwere Schlussfolgerungen und große Refactorings), Vision (Bilder), Review (unabhängige Prüfung). Auf der Einstellungsseite kreuzt man Kandidaten an, ordnet Prioritäten, vergibt Noten S/A/B/C und nagelt je Route einen Reasoning-Aufwand fest — das Aufwand-Dropdown führt die Stufen, die das Modell tatsächlich beherrscht, nicht generische drei. Im Prompt jeder Runde steckt eine `[SWITCHMAN:POOLS]`-Empfehlungstabelle; nach der wird vergeben. Drei Enforcement-Modi: aus / Empfehlung / Erzwingen (Erzwingen = Modelle außerhalb der Pools werden glatt abgelehnt).
20
20
 
@@ -23,7 +23,7 @@ dsh-switchman ist ein Plugin für [DeepSeek Harness](https://github.com/deepseek
23
23
  - **Agent-Teams** — eingeschaltet injiziert das Team-Regelwerk (Delegation als Standard + gestufte Verifikation + Disziplin fürs gemeinsame Task-Board) und aktiviert die Agent-Teams-Funktion von DSH automatisch: das Hauptmodell rekrutiert Dauerteammates (`spawn_teammate`), legt Aufgaben auf das gemeinsame Task-Board (`team_task_*`) und tauscht Nachrichten mit Teammates aus. Wann ein Team fällig ist, ist klar geregelt: parallelisierbare unabhängige Subtasks, große in sich abgeschlossene Mengen, hoher Wasserstand im Hauptkontext, nötige Rollentrennung; einmalige Punktuntersuchungen bleiben beim Subagent. Wieder ausgeschaltet verschwinden die Team-Klauseln restlos, und laufenden Sessions werden die Team-Werkzeuge nie entrissen.
24
24
  - **Whitelist-Sync für Subagent-Modelle** — DSH führt eine Autorisierungs-Whitelist „Agenten dürfen Modelle für Subagents wählen“: in einem Pool gewählte, aber nicht autorisierte Routen werden bei namentlicher Vergabe abgelehnt (im Teams-Modus mit ⚠ markiert). Dieser Schalter schreibt die Vereinigung aller sechs Pools en bloc in ebendiese Whitelist — switchman ist die einzige Quelle der Wahrheit, doppelte Konfiguration entfällt. Die Whitelist gilt als Snapshot je neuer Session; der Sync wirkt nur auf danach gestartete Sessions.
25
25
 
26
- Die Session-Kopfzeile zeigt sofort, was los ist: das ⚡-Abzeichen „autonomes Team“ und ◇ mit dem tatsächlich genutzten Modell der Session; während einer laufenden Auto-Übergabe erscheint live „Übergabe läuft · Backup-Session / Kontext komprimieren / Nachfolger wecken“.
26
+ Die Session-Kopfzeile zeigt sofort, was los ist: das ⚡-Abzeichen „autonomes Team“ und ◇ mit dem tatsächlich genutzten Modell der Session; während einer laufenden Auto-Übergabe erscheint live „Übergabe läuft · Übergabedokument schreiben / Kontext komprimieren / Nachfolger wecken“.
27
27
 
28
28
  **4. Sprachpräferenzen: einmal fragen, für immer merken.** Je ein Dropdown für Antworten, Code-Kommentare und Dokumente; Geltungsbereich global oder je Projekt (`.switchman/lang.json`). Leer lassen geht auch — beim ersten Einsatz wird einmal in der Sprache deiner DSH-Oberfläche gefragt, danach hält sich jede Session daran.
29
29
 
@@ -75,7 +75,7 @@ Auch mit nur einem Modell lohnt sich die Installation — Wasserstandskontrolle
75
75
  - **Dispatch-Pools** — sechs Karten in zwei Reihen zu drei: oben Economy / Mechanical / Main, unten Hard / Vision / Review. Kandidaten je Provider ankreuzen; ein Haken bei „manuelle Reihenfolge“ macht daraus eine nummerierte Prioritätsliste, sortiert mit ↑ ↓ ×; je Route lässt sich der Reasoning-Aufwand festnageln (Standard „wie Spur“). Die Zusammenfassungszeile oben aktualisiert live — im Screenshot: „4/6 Pools konfiguriert · 2 gerankt · Modus Empfehlung“.
76
76
  - **Fähigkeits-Ranking + Enforcement-Modus** — die Vereinigung der gewählten Modelle über alle sechs Pools, nach Fähigkeit sortiert, das stärkste zuerst (im Screenshot glm-5.3 auf S und glm-5.3-flash auf A verankert), umsortier- und kürzbar; Enforcement-Modus Empfehlung / Erzwingen (Erzwingen = Modelle außerhalb der Pools werden glatt abgelehnt).
77
77
  - **Agent-Teams** — die zwei Schalter für Team-Modus und Whitelist-Sync, beide standardmäßig aus; im Screenshot an, mit Sync-Statuszeile unter dem Schalter.
78
- - **Kontext-Wasserstand** — die drei Schwellen (im Screenshot 80000 / 120000 / 150000), das Einzellese-Budget (3000), das Verhalten der Hard-Stufe (drosselnd durchlassen / blockieren), der Schalter für die Auto-Übergabe und die eigenen Obergrenzen der Subagents — alles in diesem Bereich. Unten die Befehlszeile: `/ctx-pause` Eingriffe pausieren · `/ctx-resume` fortsetzen · `/ctx-handover` sofort sichern und übergeben (die Session wird erst an eine Leerlauf-Grenze geführt und dann komprimiert — das Ergebnis kann ein paar Minuten dauern).
78
+ - **Kontext-Wasserstand** — die drei Schwellen (im Screenshot 80000 / 120000 / 150000), das Einzellese-Budget (3000), das Verhalten der Hard-Stufe (drosselnd durchlassen / blockieren), der Schalter für die Auto-Übergabe und die eigenen Obergrenzen der Subagents — alles in diesem Bereich. Unten die Befehlszeile: `/ctx-pause` Eingriffe pausieren · `/ctx-resume` fortsetzen · `/ctx-handover` Übergabedokument schreiben und sofort übergeben (die Session wird erst an eine Leerlauf-Grenze geführt und dann komprimiert — das Ergebnis kann ein paar Minuten dauern).
79
79
  - **Intent-Routing** — der Hauptschalter, die Small-Model-Route des Klassifikators, der Klassifikatortyp (auto / decision / chat), die eigene Kandidatenliste (je Eintrag eine Zeile Fähigkeitsbeschreibung), Cache-Fenster / Vertrauensschwelle / Klassifikations-Timeout / Verlaufsbudget und ein optionales Fallback-Modell. Eingeschaltet erscheint in der Modellauswahl der lokalisierte Eintrag „Automatisch (Intent-Routing)“, und der Status-Chip über dem Eingabefeld zeigt Routing-Modell und Vertrauen.
80
80
 
81
81
  4. **Prüfen** — in der Session-Kopfzeile erscheint das ⚡-Abzeichen „autonomes Team“ (daneben ◇ mit dem Modell der Session); oder das Modell einfach fragen: „Wie lautet die Überschrift des letzten Abschnitts deines System-Prompts?“ — die Antwort sollte das dsh-switchman-Regelwerk nennen.
@@ -96,13 +96,56 @@ Alles Folgende sind die vollständigen Werte direkt nach der Neuinstallation (ma
96
96
 
97
97
  **Dispatch-Pools**: alle sechs Spuren (economy/mechanical/main/hard/vision/review) starten ohne Kandidaten (= die Standardmodelle des Systems, keine Einwirkung); manuelle Reihenfolge und gepinnter Aufwand sind aus/leer; `dispatchEnforce` = `advice` (nur Empfehlung, nichts wird erzwungen).
98
98
 
99
- **Kontext-Wasserstand**: soft `80000` / hard `120000` / force `150000` Token; Einzellese-Budget `3000`; Verhalten bei Budgetüberschreitung `cap` (kürzen mit Warnung); `wmAutoHandover` = an (bei force automatische Sicherung + Übergabe mit Komprimierung); die harte Obergrenze für Subagents = an (gemeinsame 150k).
99
+ **Kontext-Wasserstand**: soft `80000` / hard `120000` / force `150000` Token; Einzellese-Budget `3000`; Verhalten bei Budgetüberschreitung `cap` (kürzen mit Warnung); `wmAutoHandover` = an (bei force Übergabedokument schreiben + Komprimierung, automatisch); die harte Obergrenze für Subagents = an (gemeinsame 150k).
100
100
 
101
101
  **Agent Teams**: `teamsMode` = aus; `syncWhitelist` = aus (die beiden Schalter sind unabhängig voneinander).
102
102
 
103
103
  **Intent-Routing** (ab Werk unbrauchbar; nötig sind Klassifikator + mindestens ein Kandidat + der Hauptschalter): `intentEnabled` = aus (ist er aus, bleibt der Auto-Eintrag verborgen, und Sessions, die bereits Auto gewählt hatten, laufen über die Fallback-Kette); Klassifikator-Route nicht gesetzt; Klassifikatortyp `chat`; Kandidatenliste leer (die mitgelieferten Kategorien nehmen erst mit eingetragenem Modell am Routing teil); Cache-Fenster `300` s / Umschalt-Vertrauensschwelle `80` / Klassifikations-Timeout `60000` ms; Verlauf `6` Nachrichten / `2400` Zeichen; `intentContextWindow` = `200000`.
104
104
 
105
- **Out-of-the-Box-Anmerkungen**: ① dass Intent-Routing ab Werk unbrauchbar ist, ist Absicht — drei Schritte zum Einschalten (Klassifikator, Kandidaten, Schalter); ② das wirksame Auto-Fenster steht ab Werk auf 200000 — die Leitplanke garantiert 0.8×Fenster ≥ force (150000), die Kern-Komprimierlinie (160000) liegt also stets hinter der Linie der weichen Übergabe (150000); ③ am spürbarsten ab Werk ist das Wasserstands-Trio: 80k mahnt zur Delegation / 120k strafft das Lesen / 150k sichert automatisch ab und übergibt — ganz ohne Einstellungen.
105
+ **Kontext-Trimming**: `trimEnabled` = an; `trimMode` = `auto` (vollautomatisch); Wachstumsschwelle `22500` + Zuwachs `50000` Tokens; Druck `60%` / Notfall `95%`; geschütztes Ende `6` Nachrichten; Fold-Takt-Obergrenze `6` pro Stunde; Nebenmodell nicht gesetzt (erbt das Session-Modell) — Details im Abschnitt „Automatisches Kontext-Trimming“ unten.
106
+
107
+ **Out-of-the-Box-Anmerkungen**: ① dass Intent-Routing ab Werk unbrauchbar ist, ist Absicht — drei Schritte zum Einschalten (Klassifikator, Kandidaten, Schalter); ② das wirksame Auto-Fenster steht ab Werk auf 200000 — die Leitplanke garantiert 0.8×Fenster ≥ force (150000), die Kern-Komprimierlinie (160000) liegt also stets hinter der Linie der weichen Übergabe (150000); ③ am spürbarsten ab Werk ist das Wasserstands-Trio: 80k mahnt zur Delegation / 120k strafft das Lesen / 150k übergibt automatisch (Dokument schreiben + Komprimierung) — ganz ohne Einstellungen.
108
+
109
+ ## Automatisches Kontext-Trimming
110
+
111
+ Lange Sessions bleiben günstig: verarbeitete Gesprächsbereiche werden zu kompakten deterministischen Zusammenfassungen gefaltet — vollautomatisch und ohne den Prompt-Cache des Anbieters zu brechen. An einer Leerlauf-Grenze wählt die Trim-Engine (`host/context-trim/`) die faltenswerten Bereiche, ein Nebenmodell schreibt die Zusammenfassung (nur unter Notfall-Druck greift stattdessen ein mechanischer Fold ohne Nebenmodell-Aufruf), und der Fold wird in den Fold-Zustand der Session committet. Von da an wird jede ausgehende Anfrage aus diesem Zustand neu aufgebaut — Zusammenfassungsblöcke ersetzen die gefalteten Bereiche, der lebende Tail bleibt unangetastet. Die gespeicherte Session wird nie umgeschrieben, und mitten im Stream passiert niemals etwas.
112
+
113
+ Automatisierungsstufen (`trimMode`):
114
+
115
+ - **`off`** — nur messen: Usage- und Cache-Hit-Sampling läuft weiter; kein Banner, keine Folds.
116
+ - **`advisory`** — das Session-Modell faltet selbst: die Tools `ctx_trim_fold` und `ctx_trim_status` plus der Protokollkopf werden dafür freigegeben.
117
+ - **`auto`** (Standard) — alles darüber hinaus plus automatische Folds an der Leerlauf-Grenze, in drei Stufen: eine Wachstumsschwelle (Zuwachs > `trimGrowthTokens`, gemessen ab dem Tiefpunkt nach dem letzten Fold — der Anker, der die Schwelle nach Folds auf hohem View-Stand scharf hält — UND ein zusammenhängender komprimierbarer Zuwachs ≥ `trimIncrementTokens`), eine Druckstufe (`trimPressurePercent` des Kontextfensters) und eine Notfallstufe (`trimEmergencyPercent` — ein mechanischer Fold mit eigener Abklingzeit).
118
+
119
+ Alle Schlüssel liegen unter Einstellungen → dsh-switchman; Defaults aus `host/config.js`:
120
+
121
+ | Schlüssel | Standard | Bedeutung |
122
+ | --- | --- | --- |
123
+ | `trimEnabled` | `true` | Hauptschalter. AUS deaktiviert Folds und Metriken komplett (der Listener wird zum reinen Durchreichen). |
124
+ | `trimMode` | `auto` | `off` nur messen / `advisory` modellgetriebene Folds / `auto` systemgetriebene Folds. |
125
+ | `trimGrowthTokens` | `22500` | Wachstumsschwelle: falte, wenn die ausgehende View seit dem letzten Fold so viel gewachsen ist. |
126
+ | `trimIncrementTokens` | `50000` | Schwellen-Begleiter: der zusammenhängende komprimierbare Zuwachs muss ebenfalls diese Größe erreichen. |
127
+ | `trimPressurePercent` | `60` | Druckstufe: falte, wenn die View-Nutzung diesen Prozentsatz des Kontextfensters erreicht. |
128
+ | `trimEmergencyPercent` | `95` | Notfallstufe: erzwungener mechanischer Fold (kein Nebenmodell-Aufruf) bei diesem Prozentsatz. |
129
+ | `trimWindowTokens` | `0` | Effektives Fensterlimit für die Stufenberechnung: gerechnet gegen min(Modellfenster, Limit). `0` = folgt `wmForceTokens`. |
130
+ | `trimProtectedTail` | `6` | Die letzten N Nachrichten werden nie gefaltet. |
131
+ | `trimMaxFoldsPerHour` | `6` | Fold-Takt-Obergrenze pro rollierender Stunde für Nebenmodell-Folds (Notfall-Folds ausgenommen). |
132
+ | `trimSideModel` | nicht gesetzt | Route des zusammenfassenden Nebenmodells. Leer = das aktuelle Session-Modell erben. |
133
+ | `trimStateDir` | nicht gesetzt | Überschreibt das Fold-Zustandsverzeichnis. Leer = `<dshHome>/.dsh-switchman/state/trim/`. |
134
+ | `surfaceFold` | `true` | Liefert committete Folds auf die Session-Oberfläche aus (Shadow-Price-Messereignis + `surfaceOp`-Replace-Ereignispaar). OFF behält die rein gesplicte View. |
135
+
136
+ **Trigger-Pfade.** Stufenprüfungen laufen an der Leerlauf-Grenze und — als Fallback für Hosts, auf denen Leerlauf-Status-Ereignisse nicht erreichbar sind — nach jedem Modell-Stream-Abschluss (auf eine Auswertung pro 60 s und Session gedrosselt). Jede Entscheidung — Folds wie No-ops gleichermaßen — wird als eine zeitgestempelte `key=value`-Zeile an `decisions.log` im Fold-Zustandsverzeichnis (siehe `trimStateDir`) angehängt und gibt Folds so eine Offline-Prüfspur; das Zeilenvokabular (`view-applied`, Fold-Entscheidungs-/Ergebniszeilen, `surface-replace`, `surface-replace skipped reason=…`) ist in [docs/context-trim.md](./docs/context-trim.md) katalogisiert.
137
+
138
+ **Leitungs-Wahrheit und stabiler Zustand.** Entscheidungszeilen tragen `view=<n>` und, sobald eine Antwort eingetroffen ist, `wire=<n>` — den echten Anbieter-Prompt (ungecachter Input + Cache-Read + Cache-Write, entnommen aus dem Usage-Event des Streams). Die View-Schätzung zählt nur das Messages-Array und liest immer um den festen Overhead kleiner als die Leitung (gemessen ≈ 17.3k: System-Prompt + Tool-Schemas + Persona); Druck- und Notfallstufe werten daher auf `max(view, wire)` aus, und nur `wire` ist eine Prompt-Größenmessung. In einem 150k-Fenster läuft der gesunde Sägezahn direkt nach einem Fold auf Leitung ≈ 40k ↔ 88–90k beim nächsten (≈27%–60% des Fensters), ein Fold pro ~50k Rohwachstum, wobei die Obergrenze eher von der Druckstufe als vom Fenster nahe 90k festgehalten wird. Steigt `view`, ohne dass je eine `surface-replace`-Zeile erscheint, lies die Skip-Zeilen (`incomplete-seqs`, `no-service`, `compaction-lock`).
139
+
140
+ **Host-Kompaktions-Epochen.** Fold-Verweise sind an Nachrichten-IDs und Inhalts-Hashes verankert; ersetzt der Host selbst den erneut gesendeten Verlauf, veralten sämtliche Blöcke auf einmal: keiner findet mehr seine Position, die View fällt still auf den Rohverlauf zurück (fail-open — das Entscheidungsprotokoll vermerkt `view-applied applied=0`), und das Plugin verankert seine Wachstumsbaseline bei der nächsten Anfrage auf der Sicht nach der Kompaktion. Die Host-Kompaktion ist damit die Backstop-Trimschicht; die Plugin-Folds leisten die feingranulare Arbeit innerhalb einer Host-Epoche. Manche Dispatches übergeben vollständig eingefrorene Anfrage-Arrays oder Optionsobjekte (die aktuelle Agent-Loop friert die zusammengesetzten Anfragen tief ein); für sie meldet `injectView` das Ergebnis im Entscheidungsprotokoll: `nothing-to-apply` (keine lokalisierbaren Fold-Blöcke — der Rohverlauf IST die View), `in-place`/`swapped` (der Fold-View wurde ausgeliefert) oder `none` (ein Fold existierte, aber der eingefrorene Dispatch konnte ihn nicht tragen — auf die Leitung geht der Rohverlauf). **Surface Replace ist der Auslieferungsweg, wann immer er verfügbar ist** (`surfaceFold`, standardmäßig an): an der seriellen `agent/turn-stopping`-Grenze plus einem Idle-Sweep hängt das Plugin das host-typische Shadow-Price-Messereignis und EIN `user/message`-Ersatzereignis an, das `surfaceOp {op:'replace', startSeq, endSeq}` trägt und mit `sourceEventSeqs` jeden überdeckten Oberflächen-Knoten abdeckt — derselbe Mechanismus, den die Host-Kompaktion selbst nutzt —, sodass ein committeter Fold aus der Quellschicht auf die Leitung gelangt, eingefrorene Dispatches eingeschlossen. Solange die letzten Dispatches einer Session `none` vermerken, behandelt das Plugin die View als reines Messobjekt: Der Wasserstand liest stattdessen die vom Anbieter gemeldete Zählung (force-Stufe und Auto-Übergabe arbeiten weiter), Fold-Zyklen pausieren, statt Summarizer-Aufrufe zu verbrennen, und das Trim-Banner markiert die Zeile mit `wire=raw` (folds not shipping; watermark reads the tally); Hosts ohne den Surface-Mechanismus (oder mit ausgeschaltetem Flag) behalten genau dieses alte Verhalten.
141
+
142
+ **Wie Folds Cache-Hits bewahren.** Jeder Fold zahlt das Präfix einmal zurück; das Ziel sind amortisierte Kosten, keine perfekte Hit-Rate. Die Engine erzwingt den Cache-Vertrag: Folds sind append-only — die Frontier bewegt sich nur vorwärts, die eingefrorene Zone wird nie wieder angetastet; die View wird deterministisch aus (Historie, Fold-Zustand) neu aufgebaut, sodass das serialisierte Präfix zwischen Folds byte-identisch bleibt und der Anbieter-Cache weiter trifft; Fold-Grenzen trennen nie einen Assistant-Tool-Call von seinem Tool-Ergebnis; die letzten `trimProtectedTail` Nachrichten sind ausgenommen; und alles failt offen — ein interner Fehler wird zum Durchreichen, nie zur kaputten Anfrage.
143
+
144
+ **Komprimierungs-Arbitrage.** Zwei automatische Trimmer über einer Historie würden doppelt zusammenfassen und einander die Frontier zerstören, deshalb setzt `cordis.patch.yml` bei den `dsh-compaction-basic`-Zeilen des Hosts `auto: false`: context-trim wird zum einzigen semantischen Trimmer. Manuelles `/compact` funktioniert weiter, und die Force-Stufen-Übergabe (sie leiht sich genau diesen manuellen Pfad) bleibt unberührt. Overflow-Recovery wandert zum Trim: Der Versuch über dem Fenster scheitert einmal, dann faltet die Notfallstufe die View, sodass der nächste Versuch passt.
145
+
146
+ **Fork-Vererbung.** Eine geforkte Session (Subagent-Fork) erbt den Fold-Zustand des Elternteils per Copy-on-Fork — aber nur, wenn der Elter-Zustand existiert und die Historie des Kindes die Frontier des Elternteils tatsächlich auflöst; andernfalls startet das Kind sauber. Eine Vererbung pro Kind, idempotent über Neustarts.
147
+
148
+ **Der Wasserstand liest die View.** Das Wasserstand-Banner meldet die Token-Zahl der ausgehenden View (das, was das Modell nach dem Falten tatsächlich sieht) statt der gespeicherten Zählung — eine stark gefaltete Session pinnt den Wasserstand damit nicht mehr auf der harten Stufe fest.
106
149
 
107
150
  ## Funktionsweise
108
151
 
@@ -113,9 +156,9 @@ Alles Folgende sind die vollständigen Werte direkt nach der Neuinstallation (ma
113
156
 
114
157
  ## Hinweise zur Wartung
115
158
 
116
- - Ändert ein DSH-Upgrade die Werks-Preset-Pluginliste, `cordis.patch.yml` aus dem neuen `presets/*.patch.yml` neu synchronisieren (Doctrine-Suffix behalten) und neu installieren.
159
+ - Ändert ein DSH-Upgrade die Werks-Preset-Pluginliste, `cordis.patch.yml` aus dem neuen `presets/*.patch.yml` neu synchronisieren (Doctrine-Suffix und die Komprimierungs-Arbitrage — `auto: false` auf den `compaction-basic`-Zeilen — behalten) und neu installieren.
117
160
  - Die Protokollzeilen (`[SWITCHMAN:LANG|POOLS|WATERMARK|TEAMS]`) sind bewusst englisch und byte-stabil — nicht lokalisieren.
118
- - `npm pack --dry-run` muss die auditierte Form mit 50 Dateien / ~205 kB behalten (`docs/`-Screenshots kommen nicht ins Paket).
161
+ - `npm pack --dry-run` muss die auditierte Form mit 60 Dateien / ~380 kB behalten (~1,26 MB entpackt; `docs/` ist ausgeschlossen).
119
162
 
120
163
  ## License
121
164
 
package/README.es.md CHANGED
@@ -14,7 +14,7 @@ Trabajando con DSH, tarde o temprano te topas con dos cosas: la sesión pesa cad
14
14
 
15
15
  dsh-switchman es un plugin para [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH). Una vez instalado, el modelo principal deja de «hacerlo todo él solo» y pasa a ser el guardagujas: medir el nivel de agua, elegir el carril, repartir tareas y aceptar el trabajo. No es un modelo nuevo: es un reglamento de despacho enganchado a DSH, más una página de configuración. En concreto hace ocho cosas:
16
16
 
17
- **1. Nivel de agua del contexto: la sesión larga no se desborda.** Cada turno contabiliza los tokens de la sesión en vivo, y tres líneas de nivel aprietan por etapas: 80k (soft, ajustable) avisa «toca delegar»; 120k (hard) recorta el presupuesto de lectura por llamada y empuja al cierre; 150k (force) lanza la entrega automática — bifurca una copia de la sesión como respaldo, compacta el contexto y despierta la continuación, sin que el trabajo se corte. Los subagentes en segundo plano que sigan corriendo en el momento de la entrega tampoco se pierden: id, descripción de la tarea y ruta del informe quedan escritos en el documento de entrega, y la sesión que continúa sabe dónde recoger los informes en vez de volver a despachar el trabajo. Cada subagente despachado lleva su propio techo duro independiente: al alcanzarlo, termina con un resumen HANDOFF. Si la intervención automática no te encaja, toma el mando manualmente en cualquier momento: `/ctx-pause` detiene todas las acciones del nivel (la medición sigue corriendo, la línea de nivel en vivo del final pasa a paused — solo deja de actuar), `/ctx-resume` lo devuelve en cualquier momento — tras reiniciar DSH la intervención también vuelve sola, lo sueltas por un rato, no lo apagas para siempre; `/ctx-handover` no espera a que el nivel llegue arriba y lanza esa misma entrega a demanda: bifurca una copia de respaldo, compacta el contexto, despierta la continuación (la sesión se conduce primero a la frontera de inactividad, así que el resultado puede tardar unos minutos).
17
+ **1. Nivel de agua del contexto: la sesión larga no se desborda.** Cada turno contabiliza los tokens de la sesión en vivo, y tres líneas de nivel aprietan por etapas: 80k (soft, ajustable) avisa «toca delegar»; 120k (hard) recorta el presupuesto de lectura por llamada y empuja al cierre; 150k (force) lanza la entrega automática — escribe el documento de entrega (el registro de sesión de solo añadido es la copia completa), compacta el contexto y despierta la sesión para continuar, sin que el trabajo se corte. Los subagentes en segundo plano que sigan corriendo en el momento de la entrega tampoco se pierden: id, descripción de la tarea y ruta del informe quedan escritos en el documento de entrega, y la sesión que continúa sabe dónde recoger los informes en vez de volver a despachar el trabajo. Cada subagente despachado lleva su propio techo duro independiente: al alcanzarlo, termina con un resumen HANDOFF. Si la intervención automática no te encaja, toma el mando manualmente en cualquier momento: `/ctx-pause` detiene todas las acciones del nivel (la medición sigue corriendo, la línea de nivel en vivo del final pasa a paused — solo deja de actuar), `/ctx-resume` lo devuelve en cualquier momento — tras reiniciar DSH la intervención también vuelve sola, lo sueltas por un rato, no lo apagas para siempre; `/ctx-handover` no espera a que el nivel llegue arriba y lanza esa misma entrega a demanda: escribe el documento de entrega, compacta el contexto, despierta la sesión para continuar (la sesión se conduce primero a la frontera de inactividad, así que el resultado puede tardar unos minutos).
18
18
 
19
19
  **2. Seis pools de despacho: a cada trabajo, su modelo.** Pool ligero (economy, tareas pequeñas en lote), mecánico (mechanical, reescrituras con plantilla), principal (main, código del día a día), de alta dificultad (hard, razonamiento difícil y refactorizaciones a gran escala), multimodal (vision, leer imágenes) y de revisión (review, verificación independiente). En la página de ajustes marcas candidatos, ordenas prioridades (con anclaje opcional a las categorías S/A/B/C) y fijas un effort de razonamiento por ruta — el desplegable de categorías sale de los niveles que ese modelo soporta de verdad, no de tres genéricos. Cada turno, el prompt del modelo principal lleva consigo una tabla de recomendaciones `[SWITCHMAN:POOLS]`, y el trabajo se despacha siguiéndola. El modo de ejecución tiene tres estados: off / advice / enforce (enforce = los modelos fuera del pool se rechazan sin más).
20
20
 
@@ -23,7 +23,7 @@ dsh-switchman es un plugin para [DeepSeek Harness](https://github.com/deepseek-a
23
23
  - **Modo de equipo de agentes** — al activarlo se inyecta el reglamento de equipo (delegación por defecto + verificación por niveles + disciplina del tablero de tareas compartido) y se habilitan automáticamente los Agent Teams de DSH: el modelo principal puede reclutar compañeros residentes (`spawn_teammate`), asignarles trabajo en el tablero compartido (`team_task_*`) e intercambiar mensajes con ellos. Hay una disciplina clara sobre cuándo formar equipo: subtareas independientes que puedan ir en paralelo, trabajo abundante y autocontenido, nivel de agua del contexto principal ya alto o necesidad de separar roles; una investigación puntual de un solo uso sigue yendo por subagent. Si vuelves a apagar el interruptor, de las cláusulas de equipo no queda rastro, y las herramientas de equipo no se retiran de las sesiones en marcha.
24
24
  - **Sincronizar la lista blanca de modelos de subagentes** — DSH tiene una lista blanca de autorización «permitir que los agentes elijan modelos para subagentes»: una ruta elegida en un pool pero no autorizada hace que el despacho explícito del modelo principal sea rechazado (en modo equipo, se marca con ⚠). Al encender este interruptor, la unión de los seis pools se escribe entera en esa lista blanca — switchman es la única fuente de verdad, sin configurar dos veces. La lista blanca entra en vigor según la instantánea de «nueva sesión»: la sincronización solo afecta a las sesiones abiertas a partir de entonces.
25
25
 
26
- La cabecera de la sesión da un feedback visual claro: la insignia ⚡ «equipo autónomo» y ◇ con el modelo real de la sesión actual; mientras hay una entrega automática en marcha, aparece un aviso en vivo: «entrega en curso · sesión de respaldo / compactación del contexto / continuación despertada».
26
+ La cabecera de la sesión da un feedback visual claro: la insignia ⚡ «equipo autónomo» y ◇ con el modelo real de la sesión actual; mientras hay una entrega automática en marcha, aparece un aviso en vivo: «entrega en curso · escribiendo el documento de entrega / compactación del contexto / continuación despertada».
27
27
 
28
28
  **4. Preferencias de idioma: pregunta una vez, lo recuerda para siempre.** Un desplegable para cada una de las tres: respuestas, comentarios de código y documentos; el ámbito puede ser global o por proyecto (`.switchman/lang.json`). No hace falta configurarlo: la primera vez que se necesite pregunta una sola vez, en el idioma de la interfaz de tu DSH, y una vez recordado, cada sesión lo respeta automáticamente.
29
29
 
@@ -75,7 +75,7 @@ La cabecera de la sesión da un feedback visual claro: la insignia ⚡ «equipo
75
75
  - **Pools de despacho** — seis tarjetas en dos filas de tres: arriba ligero / mecánico / principal; abajo alta dificultad / multimodal / revisión. Cada tarjeta agrupa los modelos candidatos por proveedor; marca «orden manual» y se convierte en una lista de prioridades numerada que se reordena con ↑ ↓ ×; junto a cada ruta puedes fijar el effort de razonamiento (por defecto «seguir el carril»). La línea de resumen superior se actualiza en vivo; en la captura: «pools configurados: 4/6 · posiciones en el ranking: 2 · modo advice».
76
76
  - **Orden por capacidad + modo de ejecución** — la unión de los modelos elegidos en los seis pools, ordenada por capacidad y con los más fuertes delante (en la captura glm-5.3 anclado en S y glm-5.3-flash anclado en A); se puede reordenar y recortar; el modo de ejecución va de advice / enforce (enforce = los modelos fuera del pool se rechazan sin más).
77
77
  - **Equipo de agentes** — los dos interruptores, modo de equipo y sincronización de la lista blanca, vienen apagados; en la captura están encendidos, y debajo de cada uno hay una línea de estado de sincronización.
78
- - **Nivel de agua del contexto** — los tres umbrales (en la captura 80000 / 120000 / 150000), el presupuesto de lectura por llamada (3000), el comportamiento del umbral duro (dejar pasar limitando / bloquear), el interruptor de entrega automática y el techo independiente para subagentes: todo en esta zona. Abajo, una línea de comandos: `/ctx-pause` pausa la intervención · `/ctx-resume` la reanuda · `/ctx-handover` respalda y entrega ahora mismo (conduce la sesión a la frontera de inactividad antes de compactar; el resultado puede tardar unos minutos).
78
+ - **Nivel de agua del contexto** — los tres umbrales (en la captura 80000 / 120000 / 150000), el presupuesto de lectura por llamada (3000), el comportamiento del umbral duro (dejar pasar limitando / bloquear), el interruptor de entrega automática y el techo independiente para subagentes: todo en esta zona. Abajo, una línea de comandos: `/ctx-pause` pausa la intervención · `/ctx-resume` la reanuda · `/ctx-handover` escribe el documento y entrega ahora mismo (conduce la sesión a la frontera de inactividad antes de compactar; el resultado puede tardar unos minutos).
79
79
  - **Enrutado por intención** — el interruptor maestro, la ruta de modelo pequeño del clasificador, el tipo de clasificador (auto / decision / chat), la lista de candidatos independiente (cada uno con su nota de capacidades), la ventana de caché / umbral de confianza / timeout / presupuesto de historial y un modelo de respaldo opcional. Al activarlo, el selector de modelos gana la entrada localizada «Automático (enrutado por intención)» y sobre el cuadro de entrada aparece el chip con el modelo en ruta y la confianza.
80
80
 
81
81
  4. **Verificar** — en la cabecera de la sesión aparece la insignia ⚡ «equipo autónomo» (al lado, ◇ muestra el modelo de la sesión actual); o pregúntale directamente al modelo «¿cómo se titula la última sección de tu system prompt?» — debería mencionar el reglamento de dsh-switchman.
@@ -96,13 +96,56 @@ Esto es todo el conjunto de valores tras una instalación nueva (fuente autorita
96
96
 
97
97
  **Pools de despacho**: los seis carriles (economy/mechanical/main/hard/vision/review) arrancan sin candidatos (= los modelos por defecto del sistema, sin intervenir); el orden manual y el effort fijado, apagados/vacíos; `dispatchEnforce` = `advice` (solo aconseja, no obliga).
98
98
 
99
- **Nivel de agua del contexto**: soft `80000` / hard `120000` / force `150000` tokens; presupuesto de lectura por llamada `3000`; conducta al pasarse del presupuesto `cap` (recorta y avisa); `wmAutoHandover` = encendido (en force, respaldo automático + entrega con compactación); el techo duro de subagentes = encendido (150k compartidas).
99
+ **Nivel de agua del contexto**: soft `80000` / hard `120000` / force `150000` tokens; presupuesto de lectura por llamada `3000`; conducta al pasarse del presupuesto `cap` (recorta y avisa); `wmAutoHandover` = encendido (en force, escritura del documento de entrega + compactación automáticas); el techo duro de subagentes = encendido (150k compartidas).
100
100
 
101
101
  **Agent Teams**: `teamsMode` = apagado; `syncWhitelist` = apagado (los dos interruptores son independientes entre sí).
102
102
 
103
103
  **Enrutado por intención** (no disponible de fábrica; hacen falta clasificador + al menos un candidato + encender el interruptor maestro): `intentEnabled` = apagado (apagado, la entrada Auto queda oculta y las sesiones que ya la tenían elegida caen en la cadena de respaldo); ruta del clasificador sin configurar; tipo de clasificador `chat`; lista de candidatos vacía (las categorías preinstaladas solo entran en el enrutado cuando se les asigna un modelo); ventana de caché `300` s / umbral de confianza para el cambio `80` / timeout de clasificación `60000` ms; historial de `6` mensajes / `2400` caracteres; `intentContextWindow` = `200000`.
104
104
 
105
- **Notas de serie**: ① que el enrutado por intención no venga disponible de serie es intencionado: tres pasos para activarlo (clasificador, candidatos, interruptor); ② la ventana efectiva de Auto vale 200000 de serie — la barrera de seguridad garantiza 0.8×ventana ≥ force (150000), así que la línea de compactación del núcleo (160000) queda siempre por detrás de la línea de entrega suave (150000); ③ lo más perceptible de serie es el trío de niveles: 80k avisa de delegar / 120k aprieta la lectura / 150k respalda y entrega automáticamente — sin tocar ningún ajuste.
105
+ **Recorte de contexto**: `trimEnabled` = encendido; `trimMode` = `auto` (totalmente automático); compuerta de crecimiento `22500` + incremento `50000` tokens; presión `60%` / emergencia `95%`; cola protegida `6` mensajes; tope de cadencia de folds `6` por hora; modelo secundario sin definir (hereda el modelo de la sesión) — detalles en la sección «Recorte automático del contexto» más abajo.
106
+
107
+ **Notas de serie**: ① que el enrutado por intención no venga disponible de serie es intencionado: tres pasos para activarlo (clasificador, candidatos, interruptor); ② la ventana efectiva de Auto vale 200000 de serie — la barrera de seguridad garantiza 0.8×ventana ≥ force (150000), así que la línea de compactación del núcleo (160000) queda siempre por detrás de la línea de entrega suave (150000); ③ lo más perceptible de serie es el trío de niveles: 80k avisa de delegar / 120k aprieta la lectura / 150k entrega automáticamente (documento + compactación) — sin tocar ningún ajuste.
108
+
109
+ ## Recorte automático del contexto
110
+
111
+ Las sesiones largas salen baratas: los tramos de conversación ya consumidos se pliegan en resúmenes deterministas compactos — de forma totalmente automática y sin romper la caché de prompts del proveedor. En un límite de inactividad, el motor de trim (`host/context-trim/`) elige los tramos que vale la pena plegar, un modelo secundario escribe el resumen (solo bajo presión de emergencia se recurre a un fold mecánico sin llamada al modelo secundario) y el fold se confirma en el estado de folds de la sesión. A partir de ahí, cada petición saliente se reconstruye desde ese estado — los bloques de resumen sustituyen a los tramos plegados y la cola viva queda intacta. La sesión almacenada jamás se reescribe, y nada ocurre en mitad de un stream.
112
+
113
+ Niveles de automatización (`trimMode`):
114
+
115
+ - **`off`** — solo medir: el muestreo de uso y de aciertos de caché sigue funcionando; sin banner ni folds.
116
+ - **`advisory`** — el modelo de la sesión se pliega a sí mismo: se exponen las herramientas `ctx_trim_fold` y `ctx_trim_status` más la cabecera de protocolo.
117
+ - **`auto`** (predeterminado) — todo lo anterior más folds automáticos en el límite de inactividad, en tres escalones: una compuerta de crecimiento (crecimiento > `trimGrowthTokens` medido desde el mínimo tras el último plegado — el ancla que mantiene la compuerta activa tras plegados que confirman en puntos altos de la view — Y un incremento comprimible contiguo ≥ `trimIncrementTokens`), un escalón de presión (`trimPressurePercent` de la ventana de contexto) y un escalón de emergencia (`trimEmergencyPercent` — un fold mecánico con su propio enfriamiento).
118
+
119
+ Todas las claves viven en Ajustes → dsh-switchman; los valores por defecto salen de `host/config.js`:
120
+
121
+ | Clave | Por defecto | Significado |
122
+ | --- | --- | --- |
123
+ | `trimEnabled` | `true` | Interruptor maestro. Apagado desactiva folds y métricas por completo (el listener degenera en puro paso a través). |
124
+ | `trimMode` | `auto` | `off` solo medir / `advisory` folds dictados por el modelo / `auto` folds dictados por el sistema. |
125
+ | `trimGrowthTokens` | `22500` | Compuerta de crecimiento: pliega cuando la view saliente crezca esto desde el último fold. |
126
+ | `trimIncrementTokens` | `50000` | Compañera de la compuerta: el incremento comprimible contiguo también debe alcanzar este tamaño. |
127
+ | `trimPressurePercent` | `60` | Escalón de presión: pliega cuando el uso de la view alcance este porcentaje de la ventana de contexto. |
128
+ | `trimEmergencyPercent` | `95` | Escalón de emergencia: fold mecánico forzado (sin llamada al modelo secundario) a este porcentaje. |
129
+ | `trimWindowTokens` | `0` | Límite de ventana efectivo para el cálculo de niveles: se calcula sobre min(ventana del modelo, límite). `0` = sigue a `wmForceTokens`. |
130
+ | `trimProtectedTail` | `6` | Los últimos N mensajes nunca se pliegan. |
131
+ | `trimMaxFoldsPerHour` | `6` | Tope de cadencia de folds por hora móvil para el modelo secundario (los de emergencia quedan exentos). |
132
+ | `trimSideModel` | sin definir | Ruta del modelo secundario que escribe los resúmenes. Vacía = heredar el modelo actual de la sesión. |
133
+ | `trimStateDir` | sin definir | Anula el directorio del estado de folds. Vacía = `<dshHome>/.dsh-switchman/state/trim/`. |
134
+ | `surfaceFold` | `true` | Envía los folds confirmados a la superficie de la sesión (par de eventos de medición shadow-price + replace con `surfaceOp`). Apagado conserva la vista de solo empalme. |
135
+
136
+ **Rutas de activación.** Las comprobaciones de escalones se ejecutan en el límite de inactividad y, como respaldo para hosts donde los eventos de estado de inactividad son inalcanzables, tras cada finalización del flujo del modelo (limitadas a una evaluación cada 60 s por sesión). Cada decisión — folds y no-ops por igual — se añade como una línea `key=value` con marca de tiempo a `decisions.log` en el directorio del estado de folds (véase `trimStateDir`), dando a los folds una pista de auditoría fuera de línea; el vocabulario de filas (`view-applied`, filas de decisión/resultado de folds, `surface-replace`, `surface-replace skipped reason=…`) está catalogado en [docs/context-trim.md](./docs/context-trim.md).
137
+
138
+ **Verdad del cable y estado estacionario.** Las filas de decisión llevan `view=<n>` y, una vez que una respuesta ha llegado, `wire=<n>` — el prompt real del proveedor (entrada sin caché + lectura de caché + escritura de caché, muestreado del evento de uso del stream). La estimación de la view cuenta solo el array de mensajes y siempre lee menos que el cable por la sobrecarga fija (medida ≈ 17.3k: system prompt + esquemas de herramientas + persona), así que los escalones de presión y emergencia evalúan sobre `max(view, wire)`, y solo `wire` es una lectura del tamaño del prompt. En una ventana de 150k, el diente de sierra sano marca el cable en ≈ 40k justo después de un fold ↔ 88–90k en el siguiente (≈27%–60% de la ventana), un fold por cada ~50k de crecimiento bruto, con el techo fijado cerca de 90k por el escalón de presión y no por la ventana. Si `view` sube sin que aparezca nunca una fila `surface-replace`, lee las filas de omisión (`incomplete-seqs`, `no-service`, `compaction-lock`).
139
+
140
+ **Épocas de compactación del host.** Las referencias de fold se anclan en ids de mensaje y hashes de contenido, de modo que una compactación del propio host que reemplace el historial reenviado hace envejecer el libro entero de golpe: ningún bloque localiza su posición, la vista retrocede silenciosamente al historial sin plegar (fail-open — el registro de decisiones anota `view-applied applied=0`) y el plugin reancla su línea base de crecimiento en la vista posterior a la compactación en la siguiente petición. La compactación del host es así la capa de recorte de último recurso; los folds del plugin hacen el trabajo fino dentro de una época del host. Algunos despachos entregan matrices de solicitud u objetos de opciones totalmente congelados (el bucle de agente actual congela en profundidad las peticiones ya montadas); para ellos, `injectView` informa del resultado en el registro de decisiones: `nothing-to-apply` (no hay bloques de fold localizados — el historial sin plegar ES la vista), `in-place`/`swapped` (la vista plegada viajó con la petición) o `none` (había un fold, pero el despacho congelado no podía llevarlo — por el cable va el historial sin plegar). **El reemplazo de superficie es la ruta de envío cuando está disponible** (`surfaceFold`, activado por defecto): en el límite serial `agent/turn-stopping` más un barrido de inactividad, el plugin añade el evento de medición shadow-price al estilo del host y UN reemplazo `user/message` que lleva `surfaceOp {op:'replace', startSeq, endSeq}` con `sourceEventSeqs` cubriendo cada nodo de superficie sombreado — el mismo mecanismo que usa la propia compactación del host —, de modo que un fold confirmado llega al cable desde el nivel de origen, despachos congelados incluidos. Mientras los despachos recientes de una sesión registran `none`, el plugin trata la vista como solo-medición: el nivel de agua mide en su lugar la cuenta que reporta el proveedor (el escalón force y la entrega automática siguen funcionando), los ciclos de fold se pausan en vez de quemar llamadas al resumidor, y el banner de recorte marca la línea con `wire=raw` (folds not shipping; watermark reads the tally); los hosts sin el mecanismo de superficie (o con la opción apagada) se quedan exactamente con ese comportamiento antiguo.
141
+
142
+ **Cómo los folds conservan los aciertos de caché.** Cada fold repaga el prefijo una vez; el objetivo es el coste amortizado, no una tasa de aciertos perfecta. El motor hace cumplir el contrato de caché: los folds son de solo añadir — la frontier solo avanza y la zona congelada no se vuelve a editar; la view se reconstruye de forma determinista a partir de (historial, estado de folds), de modo que el prefijo serializado queda idéntico byte a byte entre folds y la caché del proveedor sigue acertando; los límites de fold jamás separan una tool-call del assistant de su resultado; los últimos `trimProtectedTail` mensajes quedan exentos; y todo falla abierto — un error interno degenera en paso a través, nunca en una petición rota.
143
+
144
+ **Arbitraje de compactación.** Dos recortadores automáticos sobre un mismo historial se resumirían dos veces y destruirían la frontier mutuamente, así que `cordis.patch.yml` pone `auto: false` en las filas de `dsh-compaction-basic` del host: context-trim pasa a ser el único recortador semántico. El `/compact` manual sigue funcionando, y la entrega del escalón force (que toma prestado justamente ese camino manual) queda intacta. La recuperación de desbordamiento pasa al trim: el intento que excede la ventana falla una vez, luego el escalón de emergencia pliega la view para que el siguiente intento quepa.
145
+
146
+ **Herencia en forks.** Una sesión forkeada (fork de subagente) hereda el estado de folds del padre por copia al forkear — pero solo si el estado del padre existe y el historial del hijo realmente resuelve la frontier del padre; si no, el hijo arranca limpio. Una herencia por hijo, idempotente entre reinicios.
147
+
148
+ **El banner de niveles lee la view.** El banner de niveles informa del recuento de tokens de la view saliente (lo que el modelo ve de verdad tras el plegado) en lugar del recuento almacenado, así una sesión muy plegada ya no clava el nivel de agua en el escalón hard.
106
149
 
107
150
  ## Cómo funciona
108
151
 
@@ -113,9 +156,9 @@ Esto es todo el conjunto de valores tras una instalación nueva (fuente autorita
113
156
 
114
157
  ## Mantenimiento
115
158
 
116
- - Si tras una actualización de DSH cambia la lista de plugins de los presets de fábrica, resincroniza `cordis.patch.yml` desde los nuevos `presets/*.patch.yml` (conserva el doctrine suffix) y reinstala.
159
+ - Si tras una actualización de DSH cambia la lista de plugins de los presets de fábrica, resincroniza `cordis.patch.yml` desde los nuevos `presets/*.patch.yml` (conserva el doctrine suffix y el arbitraje de compactación — `auto: false` en las filas de `compaction-basic`) y reinstala.
117
160
  - Las líneas de protocolo (`[SWITCHMAN:LANG|POOLS|WATERMARK|TEAMS]`) están deliberadamente en inglés y son estables byte a byte — no las localices.
118
- - `npm pack --dry-run` debe mantenerse en la forma auditada de 50 archivos / ~205 kB (las capturas de `docs/` no entran en el paquete).
161
+ - `npm pack --dry-run` debe mantenerse en la forma auditada de 60 archivos / ~380 kB (~1,26 MB descomprimido; `docs/` queda excluido).
119
162
 
120
163
  ## License
121
164
 
package/README.fr.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  dsh-switchman est un plugin pour [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH). Une fois installé, le modèle principal passe de « tout faire lui-même » au rôle de répartiteur : mesurer le niveau d'eau, choisir le couloir, distribuer les tâches, contrôler le résultat. Ce n'est pas un nouveau modèle — c'est une doctrine d'orchestration accrochée à DSH, accompagnée d'une page de réglages. Concrètement, il fait huit choses :
16
16
 
17
- **1. Niveau d'eau du contexte : les longues sessions ne débordent jamais.** Chaque tour compte en direct les tokens de la session et les confronte à trois lignes d'eau qui durcissent par paliers : 80k (soft, réglable) rappelle qu'« il est temps de déléguer » ; 120k (hard) resserre le budget de lecture par appel et pousse à conclure ; 150k (force) déclenche la remise automatique — un fork de la session est archivé, le contexte compacté, la continuation réveillée, et la tâche ne s'interrompt jamais. Les subagents d'arrière-plan encore en course à cet instant ne sont pas perdus pour autant : leurs id, descriptions de tâche et chemins de rapport sont consignés dans le document de remise, et la session relancée sait où récupérer les rapports au lieu de redéspatcher en double. Chaque subagent dispatché embarque son propre plafond strict ; une fois atteint, il écrit son résumé HANDOFF et se retire. Quand l'intervention automatique ne vous convient pas, reprenez la main à tout moment : `/ctx-pause` arrête toutes les actions de niveau (la mesure continue, la ligne d'eau en direct de la fin passe en paused — il n'agit plus, c'est tout), `/ctx-resume` rétablit à tout moment — après un redémarrage de DSH, l'intervention revient aussi d'elle-même, un lâcher-prise temporaire, pas une extinction définitive ; `/ctx-handover` n'attend pas que le niveau touche le plafond et déclenche à la demande la même remise : forker une sauvegarde, compacter le contexte, réveiller la continuation (la session est d'abord conduite jusqu'à la limite d'inactivité, le résultat peut donc prendre quelques minutes).
17
+ **1. Niveau d'eau du contexte : les longues sessions ne débordent jamais.** Chaque tour compte en direct les tokens de la session et les confronte à trois lignes d'eau qui durcissent par paliers : 80k (soft, réglable) rappelle qu'« il est temps de déléguer » ; 120k (hard) resserre le budget de lecture par appel et pousse à conclure ; 150k (force) déclenche la remise automatique — le document de remise est écrit (le journal de session à ajout seul constitue la sauvegarde complète), le contexte compacté, la session réveillée pour continuer, et la tâche ne s'interrompt jamais. Les subagents d'arrière-plan encore en course à cet instant ne sont pas perdus pour autant : leurs id, descriptions de tâche et chemins de rapport sont consignés dans le document de remise, et la session relancée sait où récupérer les rapports au lieu de redéspatcher en double. Chaque subagent dispatché embarque son propre plafond strict ; une fois atteint, il écrit son résumé HANDOFF et se retire. Quand l'intervention automatique ne vous convient pas, reprenez la main à tout moment : `/ctx-pause` arrête toutes les actions de niveau (la mesure continue, la ligne d'eau en direct de la fin passe en paused — il n'agit plus, c'est tout), `/ctx-resume` rétablit à tout moment — après un redémarrage de DSH, l'intervention revient aussi d'elle-même, un lâcher-prise temporaire, pas une extinction définitive ; `/ctx-handover` n'attend pas que le niveau touche le plafond et déclenche à la demande la même remise : écrire le document de remise, compacter le contexte, réveiller la session pour continuer (la session est d'abord conduite jusqu'à la limite d'inactivité, le résultat peut donc prendre quelques minutes).
18
18
 
19
19
  **2. Six pools de dispatch : le bon modèle pour chaque tâche.** Un pool léger (economy, les petites tâches en série), un pool mécanique (mechanical, les réécritures sur gabarit), un pool principal (main, le code au quotidien), un pool haute difficulté (hard, raisonnement corsé et refontes à grande échelle), un pool multimodal (vision, lire les images) et un pool de relecture (review, vérification indépendante). Dans la page de réglages, vous cochez les candidats, les classez par priorité (avec tiers S/A/B/C en option) et épinglez un effort de raisonnement pour chaque route — le menu déroulant des niveaux reflète ce que ce modèle supporte réellement, pas trois niveaux génériques. Chaque prompt du modèle principal embarque une table de recommandations `[SWITCHMAN:POOLS]`, et le dispatch suit cette table. Le mode d'exécution a trois états : off / advice / enforce (enforce = tout modèle hors pool est refusé sans appel).
20
20
 
@@ -23,7 +23,7 @@ dsh-switchman est un plugin pour [DeepSeek Harness](https://github.com/deepseek-
23
23
  - **Mode équipe d'agents** — l'activer injecte la doctrine d'équipe (délégation par défaut + vérification par paliers + discipline du tableau de tâches partagé) et active automatiquement les Agent Teams de DSH : le modèle principal peut recruter des coéquipiers permanents (`spawn_teammate`), confier du travail au tableau de tâches partagé (`team_task_*`) et échanger des messages avec eux. Le moment de constituer une équipe obéit à une discipline explicite : sous-tâches indépendantes parallélisables, gros lots autoportants, contexte principal déjà bien chargé, besoin de séparer les rôles ; les enquêtes ponctuelles restent du ressort du subagent. Refermer l'interrupteur ne laisse aucun résidu des clauses d'équipe, et ne retire jamais les outils d'équipe aux sessions déjà en cours.
24
24
  - **Synchronisation de la liste blanche des modèles de subagents** — DSH tient une liste d'autorisation « modèles qu'un agent peut choisir pour ses subagents » : une route cochée dans un pool mais jamais autorisée se voit refuser le dispatch quand le modèle principal la désigne nommément (en mode équipe, elle est marquée d'un ⚠). Activez cet interrupteur et l'union des six pools est d'un coup écrite dans cette liste — switchman reste l'unique source de vérité, fini la double configuration. La liste blanche s'applique par instantané à chaque nouvelle session : la synchronisation ne concerne que les sessions ouvertes ensuite.
25
25
 
26
- L'en-tête de session donne un retour immédiat : un badge ⚡ « équipe autonome » et une puce ◇ indiquant le modèle réellement utilisé par la session ; pendant une remise automatique, une bannière en direct annonce « remise en cours · sauvegarde de la session / compactage du contexte / réveil de la continuation ».
26
+ L'en-tête de session donne un retour immédiat : un badge ⚡ « équipe autonome » et une puce ◇ indiquant le modèle réellement utilisé par la session ; pendant une remise automatique, une bannière en direct annonce « remise en cours · écriture du document de remise / compactage du contexte / réveil de la continuation ».
27
27
 
28
28
  **4. Préférences de langue : une question posée une fois, retenue pour toujours.** Un menu déroulant chacun pour les réponses, les commentaires de code et la documentation, avec un scope global ou par projet (`.switchman/lang.json`). Ne rien régler est permis — au premier besoin, on vous pose la question une fois dans la langue de votre interface DSH, la réponse est retenue, et chaque session suivante s'y conforme automatiquement.
29
29
 
@@ -75,7 +75,7 @@ Un seul modèle ? Ça vaut quand même l'installation — le contrôle du niveau
75
75
  - **Pools de dispatch** — six cartes sur deux rangées de trois : en haut léger / mécanique / principal ; en bas haute difficulté / multimodal / révision. Chaque carte coche les modèles candidats groupés par fournisseur ; cochez « ordre manuel » et elle devient une liste de priorités numérotée, à réordonner avec ↑ ↓ × ; chaque route peut épingler un effort de raisonnement (par défaut « suivre le couloir »). La ligne de résumé en haut se met à jour en direct ; sur la capture : « 4/6 pools configurés · 2 classés · mode advice ».
76
76
  - **Classement des capacités + mode d'exécution** — l'union des modèles cochés dans les six pools, classée par capacité, les plus forts devant (sur la capture glm-5.3 ancré S et glm-5.3-flash ancré A) ; on peut réordonner et retirer ; le mode d'exécution va de advice / enforce (enforce = tout modèle hors pool est refusé sans appel).
77
77
  - **Équipe d'agents** — les deux interrupteurs, mode équipe et synchronisation de la liste blanche, arrivent désactivés ; sur la capture ils sont activés, avec sous chacun une ligne d'état de synchronisation.
78
- - **Niveau d'eau du contexte** — les trois seuils (sur la capture 80000 / 120000 / 150000), le budget de lecture par appel (3000), le comportement du palier hard (passage à débit limité / blocage), l'interrupteur de remise automatique et le plafond indépendant des subagents : tout tient dans cette zone. La ligne du bas porte les commandes : `/ctx-pause` suspend les interventions · `/ctx-resume` reprend · `/ctx-handover` sauvegarde et remet immédiatement (il conduit la session jusqu'à une limite d'inactivité avant de compacter — le résultat peut prendre quelques minutes).
78
+ - **Niveau d'eau du contexte** — les trois seuils (sur la capture 80000 / 120000 / 150000), le budget de lecture par appel (3000), le comportement du palier hard (passage à débit limité / blocage), l'interrupteur de remise automatique et le plafond indépendant des subagents : tout tient dans cette zone. La ligne du bas porte les commandes : `/ctx-pause` suspend les interventions · `/ctx-resume` reprend · `/ctx-handover` écrit le document et remet immédiatement (il conduit la session jusqu'à une limite d'inactivité avant de compacter — le résultat peut prendre quelques minutes).
79
79
  - **Routage par intention** — l'interrupteur général, la route du petit modèle classifieur, le type de classifieur (auto / decision / chat), la liste de candidats indépendante (chacun avec sa note de capacité), la fenêtre de cache / seuil de confiance / timeout / budget d'historique et un modèle de repli facultatif. Une fois activé, le sélecteur de modèles gagne l'entrée localisée « Automatique (routage par intention) », et une pastille au-dessus du champ de saisie affiche le modèle routé et la confiance.
80
80
 
81
81
  4. **Vérifier** — un badge ⚡ « équipe autonome » apparaît dans l'en-tête de session (avec à côté une puce ◇ montrant le modèle de la session en cours) ; ou demandez simplement au modèle « quel est le titre du dernier paragraphe de ton prompt système » — la réponse doit mentionner la doctrine dsh-switchman.
@@ -96,13 +96,56 @@ Voici l'ensemble complet des valeurs juste après une installation neuve (source
96
96
 
97
97
  **Pools de dispatch** : les six couloirs (economy/mechanical/main/hard/vision/review) démarrent sans candidats (= les modèles par défaut du système, aucune intervention) ; l'ordre manuel et l'effort épinglé sont désactivés/vides ; `dispatchEnforce` = `advice` (simple recommandation, rien n'est forcé).
98
98
 
99
- **Niveau d'eau du contexte** : soft `80000` / hard `120000` / force `150000` tokens ; budget de lecture par appel `3000` ; comportement en cas de dépassement `cap` (tronquer avec avertissement) ; `wmAutoHandover` = activé (à force, sauvegarde automatique + remise avec compactage) ; le plafond strict des subagents = activé (150k partagées).
99
+ **Niveau d'eau du contexte** : soft `80000` / hard `120000` / force `150000` tokens ; budget de lecture par appel `3000` ; comportement en cas de dépassement `cap` (tronquer avec avertissement) ; `wmAutoHandover` = activé (à force, écriture du document de remise + compactage automatiques) ; le plafond strict des subagents = activé (150k partagées).
100
100
 
101
101
  **Agent Teams** : `teamsMode` = désactivé ; `syncWhitelist` = désactivé (les deux interrupteurs sont indépendants l'un de l'autre).
102
102
 
103
103
  **Routage par intention** (indisponible par défaut ; il exige un classifieur + au moins un candidat + l'interrupteur général) : `intentEnabled` = désactivé (désactivé, l'entrée Auto reste cachée et les sessions déjà passées en Auto empruntent la chaîne de repli) ; route du classifieur non définie ; type de classifieur `chat` ; liste de candidats vide (les catégories préinstallées ne participent au routage qu'une fois un modèle renseigné) ; fenêtre de cache `300` s / seuil de confiance du basculement `80` / timeout de classification `60000` ms ; historique de `6` messages / `2400` caractères ; `intentContextWindow` = `200000`.
104
104
 
105
- **À savoir dès l'installation** : ① l'indisponibilité du routage par intention en sortie d'usine est un choix délibéré — trois étapes pour l'activer (classifieur, candidats, interrupteur) ; ② la fenêtre effective d'Auto vaut 200000 dès l'usine — le garde-fou garantit 0.8×fenêtre ≥ force (150000), si bien que la ligne de compactage cœur (160000) se situe toujours après la ligne de remise en douceur (150000) ; ③ l'effet le plus tangible dès l'usine est le trio de niveaux : 80k rappelle de déléguer / 120k resserre la lecture / 150k sauvegarde et remet automatiquement — sans aucun réglage.
105
+ **Trim de contexte** : `trimEnabled` = activé ; `trimMode` = `auto` (entièrement automatique) ; porte de croissance `22500` + incrément `50000` tokens ; pression `60%` / urgence `95%` ; queue protégée `6` messages ; plafond de cadence des folds `6` par heure ; modèle annexe non défini (hérite du modèle de la session) — détails dans la section « Trim automatique du contexte » ci-dessous.
106
+
107
+ **À savoir dès l'installation** : ① l'indisponibilité du routage par intention en sortie d'usine est un choix délibéré — trois étapes pour l'activer (classifieur, candidats, interrupteur) ; ② la fenêtre effective d'Auto vaut 200000 dès l'usine — le garde-fou garantit 0.8×fenêtre ≥ force (150000), si bien que la ligne de compactage cœur (160000) se situe toujours après la ligne de remise en douceur (150000) ; ③ l'effet le plus tangible dès l'usine est le trio de niveaux : 80k rappelle de déléguer / 120k resserre la lecture / 150k remet automatiquement (document + compactage) — sans aucun réglage.
108
+
109
+ ## Trim automatique du contexte
110
+
111
+ Les longues sessions restent bon marché : les plages de conversation consommées sont repliées en résumés déterministes compacts — entièrement automatiquement, sans casser le cache de prompts du fournisseur. À une frontière d'inactivité, le moteur de trim (`host/context-trim/`) choisit les plages qui valent un repli, un modèle annexe écrit le résumé (sous pression d'urgence seulement, c'est un fold mécanique sans appel de modèle annexe), et le fold est validé dans l'état de folds de la session. Dès lors, chaque requête sortante est reconstruite depuis cet état — des blocs de résumé remplacent les plages repliées, la queue vive reste intacte. La session stockée n'est jamais réécrite, et rien ne se passe en plein stream.
112
+
113
+ Niveaux d'automatisation (`trimMode`) :
114
+
115
+ - **`off`** — mesure seule : l'échantillonnage de l'usage et des hits de cache tourne toujours ; pas de bannière, pas de folds.
116
+ - **`advisory`** — le modèle de session se replie lui-même : les outils `ctx_trim_fold` et `ctx_trim_status` plus l'en-tête de protocole sont exposés.
117
+ - **`auto`** (par défaut) — tout ce qui précède, plus des folds automatiques à la frontière d'inactivité, en trois paliers : une porte de croissance (croissance > `trimGrowthTokens` mesurée depuis le point bas après le dernier pli — l'ancre qui garde la porte armée après des plis confirmés à des points hauts de la view — ET un incrément compressible contigu ≥ `trimIncrementTokens`), un palier de pression (`trimPressurePercent` de la fenêtre de contexte) et un palier d'urgence (`trimEmergencyPercent` — un fold mécanique avec son propre refroidissement).
118
+
119
+ Toutes les clés vivent dans Réglages → dsh-switchman ; les valeurs par défaut viennent de `host/config.js` :
120
+
121
+ | Clé | Par défaut | Signification |
122
+ | --- | --- | --- |
123
+ | `trimEnabled` | `true` | Interrupteur maître. Désactivé, folding et métriques s'arrêtent totalement (le listener se dégrade en simple passage). |
124
+ | `trimMode` | `auto` | `off` mesure seule / `advisory` folds pilotés par le modèle / `auto` folds pilotés par le système. |
125
+ | `trimGrowthTokens` | `22500` | Porte de croissance : on plie quand la view sortante a grandi d'autant depuis le dernier fold. |
126
+ | `trimIncrementTokens` | `50000` | Compagnon de la porte : l'incrément compressible contigu doit aussi atteindre cette taille. |
127
+ | `trimPressurePercent` | `60` | Palier de pression : on plie quand l'usage de la view atteint ce pourcentage de la fenêtre de contexte. |
128
+ | `trimEmergencyPercent` | `95` | Palier d'urgence : fold mécanique forcé (sans appel de modèle annexe) à ce pourcentage. |
129
+ | `trimWindowTokens` | `0` | Plafond de fenêtre effectif pour le calcul des paliers : calculé sur min(fenêtre du modèle, plafond). `0` = suit `wmForceTokens`. |
130
+ | `trimProtectedTail` | `6` | Les N derniers messages ne sont jamais repliés. |
131
+ | `trimMaxFoldsPerHour` | `6` | Plafond de cadence des folds par heure glissante pour le modèle annexe (les folds d'urgence en sont exemptés). |
132
+ | `trimSideModel` | non définie | Route du modèle annexe qui écrit les résumés. Vide = hériter du modèle courant de la session. |
133
+ | `trimStateDir` | non défini | Redéfinit le répertoire d'état des folds. Vide = `<dshHome>/.dsh-switchman/state/trim/`. |
134
+ | `surfaceFold` | `true` | Expédie les folds validés sur la surface de session (paire d'événements : mesure shadow-price + remplacement `surfaceOp`). Désactivé, la vue reste en splice seul. |
135
+
136
+ **Chemins de déclenchement.** Les vérifications de paliers s'exécutent à la frontière d'inactivité et, en repli pour les hôtes où les événements d'état d'inactivité sont inaccessibles, après chaque fin de flux du modèle (limitées à une évaluation par 60 s et par session). Chaque décision — folds comme no-ops — est ajoutée comme une ligne `key=value` horodatée à `decisions.log` dans le répertoire d'état des folds (voir `trimStateDir`), donnant aux folds une piste d'audit hors ligne; le vocabulaire des lignes (`view-applied`, lignes de décision/résultat de fold, `surface-replace`, `surface-replace skipped reason=…`) est catalogué dans [docs/context-trim.md](./docs/context-trim.md).
137
+
138
+ **Vérité du fil et régime stationnaire.** Les lignes de décision portent `view=<n>` et, une fois qu'une réponse est arrivée, `wire=<n>` — le prompt réel du fournisseur (entrée non cachée + lecture de cache + écriture de cache, échantillonné depuis l'événement d'usage du stream). L'estimation de la view ne compte que le tableau de messages et lit toujours moins que le fil, du surcoût fixe (mesuré ≈ 17.3k : system prompt + schémas d'outils + persona), donc les paliers de pression et d'urgence évaluent sur `max(view, wire)`, et seul `wire` est une lecture de la taille du prompt. Sur une fenêtre de 150k, la dent de scie saine fait tourner le fil à ≈ 40k juste après un fold ↔ 88–90k au suivant (≈27%–60% de la fenêtre), un fold par ~50k de croissance brute, le plafond étant arrimé près de 90k par le palier de pression plutôt que par la fenêtre. Si `view` monte sans qu'aucune ligne `surface-replace` n'apparaisse jamais, lisez les lignes d'omission (`incomplete-seqs`, `no-service`, `compaction-lock`).
139
+
140
+ **Époques de compactage de l'hôte.** Les références de fold sont ancrées dans les identifiants de messages et les hachages de contenu ; quand l'hôte compacte lui-même l'historique renvoyé, tout le registre vieillit d'un coup : plus aucun bloc ne se localise, la vue retombe silencieusement sur l'historique brut (fail-open — le journal des décisions note `view-applied applied=0`), et le plugin ré-ancre sa base de croissance sur la vue post-compaction à la requête suivante. La compaction de l'hôte est donc la couche de rognage de dernier recours ; les folds du plugin font le travail fin au sein d'une époque de l'hôte. Certains dispatches remettent des tableaux de requête ou des objets d'options entièrement gelés (la boucle d'agent actuelle gèle en profondeur les requêtes assemblées) ; pour eux, `injectView` rapporte l'issue dans le journal des décisions : `nothing-to-apply` (aucun bloc de fold localisé — l'historique brut EST la vue), `in-place`/`swapped` (la vue pliée est partie avec la requête) ou `none` (un fold existait mais le dispatch gelé ne pouvait pas le transporter — l'historique brut part sur le fil). **Le remplacement de surface est le chemin d'expédition quand il est disponible** (`surfaceFold`, activé par défaut) : à la frontière sérielle `agent/turn-stopping` plus un balayage d'inactivité, le plugin ajoute l'événement de mesure shadow-price à la manière de l'hôte et UN remplacement `user/message` portant `surfaceOp {op:'replace', startSeq, endSeq}` avec des `sourceEventSeqs` couvrant chaque nœud de surface masqué — le même mécanisme que celui de la compaction de l'hôte elle-même —, de sorte qu'un fold validé rejoint le fil depuis le niveau source, dispatches gelés compris. Tant que les dispatches récents d'une session consignent `none`, le plugin traite la vue comme une simple mesure : le niveau d'eau lit à la place le total rapporté par le fournisseur (le palier force et la remise automatique continuent de fonctionner), les cycles de fold se mettent en pause au lieu de brûler des appels au résumeur, et la bannière de rognage marque la ligne avec `wire=raw` (folds not shipping; watermark reads the tally) ; les hôtes sans le mécanisme de surface (ou avec l'option désactivée) gardent exactement cet ancien comportement.
141
+
142
+ **Comment les folds préservent les hits de cache.** Chaque fold repaye le préfixe une fois ; l'objectif est le coût amorti, pas un taux de hits parfait. Le moteur fait respecter le contrat de cache : les folds sont en ajout seul — la frontier n'avance que vers l'avant et la zone gelée n'est plus jamais éditée ; la view est reconstruite de façon déterministe depuis (historique, état des folds), donc le préfixe sérialisé reste identique à l'octet entre les folds et le cache du fournisseur continue de toucher ; les frontières de fold ne séparent jamais un tool-call d'assistant de son résultat ; les `trimProtectedTail` derniers messages sont exemptés ; et tout échoue ouvert — une erreur interne se dégrade en passage à travers, jamais en requête cassée.
143
+
144
+ **Arbitrage du compactage.** Deux mécanismes de trim automatiques sur un même historique résumeraient tout deux fois et détruiraient mutuellement leur frontier, donc `cordis.patch.yml` met `auto: false` sur les lignes `dsh-compaction-basic` de l'hôte : context-trim devient l'unique trimmer sémantique. Le `/compact` manuel continue de marcher, et la remise du palier force (qui emprunte justement ce chemin manuel) reste intacte. La récupération de débordement passe au trim : la tentative au-dessus de la fenêtre échoue une fois, puis le palier d'urgence plie la view pour que la tentative suivante tienne.
145
+
146
+ **Héritage des forks.** Une session forkée (fork de sous-agent) hérite de l'état de folds du parent par copie-au-fork — mais seulement si l'état parent existe et si l'historique de l'enfant résout réellement la frontier du parent ; sinon l'enfant démarre propre. Un héritage par enfant, idempotent entre redémarrages.
147
+
148
+ **La bannière de niveaux lit la view.** La bannière de niveaux rapporte le nombre de tokens de la view sortante (ce que le modèle voit réellement après repli) au lieu du décompte stocké — une session fortement repliée n'épingle donc plus le niveau d'eau au palier hard.
106
149
 
107
150
  ## Fonctionnement
108
151
 
@@ -113,9 +156,9 @@ Voici l'ensemble complet des valeurs juste après une installation neuve (source
113
156
 
114
157
  ## Maintenance
115
158
 
116
- - Si une mise à niveau de DSH change la liste de plugins des presets d'usine, resynchronisez `cordis.patch.yml` depuis les nouveaux `presets/*.patch.yml` (conservez le doctrine suffix), puis réinstallez.
159
+ - Si une mise à niveau de DSH change la liste de plugins des presets d'usine, resynchronisez `cordis.patch.yml` depuis les nouveaux `presets/*.patch.yml` (conservez le doctrine suffix et l'arbitrage du compactage — `auto: false` sur les lignes `compaction-basic`), puis réinstallez.
117
160
  - Les lignes de protocole (`[SWITCHMAN:LANG|POOLS|WATERMARK|TEAMS]`) restent volontairement en anglais et stables à l'octet — ne les localisez pas.
118
- - `npm pack --dry-run` doit conserver la forme auditée de 50 fichiers / ~205 kB (les captures d'écran de `docs/` ne partent pas dans le paquet).
161
+ - `npm pack --dry-run` doit conserver la forme auditée de 60 fichiers / ~380 kB (~1,26 Mo décompressés ; `docs/` est exclu).
119
162
 
120
163
  ## License
121
164
 
package/README.it.md CHANGED
@@ -14,7 +14,7 @@ Se lavori con DSH, col passare del tempo ti imbatti in due cose: la sessione pes
14
14
 
15
15
  dsh-switchman è un plugin per [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH). Una volta installato, il modello principale smette di «fare tutto da solo» e diventa un dispatcher: misura il livello dell'acqua, sceglie la corsia, assegna i task, verifica il risultato. Non è un nuovo modello: è un regolamento di orchestrazione agganciato a DSH più un'interfaccia di configurazione. In concreto fa otto cose:
16
16
 
17
- **1. Livello dell'acqua del contesto: le sessioni lunghe non esplodono.** A ogni turno conta i token della sessione in tempo reale, e tre soglie inaspriscono a gradini: 80k (soft, regolabile) ricorda che «è ora di delegare»; 120k (hard) stringe il budget di lettura per singola chiamata e spinge verso la chiusura; 150k (force) avvia l'handover automatico — fork della sessione come copia di backup, contesto compattato, continuazione risvegliata: il task non si interrompe. Nemmeno i subagent in background ancora in corsa al momento del passaggio si perdono: id, descrizione del task e percorso del report finiscono nel documento di handover, così la sessione ripresa sa dove raccogliere i report invece di ri-assegnare lo stesso lavoro. Ogni subagent inviato porta con sé un tetto massimo indipendente e, raggiunto il limite, esce dopo aver scritto il riepilogo HANDOFF. Se l'intervento automatico non ti torna, prendi tu il comando in qualsiasi momento: `/ctx-pause` ferma tutte le azioni della soglia (la misurazione continua, la riga del livello dell'acqua in tempo reale in coda passa a paused — smette solo di agire), `/ctx-resume` ripristina in qualsiasi momento — dopo un riavvio di DSH l'intervento torna anche da solo, un mollare la presa temporaneo, non uno spegnimento definitivo; `/ctx-handover` non aspetta che la soglia tocchi il limite e avvia su richiesta lo stesso handover: fork di una copia di backup, compattazione del contesto, risveglio della continuazione (la sessione viene prima guidata fino al confine di inattività, quindi il risultato può richiedere qualche minuto).
17
+ **1. Livello dell'acqua del contesto: le sessioni lunghe non esplodono.** A ogni turno conta i token della sessione in tempo reale, e tre soglie inaspriscono a gradini: 80k (soft, regolabile) ricorda che «è ora di delegare»; 120k (hard) stringe il budget di lettura per singola chiamata e spinge verso la chiusura; 150k (force) avvia l'handover automatico — scrittura del documento di handover (il log di sessione append-only è la copia completa), contesto compattato, sessione risvegliata per continuare: il task non si interrompe. Nemmeno i subagent in background ancora in corsa al momento del passaggio si perdono: id, descrizione del task e percorso del report finiscono nel documento di handover, così la sessione ripresa sa dove raccogliere i report invece di ri-assegnare lo stesso lavoro. Ogni subagent inviato porta con sé un tetto massimo indipendente e, raggiunto il limite, esce dopo aver scritto il riepilogo HANDOFF. Se l'intervento automatico non ti torna, prendi tu il comando in qualsiasi momento: `/ctx-pause` ferma tutte le azioni della soglia (la misurazione continua, la riga del livello dell'acqua in tempo reale in coda passa a paused — smette solo di agire), `/ctx-resume` ripristina in qualsiasi momento — dopo un riavvio di DSH l'intervento torna anche da solo, un mollare la presa temporaneo, non uno spegnimento definitivo; `/ctx-handover` non aspetta che la soglia tocchi il limite e avvia su richiesta lo stesso handover: scrittura del documento di handover, compattazione del contesto, risveglio della sessione per continuare (la sessione viene prima guidata fino al confine di inattività, quindi il risultato può richiedere qualche minuto).
18
18
 
19
19
  **2. Sei pool di dispatch: a ogni lavoro il suo modello.** Pool leggero (economy, piccoli lavori in serie), pool meccanico (mechanical, riscritture modellate), pool principale (main, coding di tutti i giorni), pool ad alta difficoltà (hard, ragionamento complesso e refactoring su larga scala), pool multimodale (vision, lettura delle immagini), pool di revisione (review, verifica indipendente). Nella pagina delle impostazioni spunti i candidati, ordini le priorità (con fasce opzionali S/A/B/C) e fichi un reasoning effort per ogni route — il menu a tendina elenca i livelli che quel modello supporta davvero, non tre livelli generici. A ogni turno il prompt del modello principale viaggia con una tabella di raccomandazioni `[SWITCHMAN:POOLS]`: si assegna il lavoro seguendo la tabella. La modalità di esecuzione ha tre stati: off / advice / enforce (enforce = i modelli fuori pool vengono rifiutati sul posto).
20
20
 
@@ -23,7 +23,7 @@ dsh-switchman è un plugin per [DeepSeek Harness](https://github.com/deepseek-ai
23
23
  - **Modalità Agent Teams** — una volta attivata, inietta la dottrina di squadra (delega per default + verifica a livelli + disciplina del task board condiviso) e abilita automaticamente gli Agent Teams di DSH: il modello principale può arruolare compagni permanenti (`spawn_teammate`), assegnare lavori al task board condiviso (`team_task_*`) e scambiarsi messaggi con loro. Quando formare la squadra segue una disciplina precisa: sotto-task indipendenti parallelizzabili, lavori voluminosi e autosufficienti, livello dell'acqua del contesto principale già alto, necessità di separare i ruoli; le indagini una-tantum restano al subagent. Ri-spegnendo l'interruttore, le clausole di squadra spariscono senza residui, e gli strumenti di squadra nelle sessioni in corso non vengono ritirati.
24
24
  - **Sincronizzazione della whitelist dei modelli per i subagent** — DSH ha una whitelist di autorizzazione «Consenti agli agenti di scegliere i modelli per i subagent»: le route scelte nei pool ma non autorizzate vengono rifiutate se il modello principale le nomina esplicitamente (in modalità squadra, vengono marcate con ⚠). Con questo interruttore attivo, l'unione dei sei pool viene scritta in un colpo solo in quella whitelist — niente configurazione doppia: switchman è l'unica fonte di verità. La whitelist scatta come snapshot alla «nuova sessione»: la sincronizzazione vale solo per le sessioni aperte dopo.
25
25
 
26
- L'intestazione della sessione dà un riscontro immediato: badge ⚡ «Squadra autonoma», ◇ modello effettivo della sessione in corso; durante un handover automatico compare un avviso in tempo reale del tipo «Handover in corso · backup della sessione / compattazione del contesto / risveglio della continuazione».
26
+ L'intestazione della sessione dà un riscontro immediato: badge ⚡ «Squadra autonoma», ◇ modello effettivo della sessione in corso; durante un handover automatico compare un avviso in tempo reale del tipo «Handover in corso · scrittura del documento di handover / compattazione del contesto / risveglio della continuazione».
27
27
 
28
28
  **4. Preferenze linguistiche: chiede una volta, ricorda per sempre.** Un menu a tendina per ciascuna delle tre lingue — risposte, commenti nel codice, documenti — con ambito a scelta globale o per progetto (`.switchman/lang.json`). Puoi anche non impostarlo: al primo utilizzo ti viene chiesto una volta, nella lingua dell'interfaccia DSH, e da lì in poi ogni sessione lo rispetta da sé.
29
29
 
@@ -75,7 +75,7 @@ Un solo modello? Vale comunque la pena installarlo — il controllo del livello
75
75
  - **Pool di dispatch** — sei schede su due righe da tre: in alto leggero / meccanico / principale; in basso alta difficoltà / multimodale / revisione. In ogni scheda si spuntano i candidati raggruppati per provider; spunta «ordine manuale» e diventa una lista di priorità numerata da riordinare con ↑ ↓ ×; accanto a ogni route puoi fichare il reasoning effort (predefinito «segui la corsia»). La riga di riepilogo in alto si aggiorna in tempo reale; nella schermata: «4/6 pool configurati · 2 in classifica · modalità advice».
76
76
  - **Classifica di capacità + modalità di esecuzione** — l'unione dei modelli scelti nei sei pool, ordinata per capacità con i più forti davanti (nella schermata glm-5.3 ancorato in S e glm-5.3-flash ancorato in A); si può riordinare e ridurre; la modalità di esecuzione va da advice / enforce (enforce = i modelli fuori pool vengono rifiutati sul posto).
77
77
  - **Squadra di agenti** — i due interruttori, modalità squadra e sincronizzazione della whitelist, partono spenti; nella schermata sono accesi, con sotto una riga di stato della sincronizzazione.
78
- - **Livello dell'acqua del contesto** — le tre soglie (nella schermata 80000 / 120000 / 150000), il budget di lettura per singola chiamata (3000), il comportamento della soglia hard (passa con limitazione / blocca), l'interruttore dell'handover automatico e il tetto indipendente dei subagent: tutto in quest'area. Una riga di comandi in fondo: `/ctx-pause` sospende gli interventi · `/ctx-resume` li riprende · `/ctx-handover` fa subito backup e passaggio di consegne (guida la sessione fino al confine di inattività prima di compattare: il risultato può richiedere alcuni minuti).
78
+ - **Livello dell'acqua del contesto** — le tre soglie (nella schermata 80000 / 120000 / 150000), il budget di lettura per singola chiamata (3000), il comportamento della soglia hard (passa con limitazione / blocca), l'interruttore dell'handover automatico e il tetto indipendente dei subagent: tutto in quest'area. Una riga di comandi in fondo: `/ctx-pause` sospende gli interventi · `/ctx-resume` li riprende · `/ctx-handover` scrive il documento e passa le consegne subito (guida la sessione fino al confine di inattività prima di compattare: il risultato può richiedere alcuni minuti).
79
79
  - **Instradamento per intent** — l'interruttore principale, la route del piccolo modello classificatore, il tipo di classificatore (auto / decision / chat), la lista di candidati indipendente (ciascuno con la sua nota di capacità), finestra di cache / soglia di confidenza / timeout / budget di cronologia e un modello di fallback facoltativo. Attivandolo, il selettore dei modelli guadagna la voce localizzata «Automatico (instradamento per intent)» e sopra la casella di input compare la chip con il modello di instradamento e la confidenza.
80
80
 
81
81
  4. **Verifica** — nell'intestazione della sessione compare il badge ⚡ «Squadra autonoma» (accanto, il ◇ mostra il modello della sessione in corso); oppure chiedi direttamente al modello «qual è il titolo dell'ultima sezione del tuo system prompt?» — la risposta dovrebbe menzionare la dottrina dsh-switchman.
@@ -96,13 +96,56 @@ Questi sono tutti i valori così come si presentano dopo una nuova installazione
96
96
 
97
97
  **Pool di dispatch**: le sei corsie (economy/mechanical/main/hard/vision/review) partono senza candidati (= i modelli predefiniti del sistema, nessuna interferenza); ordine manuale ed effort fissato spenti/vuoti; `dispatchEnforce` = `advice` (solo consiglio, niente di imposto).
98
98
 
99
- **Livello dell'acqua del contesto**: soft `80000` / hard `120000` / force `150000` token; budget di lettura per singola chiamata `3000`; comportamento oltre budget `cap` (tronca con avviso); `wmAutoHandover` = attivo (a force, backup automatico + consegna con compattazione); il tetto massimo dei subagent = attivo (150k condivisi).
99
+ **Livello dell'acqua del contesto**: soft `80000` / hard `120000` / force `150000` token; budget di lettura per singola chiamata `3000`; comportamento oltre budget `cap` (tronca con avviso); `wmAutoHandover` = attivo (a force, scrittura del documento di handover + compattazione automatiche); il tetto massimo dei subagent = attivo (150k condivisi).
100
100
 
101
101
  **Agent Teams**: `teamsMode` = spento; `syncWhitelist` = spento (i due interruttori sono indipendenti tra loro).
102
102
 
103
103
  **Instradamento per intent** (non disponibile per predefinito; servono classificatore + almeno un candidato + l'interruttore principale): `intentEnabled` = spento (spento, la voce Auto resta nascosta e le sessioni che l'avevano già scelta percorrono la catena di fallback); route del classificatore non impostata; tipo di classificatore `chat`; lista dei candidati vuota (le categorie preinstallate entrano nell'instradamento solo con un modello assegnato); finestra di cache `300` s / soglia di confidenza del cambio `80` / timeout di classificazione `60000` ms; cronologia di `6` messaggi / `2400` caratteri; `intentContextWindow` = `200000`.
104
104
 
105
- **Note di serie**: ① l'instradamento per intent non disponibile di serie è una scelta voluta — tre passi per attivarlo (classificatore, candidati, interruttore); ② la finestra effettiva di Auto vale di serie 200000 — la barriera di sicurezza garantisce 0.8×finestra ≥ force (150000), così la linea di compattazione del cuore (160000) resta sempre dopo la linea di consegna morbida (150000); ③ di serie la cosa più percettibile è il terzetto di soglie: 80k ricorda di delegare / 120k stringe la lettura / 150k fa da solo backup e consegna — senza alcuna impostazione.
105
+ **Trim del contesto**: `trimEnabled` = acceso; `trimMode` = `auto` (del tutto automatico); cancello di crescita `22500` + incremento `50000` token; pressione `60%` / emergenza `95%`; coda protetta `6` messaggi; tetto di cadenza dei fold `6` all'ora; modello secondario non impostato (eredita il modello della sessione) — dettagli nella sezione «Trim automatico del contesto» più sotto.
106
+
107
+ **Note di serie**: ① l'instradamento per intent non disponibile di serie è una scelta voluta — tre passi per attivarlo (classificatore, candidati, interruttore); ② la finestra effettiva di Auto vale di serie 200000 — la barriera di sicurezza garantisce 0.8×finestra ≥ force (150000), così la linea di compattazione del cuore (160000) resta sempre dopo la linea di consegna morbida (150000); ③ di serie la cosa più percettibile è il terzetto di soglie: 80k ricorda di delegare / 120k stringe la lettura / 150k consegna da sola (documento + compattazione) — senza alcuna impostazione.
108
+
109
+ ## Trim automatico del contesto
110
+
111
+ Le sessioni lunghe restano economiche: le porzioni di conversazione già consumate vengono ripiegate in riassunti deterministici compatti — in modo del tutto automatico e senza rompere la cache dei prompt del fornitore. A un confine di inattività, il motore di trim (`host/context-trim/`) sceglie le porzioni che valgono un fold, un modello secondario scrive il riassunto (solo sotto pressione di emergenza si ricorre a un fold meccanico senza chiamata al modello secondario) e il fold viene registrato nello stato dei fold della sessione. Da lì in poi ogni richiesta in uscita viene ricostruita da quello stato — i blocchi di riassunto sostituiscono le porzioni ripiegate, la coda viva resta intatta. La sessione memorizzata non viene mai riscritta, e nulla accade a metà stream.
112
+
113
+ Livelli di automazione (`trimMode`):
114
+
115
+ - **`off`** — solo misura: il campionamento di uso e hit di cache continua; niente banner, niente fold.
116
+ - **`advisory`** — il modello della sessione si ripiega da sé: vengono esposti gli strumenti `ctx_trim_fold` e `ctx_trim_status` più l'intestazione di protocollo.
117
+ - **`auto`** (predefinito) — tutto quanto sopra più fold automatici al confine di inattività, su tre scalini: un cancello di crescita (crescita > `trimGrowthTokens` misurata dal minimo post-ripiegamento dell'ultimo fold — l'ancora che mantiene il cancello armato dopo ripiegamenti confermati su punti alti della view — E un incremento comprimibile contiguo ≥ `trimIncrementTokens`), uno scalino di pressione (`trimPressurePercent` della finestra di contesto) e uno scalino di emergenza (`trimEmergencyPercent` — un fold meccanico con il proprio raffreddamento).
118
+
119
+ Tutte le chiavi stanno in Impostazioni → dsh-switchman; i valori predefiniti vengono da `host/config.js`:
120
+
121
+ | Chiave | Predefinito | Significato |
122
+ | --- | --- | --- |
123
+ | `trimEnabled` | `true` | Interruttore principale. Spento disattiva fold e metriche del tutto (il listener degrada a puro pass-through). |
124
+ | `trimMode` | `auto` | `off` solo misura / `advisory` fold guidati dal modello / `auto` fold guidati dal sistema. |
125
+ | `trimGrowthTokens` | `22500` | Cancello di crescita: ripiega quando la view in uscita è cresciuta di tanto dall'ultimo fold. |
126
+ | `trimIncrementTokens` | `50000` | Compagno del cancello: anche l'incremento comprimibile contiguo deve raggiungere questa taglia. |
127
+ | `trimPressurePercent` | `60` | Scalino di pressione: ripiega quando l'uso della view raggiunge questa percentuale della finestra di contesto. |
128
+ | `trimEmergencyPercent` | `95` | Scalino di emergenza: fold meccanico forzato (nessuna chiamata al modello secondario) a questa percentuale. |
129
+ | `trimWindowTokens` | `0` | Limite di finestra effettivo per il calcolo delle soglie: calcolato su min(finestra del modello, limite). `0` = segue `wmForceTokens`. |
130
+ | `trimProtectedTail` | `6` | Gli ultimi N messaggi non vengono mai ripiegati. |
131
+ | `trimMaxFoldsPerHour` | `6` | Tetto di cadenza dei fold per ora mobile per il modello secondario (quelli di emergenza ne sono esenti). |
132
+ | `trimSideModel` | non impostata | Route del modello secondario che scrive i riassunti. Vuota = eredita il modello attuale della sessione. |
133
+ | `trimStateDir` | non impostata | Sostituisce la directory dello stato dei fold. Vuota = `<dshHome>/.dsh-switchman/state/trim/`. |
134
+ | `surfaceFold` | `true` | Spedisce i fold confermati sulla superficie della sessione (coppia di eventi: misurazione shadow-price + replace `surfaceOp`). OFF mantiene la vista a solo splice. |
135
+
136
+ **Percorsi di attivazione.** I controlli degli scalini girano al confine di inattività e, come ripiego per gli host dove gli eventi di stato di inattività non sono raggiungibili, dopo ogni completamento del flusso del modello (limitati a una valutazione ogni 60 s per sessione). Ogni decisione — fold e no-op allo stesso modo — viene aggiunta come una riga `key=value` con timestamp a `decisions.log` nella directory dello stato dei fold (vedi `trimStateDir`), dando ai fold una traccia di audit offline; il vocabolario delle righe (`view-applied`, righe di decisione/esito dei fold, `surface-replace`, `surface-replace skipped reason=…`) è catalogato in [docs/context-trim.md](./docs/context-trim.md).
137
+
138
+ **Verità del filo e stato stazionario.** Le righe di decisione portano `view=<n>` e, una volta arrivata una risposta, `wire=<n>` — il prompt reale del provider (input non in cache + lettura cache + scrittura cache, campionato dall'evento di utilizzo dello stream). La stima della view conta solo l'array di messaggi e legge sempre meno del filo, della misura dell'overhead fisso (misurato ≈ 17.3k: system prompt + schemi degli strumenti + persona), quindi gli scalini di pressione e di emergenza valutano su `max(view, wire)`, e solo `wire` è una lettura della dimensione del prompt. Su una finestra da 150k l'andamento a dente di sega sano porta il filo a ≈ 40k subito dopo un fold ↔ 88–90k al successivo (≈27%–60% della finestra), un fold ogni ~50k di crescita grezza, con il tetto fissato vicino a 90k dallo scalino di pressione anziché dalla finestra. Se `view` sale senza che compaia mai una riga `surface-replace`, leggi le righe di salto (`incomplete-seqs`, `no-service`, `compaction-lock`).
139
+
140
+ **Epoche di compattazione dell'host.** I riferimenti dei fold sono ancorati a id dei messaggi e hash dei contenuti; quando l'host compatta esso stesso la cronologia reinviata, l'intero registro invecchia in un colpo: nessun blocco si localizza, la vista retrocede silenziosamente alla cronologia grezza (fail-open — il registro delle decisioni annota `view-applied applied=0`) e il plugin re-ancora la sua baseline di crescita sulla vista post-compattazione alla richiesta successiva. La compattazione dell'host è quindi lo strato di taglio di ultima istanza; i fold del plugin fanno il lavoro fine all'interno di un'epoca dell'host. Alcuni dispatch consegnano array di richiesta o oggetti opzioni completamente congelati (l'attuale loop d'agente congela in profondità le richieste assemblate); per essi `injectView` riporta l'esito nel registro delle decisioni: `nothing-to-apply` (nessun blocco di fold localizzato — la cronologia grezza È la vista), `in-place`/`swapped` (la vista piegata è partita con la richiesta) oppure `none` (esisteva un fold ma il dispatch congelato non poteva portarlo — sul filo va la cronologia grezza). **La sostituzione di superficie è la via di spedizione quando disponibile** (`surfaceFold`, attivo per impostazione predefinita): al confine seriale `agent/turn-stopping` più una spazzata di inattività, il plugin aggiunge l'evento di misurazione shadow-price in stile host e UNA sostituzione `user/message` che porta `surfaceOp {op:'replace', startSeq, endSeq}` con `sourceEventSeqs` che copre ogni nodo di superficie in ombra — lo stesso meccanismo usato dalla compattazione dell'host stessa — cosicché un fold confermato raggiunge il filo dal livello sorgente, dispatch congelati compresi. Finché i dispatch recenti di una sessione registrano `none`, il plugin tratta la vista come sola misurazione: il livello dell'acqua misura invece il conteggio riportato dal provider (lo scalino force e la consegna automatica continuano a funzionare), i cicli di fold si mettono in pausa invece di bruciare chiamate al riassuntore, e il banner di taglio contrassegna la riga con `wire=raw` (folds not shipping; watermark reads the tally); gli host senza il meccanismo di superficie (o con il flag spento) mantengono esattamente quel comportamento legacy.
141
+
142
+ **Come i fold preservano gli hit di cache.** Ogni fold ripaga il prefisso una volta; l'obiettivo è il costo ammortizzato, non un tasso di hit perfetto. Il motore fa rispettare il contratto di cache: i fold sono solo in aggiunta — la frontier avanza soltanto e la zona congelata non viene più modificata; la view è ricostruita in modo deterministico da (cronologia, stato dei fold), quindi il prefisso serializzato resta identico al byte tra un fold e l'altro e la cache del fornitore continua a colpire; i confini di fold non separano mai una tool-call dell'assistant dal suo risultato; gli ultimi `trimProtectedTail` messaggi sono esenti; e tutto fallisce aperto — un errore interno degrada a pass-through, mai a una richiesta rotta.
143
+
144
+ **Arbitrato della compattazione.** Due trimmer automatici sulla stessa cronologia si riassumerebbero due volte e distruggerebbero a vicenda la frontier, quindi `cordis.patch.yml` mette `auto: false` sulle righe `dsh-compaction-basic` dell'host: context-trim diventa l'unico trimmer semantico. La `/compact` manuale continua a funzionare, e la consegna dello scalino force (che prende in prestito proprio quel percorso manuale) resta intatta. Il recupero da overflow passa al trim: il tentativo oltre la finestra fallisce una volta, poi lo scalino di emergenza ripiega la view così il tentativo successivo entra.
145
+
146
+ **Eredità dei fork.** Una sessione forkata (fork di subagente) eredita lo stato dei fold del genitore per copia-al-fork — ma solo se lo stato del genitore esiste e la cronologia del figlio risolve davvero la frontier del genitore; altrimenti il figlio parte pulito. Un'eredità per figlio, idempotente tra riavvii.
147
+
148
+ **Il banner delle soglie legge la view.** Il banner delle soglie riporta il conteggio di token della view in uscita (ciò che il modello vede davvero dopo il ripiegamento) invece del totale memorizzato, così una sessione molto ripiegata non inchioda più il livello al — anzi — allo scalino hard.
106
149
 
107
150
  ## Come funziona
108
151
 
@@ -113,9 +156,9 @@ Questi sono tutti i valori così come si presentano dopo una nuova installazione
113
156
 
114
157
  ## Manutenzione
115
158
 
116
- - Dopo un upgrade di DSH che cambia la lista di plugin dei preset di serie, risincronizza `cordis.patch.yml` dai nuovi `presets/*.patch.yml` (mantieni il doctrine suffix), poi reinstalla.
159
+ - Dopo un upgrade di DSH che cambia la lista di plugin dei preset di serie, risincronizza `cordis.patch.yml` dai nuovi `presets/*.patch.yml` (mantieni il doctrine suffix e l'arbitrato della compattazione — `auto: false` sulle righe `compaction-basic`), poi reinstalla.
117
160
  - Le righe di protocollo (`[SWITCHMAN:LANG|POOLS|WATERMARK|TEAMS]`) restano deliberatamente in inglese e stabili al byte — non localizzarle.
118
- - `npm pack --dry-run` deve restare alla forma auditata di 50 file / ~205 kB (gli screenshot in `docs/` non viaggiano nel pacchetto).
161
+ - `npm pack --dry-run` deve restare alla forma auditata di 60 file / ~380 kB (~1,26 MB scompattati; `docs/` è escluso).
119
162
 
120
163
  ## License
121
164