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.ru.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  dsh-switchman — плагин для [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH). После установки главная модель перестаёт «делать всё сама» и становится диспетчером: замеряет уровень воды, выбирает полосу, раздаёт задачи, принимает работу. Это не новая модель, а свод правил диспетчеризации, пристёгнутый к DSH, плюс страница настроек. Конкретно он делает восемь вещей:
16
16
 
17
- **1. Уровень воды в контексте: длинная сессия не переполняется.** Каждый ход подсчитывает токены сессии в реальном времени, и три линии уровня ужесточаются по нарастающей: 80k (soft, настраивается) подсказывает «пора делегировать»; 120k (hard) урезает бюджет разового чтения и подталкивает к завершению; 150k (force) запускает автоматическую передачу — форкает копию сессии в архив, сжимает контекст и будит продолжение, работа не обрывается. Фоновые субагенты, которые ещё бегут в момент передачи, тоже не теряются: id, описания задач и пути к отчётам записываются в документ передачи, и продолжившаяся сессия знает, куда идти за отчётами, а не раздаёт работу заново. У каждого отправленного субагента свой жёсткий потолок: доработав до него, он завершается резюме HANDOFF. Если автоматическое вмешательство работает не так, как вам нужно, возьмите управление в свои руки в любой момент: `/ctx-pause` останавливает все действия по уровню (замер продолжает работать, хвостовая строка уровня в реальном времени переключается на paused — вмешательство просто перестаёт действовать), `/ctx-resume` всё возвращает в любой момент — после перезапуска DSH вмешательство тоже вернётся само, вы временно отпускаете, а не выключаете навсегда; `/ctx-handover` не ждёт, пока уровень дойдёт до верха, и запускает ту же передачу по требованию — форкнуть копию, сжать контекст, разбудить продолжение (сессию сначала подводят к границе простоя, поэтому результат может занять несколько минут).
17
+ **1. Уровень воды в контексте: длинная сессия не переполняется.** Каждый ход подсчитывает токены сессии в реальном времени, и три линии уровня ужесточаются по нарастающей: 80k (soft, настраивается) подсказывает «пора делегировать»; 120k (hard) урезает бюджет разового чтения и подталкивает к завершению; 150k (force) запускает автоматическую передачу — записывает документ передачи (append-only лог сессии — сам по себе полная резервная копия), сжимает контекст и будит сессию, работа не обрывается. Фоновые субагенты, которые ещё бегут в момент передачи, тоже не теряются: id, описания задач и пути к отчётам записываются в документ передачи, и продолжившаяся сессия знает, куда идти за отчётами, а не раздаёт работу заново. У каждого отправленного субагента свой жёсткий потолок: доработав до него, он завершается резюме HANDOFF. Если автоматическое вмешательство работает не так, как вам нужно, возьмите управление в свои руки в любой момент: `/ctx-pause` останавливает все действия по уровню (замер продолжает работать, хвостовая строка уровня в реальном времени переключается на paused — вмешательство просто перестаёт действовать), `/ctx-resume` всё возвращает в любой момент — после перезапуска DSH вмешательство тоже вернётся само, вы временно отпускаете, а не выключаете навсегда; `/ctx-handover` не ждёт, пока уровень дойдёт до верха, и запускает ту же передачу по требованию — записать документ передачи, сжать контекст, разбудить сессию (сессию сначала подводят к границе простоя, поэтому результат может занять несколько минут).
18
18
 
19
19
  **2. Шесть пулов диспетчеризации: под каждую работу — своя модель.** Лёгкий пул (economy — мелкие массовые задачи), механический (mechanical — шаблонные переработки), основной (main — повседневный кодинг), пул высокой сложности (hard — тяжёлые рассуждения и крупные рефакторинги), мультимодальный (vision — чтение картинок), пул ревью (review — независимая проверка). На странице настроек отмечаете кандидатов, расставляете приоритеты (можно с привязкой к ступеням S/A/B/C) и для каждого маршрута отдельно фиксируете reasoning effort — выпадающий список ступеней берётся из уровней, которые модель реально поддерживает, а не из трёх универсальных. Каждый ход промпт главной модели несёт с собой таблицу рекомендаций `[SWITCHMAN:POOLS]`, и работа раздаётся по ней. У режима исполнения три состояния: off / advice / enforce (enforce = модель вне пула просто отклоняется).
20
20
 
@@ -23,7 +23,7 @@ dsh-switchman — плагин для [DeepSeek Harness](https://github.com/deep
23
23
  - **Режим команды агентов** — при включении внедряет командный регламент (делегирование по умолчанию + многоуровневая проверка + дисциплина общей доски задач) и автоматически включает Agent Teams в DSH: главная модель может нанимать постоянных товарищей по команде (`spawn_teammate`), ставить им задачи на общей доске (`team_task_*`) и обмениваться с ними сообщениями. Когда заводить команду, решает чёткая дисциплина: параллельные независимые подзадачи, объёмная самодостаточная работа, уже высокий уровень воды в главном контексте, потребность разделить роли; разовая точечная разведка по-прежнему идёт через subagent. Если выключить переключатель обратно, от командных пунктов не останется и следа, а инструменты команды из уже работающих сессий не изымаются.
24
24
  - **Синхронизация белого списка моделей субагентов** — в DSH есть белый список авторизации «разрешить агентам выбирать модели для субагентов»: если маршрут выбран в пуле, но не авторизован, явное назначение его главной моделью будет отклонено (в командном режиме он помечается ⚠). Включите этот переключатель — и объединение шести пулов целиком запишется в белый список: настраивать дважды не нужно, switchman остаётся единственным источником истины. Белый список применяется по снимку «новая сессия»: синхронизация действует только на сессии, открытые после неё.
25
25
 
26
- В шапке сессии — наглядная обратная связь: значок ⚡ «автономная команда», ◇ с фактической моделью текущей сессии; когда идёт автоматическая передача, появляется живая подсказка «идёт передача · резервная сессия / сжатие контекста / пробуждение продолжения».
26
+ В шапке сессии — наглядная обратная связь: значок ⚡ «автономная команда», ◇ с фактической моделью текущей сессии; когда идёт автоматическая передача, появляется живая подсказка «идёт передача · запись документа передачи / сжатие контекста / пробуждение продолжения».
27
27
 
28
28
  **4. Языковые предпочтения: спросит один раз — запомнит навсегда.** Три выпадающих списка: язык ответов, комментариев в коде и документов; область действия — глобальная или по проекту (`.switchman/lang.json`). Настраивать не обязательно: при первом использовании спросят один раз — на языке интерфейса вашего DSH — и запомнят; дальше каждая сессия соблюдает выбор автоматически.
29
29
 
@@ -75,7 +75,7 @@ dsh-switchman — плагин для [DeepSeek Harness](https://github.com/deep
75
75
  - **Пулы диспетчеризации** — шесть карточек в два ряда по три: сверху лёгкий / механический / основной; снизу высокосложный / мультимодальный / рецензентский. На каждой карточке отмечаются кандидаты, сгруппированные по поставщикам; отметьте «ручной порядок» — карточка станет нумерованным списком приоритетов, порядок меняется кнопками ↑ ↓ ×; рядом с каждым маршрутом можно зафиксировать reasoning effort (по умолчанию «следовать полосе»). Сводная строка сверху обновляется в реальном времени; на снимке: «пулов настроено: 4/6 · позиций в рейтинге: 2 · режим advice».
76
76
  - **Рейтинг по способностям + режим исполнения** — объединение моделей, выбранных в шести пулах, отсортировано по способности, сильнейшие впереди (на снимке glm-5.3 закреплён на ступени S, glm-5.3-flash — на A); порядок можно менять, лишние убирать; режим исполнения — advice / enforce (enforce = модель вне пула просто отклоняется).
77
77
  - **Команда агентов** — оба переключателя, командный режим и синхронизация белого списка, по умолчанию выключены; на снимке включены, под каждым — строка состояния синхронизации.
78
- - **Уровень воды в контексте** — три порога (на снимке 80000 / 120000 / 150000), бюджет разового чтения (3000), поведение жёсткого порога (пропустить с ограничением / заблокировать), переключатель автоматической передачи и отдельный потолок для субагентов — всё в этом разделе. Внизу строка команд: `/ctx-pause` приостановить вмешательство · `/ctx-resume` возобновить · `/ctx-handover` сделать резервную копию и передать управление прямо сейчас (команда подводит сессию к границе простоя и только потом сжимает контекст, результат может занять несколько минут).
78
+ - **Уровень воды в контексте** — три порога (на снимке 80000 / 120000 / 150000), бюджет разового чтения (3000), поведение жёсткого порога (пропустить с ограничением / заблокировать), переключатель автоматической передачи и отдельный потолок для субагентов — всё в этом разделе. Внизу строка команд: `/ctx-pause` приостановить вмешательство · `/ctx-resume` возобновить · `/ctx-handover` записать документ передачи и передать управление прямо сейчас (команда подводит сессию к границе простоя и только потом сжимает контекст, результат может занять несколько минут).
79
79
  - **Маршрутизация по намерению** — главный переключатель, маршрут маленькой модели-классификатора, тип классификатора (auto / decision / chat), независимый список кандидатов (у каждого строка описания способностей), окно кэша / порог уверенности / тайм-аут / бюджет истории и необязательная запасная модель. После включения в селекторе моделей появляется локализованный вход «Авто (маршрутизация по намерению)», а над полем ввода — чип с моделью маршрута и уверенностью.
80
80
 
81
81
  4. **Проверьте** — в шапке сессии появился значок ⚡ «автономная команда» (рядом ◇ показывает модель текущей сессии); либо спросите модель напрямую: «как называется последний раздел твоего системного промпта?» — в ответе должен прозвучать регламент dsh-switchman.
@@ -96,13 +96,56 @@ bash <install-dir>/skills/db-query/scripts/setup.sh
96
96
 
97
97
  **Пулы диспетчеризации**: у всех шести полос (economy/mechanical/main/hard/vision/review) кандидаты пусты (= системные модели по умолчанию, без вмешательства); ручной порядок и закреплённый effort выключены/пусты; `dispatchEnforce` = `advice` (только совет, без принуждения).
98
98
 
99
- **Уровень воды в контексте**: soft `80000` / hard `120000` / force `150000` токенов; бюджет разового чтения `3000`; поведение при превышении бюджета `cap` (обрезать с предупреждением); `wmAutoHandover` = включён (на force — автоматическая резервная копия + передача со сжатием); жёсткий потолок субагентов = включён (общие 150k).
99
+ **Уровень воды в контексте**: soft `80000` / hard `120000` / force `150000` токенов; бюджет разового чтения `3000`; поведение при превышении бюджета `cap` (обрезать с предупреждением); `wmAutoHandover` = включён (на force — запись документа передачи + сжатие автоматически); жёсткий потолок субагентов = включён (общие 150k).
100
100
 
101
101
  **Agent Teams**: `teamsMode` = выключен; `syncWhitelist` = выключен (два переключателя независимы друг от друга).
102
102
 
103
103
  **Маршрутизация по намерению** (по умолчанию недоступна; нужны классификатор + хотя бы один кандидат + включённый главный переключатель): `intentEnabled` = выключен (пока выключен, вход Auto скрыт, а сессии, где Auto уже выбран, идут по цепочке запасных моделей); маршрут классификатора не задан; тип классификатора `chat`; список кандидатов пуст (предустановленные категории участвуют в маршрутизации, только если у них указана модель); окно кэша `300` с / порог уверенности для переключения `80` / тайм-аут классификации `60000` мс; история `6` сообщений / `2400` символов; `intentContextWindow` = `200000`.
104
104
 
105
- **Что важно знать из коробки**: ① то, что маршрутизация по намерению недоступна из коробки, — намеренное решение; включается в три шага (классификатор, кандидаты, переключатель); ② эффективное окно Auto из коробки равно 200000 — защитная планка гарантирует 0.8×окна ≥ force (150000), поэтому линия сжатия ядра (160000) всегда стоит после линии плавной передачи (150000); ③ заметнее всего из коробки троица уровней: 80k напоминает делегировать / 120k ужимает чтение / 150k сам делает резервную копию и передаёт управление — без каких-либо настроек.
105
+ **Обрезка контекста**: `trimEnabled` = включено; `trimMode` = `auto` (полностью автоматически); ворота роста `22500` + прирост `50000` токенов; давление `60%` / авария `95%`; защищённый хвост `6` сообщений; потолок частоты fold-ов `6` в час; боковая модель не задана (наследует модель сессии) — подробности в разделе «Автоматическая обрезка контекста» ниже.
106
+
107
+ **Что важно знать из коробки**: ① то, что маршрутизация по намерению недоступна из коробки, — намеренное решение; включается в три шага (классификатор, кандидаты, переключатель); ② эффективное окно Auto из коробки равно 200000 — защитная планка гарантирует 0.8×окна ≥ force (150000), поэтому линия сжатия ядра (160000) всегда стоит после линии плавной передачи (150000); ③ заметнее всего из коробки троица уровней: 80k напоминает делегировать / 120k ужимает чтение / 150k сам передаёт управление (документ + сжатие) — без каких-либо настроек.
108
+
109
+ ## Автоматическая обрезка контекста
110
+
111
+ Длинные сессии остаются дешёвыми: прожитые диапазоны разговора сворачиваются (fold) в компактные детерминированные резюме — полностью автоматически и без разрушения кэша промптов провайдера. На границе простоя движок обрезки (`host/context-trim/`) выбирает диапазоны, стоящие fold, боковая модель пишет резюме (только при аварийном давлении срабатывает механический fold без вызова боковой модели), и fold фиксируется в состоянии fold-ов сессии. С этого момента каждый исходящий запрос пересобирается из этого состояния — блоки резюме заменяют свёрнутые диапазоны, живой хвост остаётся нетронутым. Хранимая сессия никогда не переписывается, и посреди стрима ничего не происходит.
112
+
113
+ Уровни автоматизации (`trimMode`):
114
+
115
+ - **`off`** — только измерения: сэмплирование расхода и попаданий кэша продолжается; без баннера и без fold-ов.
116
+ - **`advisory`** — модель сессии сворачивает сама: ей открываются инструменты `ctx_trim_fold` и `ctx_trim_status` плюс заголовок протокола.
117
+ - **`auto`** (по умолчанию) — всё вышеперечисленное плюс автоматические fold-ы на границе простоя, тремя ступенями: ворота роста (рост > `trimGrowthTokens`, отсчитываемый от низшей точки после последнего fold-а — этот якорь сохраняет ворота взведёнными после fold-ов, фиксирующихся на высоких точках view — И непрерывный сжимаемый прирост ≥ `trimIncrementTokens`), ступень давления (`trimPressurePercent` окна контекста) и аварийная ступень (`trimEmergencyPercent` — механический fold с собственным охлаждением).
118
+
119
+ Все ключи живут в разделе Настройки → dsh-switchman; значения по умолчанию — из `host/config.js`:
120
+
121
+ | Ключ | По умолчанию | Смысл |
122
+ | --- | --- | --- |
123
+ | `trimEnabled` | `true` | Главный выключатель. ВЫКЛ полностью отключает fold-ы и метрики (слушатель вырождается в чистый pass-through). |
124
+ | `trimMode` | `auto` | `off` только измерения / `advisory` fold-ы по решению модели / `auto` fold-ы по решению системы. |
125
+ | `trimGrowthTokens` | `22500` | Ворота роста: fold, когда исходящая view выросла на столько с прошлого fold-а. |
126
+ | `trimIncrementTokens` | `50000` | Спутник ворот: непрерывный сжимаемый прирост тоже должен достичь этого размера. |
127
+ | `trimPressurePercent` | `60` | Ступень давления: fold, когда использование view достигает этого процента окна контекста. |
128
+ | `trimEmergencyPercent` | `95` | Аварийная ступень: принудительный механический fold (без вызова боковой модели) на этом проценте. |
129
+ | `trimWindowTokens` | `0` | Эффективный лимит окна для расчёта уровней: считается от min(окно модели, лимит). `0` = следует за `wmForceTokens`. |
130
+ | `trimProtectedTail` | `6` | Последние N сообщений никогда не сворачиваются. |
131
+ | `trimMaxFoldsPerHour` | `6` | Потолок частоты fold-ов боковой модели за скользящий час (аварийные не в счёт). |
132
+ | `trimSideModel` | не задан | Маршрут боковой модели, пишущей резюме. Пусто = унаследовать текущую модель сессии. |
133
+ | `trimStateDir` | не задан | Переопределяет каталог состояния fold-ов. Пусто = `<dshHome>/.dsh-switchman/state/trim/`. |
134
+ | `surfaceFold` | `true` | Доставляет зафиксированные fold-ы на поверхность сессии (пара событий: учёт shadow-price + replace с `surfaceOp`). OFF оставляет view только со сплайсами. |
135
+
136
+ **Пути срабатывания.** Проверки ступеней выполняются на границе простоя и — как запасной путь для хостов, где события статуса простоя недостижимы — после завершения каждого потока модели (не чаще одной оценки в 60 с на сессию). Каждое решение — и fold-ы, и no-op-ы — дописывается одной строкой `key=value` (с отметкой времени) в `decisions.log` внутри каталога состояния fold-ов (см. `trimStateDir`), что даёт fold-ам офлайн-журнал аудита; словарь строк (`view-applied`, строки решений/исходов fold-ов, `surface-replace`, `surface-replace skipped reason=…`) перечислен в [docs/context-trim.md](./docs/context-trim.md).
137
+
138
+ **Правда провода и установившийся режим.** Строки решений несут `view=<n>` и, после того как ответ пришёл, `wire=<n>` — реальный промпт провайдера (некэшированный вход + чтение кэша + запись кэша, сэмплированные из usage-события потока). Оценка view считает только массив сообщений и всегда читается меньше провода на фиксированный оверхед (измерено ≈ 17.3k: системный промпт + схемы инструментов + persona), поэтому ступени давления и аварии оценивают по `max(view, wire)`, и только `wire` — это измерение размера промпта. На окне 150k здоровая пилообразная кривая ведёт провод от ≈ 40k сразу после fold ↔ 88–90k на следующем (≈27%–60% окна), один fold на ~50k сырого прироста, причём потолок удерживается около 90k ступенью давления, а не окном. Если `view` растёт, а строка `surface-replace` так и не появляется, читайте строки пропуска (`incomplete-seqs`, `no-service`, `compaction-lock`).
139
+
140
+ **Эпохи сжатия на стороне хоста.** Ссылки fold привязаны к идентификаторам сообщений и хешам содержимого, поэтому сжатие, выполненное самим хостом и заменяющее повторно отправляемую историю, старит весь журнал разом: ни один блок не находит своей позиции, view молча откатывается к исходной истории (fail-open — в журнал решений записывается `view-applied applied=0`), а плагин при следующем запросе перепривязывает базовую линию роста к виду после сжатия. Таким образом, сжатие хоста — запасной слой обрезки последней инстанции, а fold плагина выполняет тонкую работу внутри одной эпохи хоста. Некоторые диспетчеры передают полностью замороженные массивы запросов или объекты опций (текущий агентный луп глубоко замораживает собранные запросы); для них `injectView` фиксирует исход в журнале решений: `nothing-to-apply` (ни одного найденного fold-блока — сырая история и ЕСТЬ view), `in-place`/`swapped` (свёрнутое представление ушло с запросом) или `none` (fold существовал, но замороженный диспетчер не смог его нести — по проводу идёт сырая история). **Surface replace — путь доставки, когда он доступен** (`surfaceFold`, по умолчанию включён): на серийной границе `agent/turn-stopping` плюс холостая проходка плагин дописывает хост-стильное событие учёта shadow-price и ОДНО замещающее событие `user/message` с `surfaceOp {op:'replace', startSeq, endSeq}`, где `sourceEventSeqs` покрывает каждый затенённый узел поверхности, — тот же механизм, которым пользуется сжатие самого хоста, — так что зафиксированный fold доходит до провода с уровня источника, замороженные диспетчеры включительно. Пока недавние диспетчеры сессии пишут `none`, плагин считает view чисто измерительным: уровень воды вместо этого меряет присланную провайдером сумму (ступень force и автоматическая передача продолжают работать), циклы fold-ов приостанавливаются, не сжигая вызовы суммаризатора, а баннер обрезки помечает строку `wire=raw` (folds not shipping; watermark reads the tally); хосты без surface-механизма (или с выключенным флагом) сохраняют ровно это прежнее поведение.
141
+
142
+ **Как fold-ы берегут попадания кэша.** Каждый fold один раз переплачивает префикс; цель — амортизированная стоимость, а не стопроцентное попадание. Движок соблюдает контракт кэша: fold-ы только добавляются — frontier движется только вперёд, замороженная зона больше не редактируется; view пересобирается детерминированно из (история, состояние fold-ов), поэтому сериализованный префикс между fold-ами остаётся побайтово тем же и кэш провайдера продолжает попадать; границы fold-а никогда не разрывают пару tool-call ассистента и его результат; последние `trimProtectedTail` сообщений освобождены от свёртки; и всё отказывает открыто — внутренняя ошибка вырождается в pass-through, но не в сломанный запрос.
143
+
144
+ **Арбитраж компакции.** Два автоматических триммера поверх одной истории резюмировали бы её дважды и разрушали бы frontier друг друга, поэтому `cordis.patch.yml` ставит `auto: false` на строки `dsh-compaction-basic` хоста: context-trim становится единственным смысловым триммером. Ручной `/compact` продолжает работать, а передача уровня force (она одалживает именно этот ручной путь) остаётся нетронутой. Восстановление после переполнения переходит к обрезке: попытка сверх окна падает один раз, затем аварийная ступень сворачивает view, чтобы следующая попытка поместилась.
145
+
146
+ **Наследование при fork-е.** Форкнутая сессия (fork субагента) наследует состояние fold-ов родителя по принципу copy-on-fork — но только если состояние родителя существует и история ребёнка реально разрешает frontier родителя; иначе ребёнок стартует с чистого листа. Одно наследование на ребёнка, идемпотентно между перезапусками.
147
+
148
+ **Баннер уровней читает view.** Баннер уровней показывает число токенов исходящей view (то, что модель реально видит после свёртки) вместо накопленного счётчика, так что сильно свёрнутая сессия больше не пригвождает уровень к ступени hard.
106
149
 
107
150
  ## Как это работает
108
151
 
@@ -113,9 +156,9 @@ bash <install-dir>/skills/db-query/scripts/setup.sh
113
156
 
114
157
  ## Обслуживание
115
158
 
116
- - Если после обновления DSH изменился список плагинов заводских пресетов, пересинхронизируйте `cordis.patch.yml` из новых `presets/*.patch.yml` (сохранив doctrine suffix) и переустановите.
159
+ - Если после обновления DSH изменился список плагинов заводских пресетов, пересинхронизируйте `cordis.patch.yml` из новых `presets/*.patch.yml` (сохранив doctrine suffix и арбитраж компакции — `auto: false` в строках `compaction-basic`) и переустановите.
117
160
  - Строки протокола (`[SWITCHMAN:LANG|POOLS|WATERMARK|TEAMS]`) намеренно оставлены на английском и стабильны побайтово — не локализуйте их.
118
- - `npm pack --dry-run` должен сохранять аудированную форму: 50 файлов / ~205 kB (скриншоты из `docs/` в пакет не входят).
161
+ - `npm pack --dry-run` должен сохранять аудированную форму: 60 файлов / ~380 kB (~1,26 МБ распаковано; `docs/` исключён).
119
162
 
120
163
  ## License
121
164
 
package/README.zh-TW.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  dsh-switchman 是一個 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)外掛。裝上之後,主模型從「親自幹活」變成調度員:量水位、選車道、派任務、盯驗收。它不是新模型,是一套掛在 DSH 上的調度規程加一個設定頁,一共做八件事:
16
16
 
17
- **1. 上下文水位:長對話不撐爆。** 每輪即時統計對話 token,三條水位線逐級加碼:80k(soft,可調)提醒「該委派了」;120k(hard)收緊單次讀取預算、引導收尾;150k(force)自動交接——fork 一份對話留底、壓縮上下文、喚醒續跑,任務不斷線。交接那一刻還在跑的背景子代理也不會丟:id、任務、報告路徑都寫進交接文件,續跑的對話知道去哪收報告,不會重複派工。每個子代理自帶獨立硬上限,到頂寫完 HANDOFF 摘要退場。自動干預不合手時可臨時接管:`/ctx-pause` 停掉全部水位動作(量測照跑、尾部水位線轉成 paused,只是不動手),`/ctx-resume` 隨時恢復——重新啟動 DSH 後干預也會自動回來,是臨時放手,不是永久關閉;`/ctx-handover` 不等水位到頂,主動發起同一次交接:fork 備份、壓縮上下文、喚醒續跑(對話先被引導到閒置邊界,結果可能要等幾分鐘)。
17
+ **1. 上下文水位:長對話不撐爆。** 每輪即時統計對話 token,三條水位線逐級加碼:80k(soft,可調)提醒「該委派了」;120k(hard)收緊單次讀取預算、引導收尾;150k(force)自動交接——寫交接文件(附加式對話日誌本身就是完整備份)、壓縮上下文、喚醒續跑,任務不斷線。交接那一刻還在跑的背景子代理也不會丟:id、任務、報告路徑都寫進交接文件,續跑的對話知道去哪收報告,不會重複派工。每個子代理自帶獨立硬上限,到頂寫完 HANDOFF 摘要退場。自動干預不合手時可臨時接管:`/ctx-pause` 停掉全部水位動作(量測照跑、尾部水位線轉成 paused,只是不動手),`/ctx-resume` 隨時恢復——重新啟動 DSH 後干預也會自動回來,是臨時放手,不是永久關閉;`/ctx-handover` 不等水位到頂,主動發起同一次交接:寫交接文件、壓縮上下文、喚醒續跑(對話先被引導到閒置邊界,結果可能要等幾分鐘)。
18
18
 
19
19
  **2. 六個派發池:什麼活配什麼模型。** 輕量池(批次小活)、機械池(模板化改寫)、主力池(日常編碼)、高難池(高難推理與大規模重構)、多模態池(看圖)、複審池(獨立驗證)。設定頁裡勾候選、排優先順序、標 S/A/B/C 級,還能給每條路線單獨釘選思考強度——強度下拉來自該模型真實支援的級別,不是通用三級。主模型每輪提示詞裡都帶著一張 `[SWITCHMAN:POOLS]` 推薦表,照表派工。執行模式三態:關閉 / 建議 / 強制(強制 = 池外模型直接拒絕)。
20
20
 
@@ -23,7 +23,7 @@ dsh-switchman 是一個 [DeepSeek Harness](https://github.com/deepseek-ai/deepse
23
23
  - **代理團隊模式**——打開後注入團隊規程(預設委派 + 分級驗證 + 共享任務板紀律),並自動啟用 DSH 的 Agent Teams:主模型可以拉常駐隊友(`spawn_teammate`)、往共享任務板派工(`team_task_*`)、和隊友互傳訊息。什麼時候組團隊有明確紀律:可並行的獨立子任務、量大且自包含、主上下文水位已高、需要角色分離;一次性的單點調查仍走 subagent。再關掉開關,團隊條款零殘留,也不會撤走正在執行的對話裡的團隊工具。
24
24
  - **同步子代理模型白名單**——DSH 有張「允許 Agent 為子代理選擇模型」的授權白名單:池裡選了但沒授權的路線,點名派發會被拒(團隊模式下會標 ⚠)。打開這個開關,六個池的聯集整體寫入那張白名單——switchman 做唯一事實來源,不用兩邊重複設定。白名單按「新建對話」快照生效,同步只對之後新開的對話起作用。
25
25
 
26
- 對話標頭有直觀回饋:⚡「自主團隊」徽章、◇ 目前對話實際模型;自動交接進行時出現「交接進行中 · 備份對話 / 壓縮上下文 / 喚醒續接」的即時提示。
26
+ 對話標頭有直觀回饋:⚡「自主團隊」徽章、◇ 目前對話實際模型;自動交接進行時出現「交接進行中 · 寫入交接文件 / 壓縮上下文 / 喚醒續接」的即時提示。
27
27
 
28
28
  **4. 語言偏好:問一次,記一輩子。** 回覆、程式碼註解、文件三種語言各一個下拉,作用域可選全域或按專案(`.switchman/lang.json`)。不設也行——首次用到時用你 DSH 介面的語言問一次,記住後每個對話自動遵守。
29
29
 
@@ -75,7 +75,7 @@ dsh-switchman 是一個 [DeepSeek Harness](https://github.com/deepseek-ai/deepse
75
75
  - **派發池**——六張卡片兩行三列:上排輕量、機械、主力,下排高難、多模態、複審。卡片按供應商分組勾候選模型;勾上「手動排序」變成編號優先清單,用 ↑ ↓ × 調順序;每條路線可釘思考強度(預設「跟隨泳道」)。頂部彙總行即時更新,圖中即「已設定 4/6 池 · 排名 2 項 · 模式 建議」。
76
76
  - **能力排序 + 執行模式**——六池選中模型的聯集按能力排序、最強在前(圖中 glm-5.3 錨 S 級、glm-5.3-flash 錨 A 級),可調序、可刪減;執行模式建議 / 強制(強制 = 池外模型直接拒絕)。
77
77
  - **代理團隊**——團隊模式與白名單同步兩個開關,預設全關;圖中為開啟狀態,開關下方有同步狀態行。
78
- - **上下文水位**——三級閾值(圖中 80000 / 120000 / 150000)、單次讀取預算(3000)、hard 級行為(限流放行 / 攔截)、自動交接開關、子代理獨立上限,都在這區。底部一行指令:`/ctx-pause` 暫停干預 · `/ctx-resume` 恢復 · `/ctx-handover` 立即備份交接(它會把對話引導到閒置邊界再壓縮,結果可能要等幾分鐘)。
78
+ - **上下文水位**——三級閾值(圖中 80000 / 120000 / 150000)、單次讀取預算(3000)、hard 級行為(限流放行 / 攔截)、自動交接開關、子代理獨立上限,都在這區。底部一行指令:`/ctx-pause` 暫停干預 · `/ctx-resume` 恢復 · `/ctx-handover` 立即交接(它會把對話引導到閒置邊界再壓縮,結果可能要等幾分鐘)。
79
79
  - **意圖識別**——主開關、分類器小模型路由、分類器類型(auto / decision / chat)、獨立候選清單(每項附一句能力描述)、快取視窗 / 信心閾值 / 分類逾時 / 歷史訊息預算、可選備援模型。開啟後模型選擇器出現「自動(意圖識別)」入口,輸入框上方顯示目前路由模型與信心 chip。
80
80
 
81
81
  4. **驗證**——對話標頭出現 ⚡「自主團隊」徽章(旁邊 ◇ 顯示目前對話模型);或者直接問模型「你系統提示詞最後一段的標題是什麼」,答案應提到 dsh-switchman 規程。
@@ -96,13 +96,56 @@ bash <安裝目錄>/skills/db-query/scripts/setup.sh
96
96
 
97
97
  **派發池**:六條泳道(economy/mechanical/main/hard/vision/review)候選均為空(= 用系統預設模型,不干預);手動排序與 effort 釘選均為關/空;`dispatchEnforce` = `advice`(建議不強制)。
98
98
 
99
- **上下文水位**:soft `80000` / hard `120000` / force `150000` tokens;單次讀取預算 `3000`;超預算行為 `cap`(截斷加警告);`wmAutoHandover` = 開(force 級自動備份+壓縮交接);子代理硬上限 = 開(共享 150k)。
99
+ **上下文水位**:soft `80000` / hard `120000` / force `150000` tokens;單次讀取預算 `3000`;超預算行為 `cap`(截斷加警告);`wmAutoHandover` = 開(force 級自動寫交接文件+壓縮);子代理硬上限 = 開(共享 150k)。
100
100
 
101
101
  **Agent Teams**:`teamsMode` = 關;`syncWhitelist` = 關(兩者相互獨立)。
102
102
 
103
103
  **意圖識別**(預設不可用,需設定分類器 + 至少一個候選 + 打開主開關):`intentEnabled` = 關(關閉時 Auto 入口隱藏,已選 Auto 的對話走備援鏈);分類器路由未設定;分類器類型 `chat`;候選清單空(預置分類需填模型才參與路由);快取視窗 `300` 秒 / 切換信心閾值 `80` / 分類逾時 `60000` ms;歷史 `6` 則 / `2400` 字元;`intentContextWindow` = `200000`。
104
104
 
105
- **開箱要點**:① 意圖識別開箱不可用是有意設計,三步開啟(分類器、候選、開關);② Auto 有效視窗開箱即為 200000——護欄保證 0.8×視窗 ≥ force 級(150000),核心壓縮線(160000)永遠在平滑交接線(150000)之後;③ 開箱最有感的是水位三件套:80k 提醒委派 / 120k 收斂讀取 / 150k 自動備份交接,無須任何設定。
105
+ **上下文裁剪**:`trimEnabled` = 開;`trimMode` = `auto`(全自動);增長閘門 `22500` token + 增量 `50000` token;壓力 `60%` / 緊急 `95%`;保護尾部 `6` 則;折疊節奏上限每小時 `6` 次;side model 未設定(繼承對話模型)——詳見下文「自動上下文裁剪」一節。
106
+
107
+ **開箱要點**:① 意圖識別開箱不可用是有意設計,三步開啟(分類器、候選、開關);② Auto 有效視窗開箱即為 200000——護欄保證 0.8×視窗 ≥ force 級(150000),核心壓縮線(160000)永遠在平滑交接線(150000)之後;③ 開箱最有感的是水位三件套:80k 提醒委派 / 120k 收斂讀取 / 150k 自動交接(寫文件+壓縮),無須任何設定。
108
+
109
+ ## 自動上下文裁剪
110
+
111
+ 長對話也能保持便宜:已被消化掉的對話區間會被折疊成緊湊的確定性摘要——全自動完成,且不破壞服務商提示詞快取。在閒置邊界上,裁剪引擎(`host/context-trim/`)挑出值得折疊的區間,由 side model 寫出摘要(僅在緊急壓力下才改走無 side model 呼叫的機械折疊),並把 fold 提交進該對話的折疊狀態。此後每個出站請求都從該狀態重建——摘要塊頂替被折疊的區間,活動尾部原樣保留。儲存的對話從不被改寫,也絕不在串流過程中動手。
112
+
113
+ 自動化層級(`trimMode`):
114
+
115
+ - **`off`**——只測量:用量與快取命中取樣照常執行;無橫幅、無折疊。
116
+ - **`advisory`**——對話模型自己折疊:向其暴露 `ctx_trim_fold` 與 `ctx_trim_status` 工具和協定頭。
117
+ - **`auto`**(預設)——以上全部,外加閒置邊界上的自動折疊,分三級:增長閘門(自上次折疊後的低谷起算的增長 > `trimGrowthTokens`——該錨點保證高位折疊後閘門依然有效——且連續可壓縮增量 ≥ `trimIncrementTokens`)、壓力級(上下文視窗的 `trimPressurePercent`)、緊急級(`trimEmergencyPercent`——帶獨立冷卻的機械折疊)。
118
+
119
+ 所有鍵位於 設定 → dsh-switchman;預設值出自 `host/config.js`:
120
+
121
+ | 鍵 | 預設值 | 含義 |
122
+ | --- | --- | --- |
123
+ | `trimEnabled` | `true` | 總開關。關閉後折疊與度量全部停用(監聽器退化為純透傳)。 |
124
+ | `trimMode` | `auto` | `off` 只測量 / `advisory` 模型自折 / `auto` 系統自動折。 |
125
+ | `trimGrowthTokens` | `22500` | 增長閘門:出站 view 自上次 fold 以來增長到該值即觸發折疊。 |
126
+ | `trimIncrementTokens` | `50000` | 閘門搭檔:連續可壓縮增量也須達到該規模。 |
127
+ | `trimPressurePercent` | `60` | 壓力級:view 用量達到上下文視窗該百分比即折疊。 |
128
+ | `trimEmergencyPercent` | `95` | 緊急級:到該百分比強制機械折疊(無 side model 呼叫)。 |
129
+ | `trimWindowTokens` | `0` | 視窗上限:壓力與緊急檔按 min(模型視窗, 上限) 計算。`0` = 跟隨 `wmForceTokens`。 |
130
+ | `trimProtectedTail` | `6` | 最後 N 則訊息永不折疊。 |
131
+ | `trimMaxFoldsPerHour` | `6` | 每滾動小時 side model 折疊的節奏上限(緊急級豁免)。 |
132
+ | `trimSideModel` | 未設定 | 寫摘要的 side model 路由。留空 = 繼承對話目前模型。 |
133
+ | `trimStateDir` | 未設定 | 折疊狀態目錄覆蓋。留空 = `<dshHome>/.dsh-switchman/state/trim/`。 |
134
+ | `surfaceFold` | `true` | 把已提交的 fold 送上對話表面(shadow-price 計量事件 + `surfaceOp` replace 事件對)。關閉則維持僅拼接的 view。 |
135
+
136
+ **觸發路徑。** 級別檢查在閒置邊界上執行;在閒置狀態事件不可達的宿主上,兜底改為在每次模型串流完成後也檢查一次(每個對話節流為每 60 秒至多評估一次)。每個決策——fold 與 no-op 皆然——都會作為一行帶時間戳的 `key=value` 附加進裁剪狀態目錄(見 `trimStateDir`)下的 `decisions.log`,為 fold 留下離線稽核軌跡;列詞彙(`view-applied`、fold 決策/結果列、`surface-replace`、`surface-replace skipped reason=…`)收錄於 [docs/context-trim.md](./docs/context-trim.md)。
137
+
138
+ **線上真值與穩態。** 決策列攜帶 `view=<n>`,回應落地後還會帶上 `wire=<n>`——真實的廠商提示詞(未快取輸入 + 快取讀取 + 快取寫入,從串流的 usage 事件取樣得到)。view 估算只統計 messages 陣列,而且總是比線上讀數小一個固定額外開銷(實測 ≈ 17.3k:系統提示詞 + 工具 schema + persona),所以壓力級與緊急級按 `max(view, wire)` 評估,只有 `wire` 才是提示詞大小的讀數。150k 視窗下,健康的鋸齒波在每次 fold 剛結束時線上 ≈ 40k ↔ 下次 fold 時 88–90k(佔視窗的 ≈27%–60%),每 ~50k 原始增長折疊一次,上限由壓力級而非視窗釘在 90k 附近。若 `view` 持續攀升卻從不出現 `surface-replace` 列,就去讀跳過列(`incomplete-seqs`、`no-service`、`compaction-lock`)。
139
+
140
+ **宿主壓縮紀元。** fold 引用錨定在訊息 id 與內容雜湊上,因此宿主自身的壓縮一旦替換了重發歷史,整本帳就會同時老化:所有塊都無法定位,view 靜默回退為原始歷史(fail-open——決策日誌會記錄 `view-applied applied=0`),外掛在下一次請求時把增長基線重錨到壓縮後的視圖。宿主壓縮因此是兜底的裁剪層,外掛 fold 負責單一宿主紀元內的細粒度工作。 部分派發改用完全凍結的請求陣列或選項物件(目前的 agent loop 會深凍結裝配好的請求);對這類派發,`injectView` 會在決策日誌裡報告結果:`nothing-to-apply`(沒有定位到 fold 塊——原始歷史本身就是 view)、`in-place`/`swapped`(fold view 已隨請求送出)或 `none`(fold 存在但凍結的派發帶不動它——線上送出原始歷史)。**Surface replace 是可用時的發送路徑**(`surfaceFold`,預設開啟):在序列的 `agent/turn-stopping` 邊界外加一次閒置清掃,外掛追加宿主風格的 shadow-price 計量事件與一條攜帶 `surfaceOp {op:'replace', startSeq, endSeq}` 的 `user/message` 替換事件,`sourceEventSeqs` 覆蓋被遮蔽的每個表面節點——與宿主自身壓縮用的是同一機制——已提交的 fold 由此從源頭直達線上,凍結派發也不例外。當某對話近期派發都記錄 `none` 時,外掛把 view 當作僅供計量:水位改用服務商上報的計數(force 檔與自動交接照常運作),fold 循環暫停、不再空燒 summarizer 呼叫,裁剪橫幅以 `wire=raw`(folds not shipping; watermark reads the tally)標記該行;沒有 surface 機制的宿主(或開關關閉時)保持的正是這套舊行為。
141
+
142
+ **fold 如何保住快取命中。** 每次 fold 都要為前綴重付一次;目標是攤銷成本,不是滿分命中率。引擎守住快取契約:fold 只附加——frontier 只前進,凍結區永不再改;view 由(歷史, 折疊狀態)確定性重建,因此兩次 fold 之間序列化前綴位元組不變,服務商快取持續命中;fold 邊界絕不把 assistant 的 tool-call 與其 tool 結果拆開;最後 `trimProtectedTail` 則訊息豁免;一切皆 fail-open——內部出錯退化為透傳,絕不產生壞請求。
143
+
144
+ **壓縮仲裁。** 兩個自動裁剪器搶同一份歷史會互相重複摘要、互相摧毀 frontier,因此 `cordis.patch.yml` 給宿主的 `dsh-compaction-basic` 行設了 `auto: false`:context-trim 成為唯一語義裁剪器。手動 `/compact` 照常可用,force 級交接(借用的正是這條手動路徑)不受影響。溢出恢復改由裁剪承擔:超視窗的那次嘗試失敗一次,隨後緊急級折好 view,下一次嘗試就能裝下。
145
+
146
+ **fork 繼承。** 被 fork 的對話(子代理 fork)按 copy-on-fork 繼承父對話的折疊狀態——但僅當父狀態存在且子對話歷史確實能解析到父 frontier;否則子對話從乾淨狀態起步。每個子對話只繼承一次,跨重啟冪等。
147
+
148
+ **水位讀的是 view。** 水位橫幅報的是出站 view 的 token 數(折疊後模型真正看到的部分),不再是儲存側計數,重度折疊的對話因此不會再把水位釘死在 hard 級。
106
149
 
107
150
  ## 運作原理
108
151
 
@@ -113,9 +156,9 @@ bash <安裝目錄>/skills/db-query/scripts/setup.sh
113
156
 
114
157
  ## 維護須知
115
158
 
116
- - DSH 升級後若出廠預設外掛清單有變,從新的 `presets/*.patch.yml` 重新同步 `cordis.patch.yml`(保留 doctrine suffix),然後重裝。
159
+ - DSH 升級後若出廠預設外掛清單有變,從新的 `presets/*.patch.yml` 重新同步 `cordis.patch.yml`(保留 doctrine suffix 與壓縮仲裁——`compaction-basic` 行上的 `auto: false`),然後重裝。
117
160
  - 協定行(`[SWITCHMAN:LANG|POOLS|WATERMARK|TEAMS]`)刻意保持英文且位元組穩定——不要在地化。
118
- - `npm pack --dry-run` 應保持稽核過的 50 個檔案 / ~205 kB 形態(`docs/` 截圖不進包)。
161
+ - `npm pack --dry-run` 應保持稽核過的 60 個檔案 / ~380 kB 形態(解壓後 ~1.26 MB,`docs/` 不進包)。
119
162
 
120
163
  ## License
121
164
 
package/README.zh.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  dsh-switchman 是一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)插件。装上之后,主模型从「亲自干活」变成调度员:量水位、选车道、派任务、盯验收。它不是新模型,是一套挂在 DSH 上的调度规程加一个配置界面,一共做八件事:
16
16
 
17
- **1. 上下文水位:长会话不撑爆。** 每轮实时统计会话 token,三条水位线逐级加码:80k(soft,可调)提醒「该委派了」;120k(hard)收紧单次读取预算、引导收尾;150k(force)自动交接——fork 一份会话留底、压缩上下文、唤醒续跑,任务不断线。交接那一刻还在跑的后台子智能体也不会丢:id、任务、报告路径都写进交接文档,续跑的会话知道去哪收报告,不会重复派活。每个子智能体自带独立硬顶,到顶写完 HANDOFF 摘要退场。自动干预不合手时可临时接管:`/ctx-pause` 停掉全部水位动作(测量照跑、尾部水位行转成 paused,只是不动手),`/ctx-resume` 随时恢复——重启 DSH 后干预也会自动回来,是临时放手,不是永久关闭;`/ctx-handover` 不等水位到顶,主动发起同一次交接:fork 备份、压缩上下文、唤醒续跑(会话先被引导到空闲边界,结果可能要等几分钟)。
17
+ **1. 上下文水位:长会话不撑爆。** 每轮实时统计会话 token,三条水位线逐级加码:80k(soft,可调)提醒「该委派了」;120k(hard)收紧单次读取预算、引导收尾;150k(force)自动交接——写交接文档(追加式会话日志本身就是完整备份)、压缩上下文、唤醒续跑,任务不断线。交接那一刻还在跑的后台子智能体也不会丢:id、任务、报告路径都写进交接文档,续跑的会话知道去哪收报告,不会重复派活。每个子智能体自带独立硬顶,到顶写完 HANDOFF 摘要退场。自动干预不合手时可临时接管:`/ctx-pause` 停掉全部水位动作(测量照跑、尾部水位行转成 paused,只是不动手),`/ctx-resume` 随时恢复——重启 DSH 后干预也会自动回来,是临时放手,不是永久关闭;`/ctx-handover` 不等水位到顶,主动发起同一次交接:写交接文档、压缩上下文、唤醒续跑(会话先被引导到空闲边界,结果可能要等几分钟)。
18
18
 
19
19
  **2. 六个派发池:什么活配什么模型。** 轻量池(批量小活)、机械池(模板化改写)、主力池(日常编码)、高难池(高难推理与大规模重构)、多模态池(看图)、复审池(独立验证)。设置页里勾候选、排优先级、标 S/A/B/C 档,还能给每条路线单独钉思考强度——档位下拉来自该模型真实支持的级别,不是通用三档。主模型每轮提示词里都带着一张 `[SWITCHMAN:POOLS]` 推荐表,照表派活。执行模式三态:关闭 / 建议 / 强制(强制 = 池外模型直接拒绝)。
20
20
 
@@ -23,7 +23,7 @@ dsh-switchman 是一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepse
23
23
  - **智能体团队模式**——打开后注入团队规程(默认委派 + 分级验证 + 共享任务板纪律),并自动启用 DSH 的 Agent Teams:主模型可以拉常驻队友(`spawn_teammate`)、往共享任务板派活(`team_task_*`)、和队友互发消息。什么时候建团队有明确纪律:可并行的独立子任务、量大且自包含、主上下文水位已高、需要角色分离;一次性的单点调查仍走 subagent。再关掉开关,团队条款零残留,也不会撤走正在运行的会话里的团队工具。
24
24
  - **同步子智能体模型白名单**——DSH 有张「允许 Agent 为子智能体选择模型」的授权白名单:池里选了但没授权的路线,点名派发会被拒(团队模式下会标 ⚠)。打开这个开关,六个池的并集整体写入那张白名单——switchman 做唯一事实源,不用两边重复配置。白名单按「新建会话」快照生效,同步只对之后新开的会话起作用。
25
25
 
26
- 会话头部有直观反馈:⚡「自主团队」徽标、◇ 当前会话实际模型;自动交接进行时出现「交接进行中 · 备份会话 / 压缩上下文 / 唤醒续接」的实时提示。
26
+ 会话头部有直观反馈:⚡「自主团队」徽标、◇ 当前会话实际模型;自动交接进行时出现「交接进行中 · 写入交接文档 / 压缩上下文 / 唤醒续接」的实时提示。
27
27
 
28
28
  **4. 语言偏好:问一次,记一辈子。** 回复、代码注释、文档三种语言各一个下拉,作用域可选全局或按项目(`.switchman/lang.json`)。不设也行——首次用到时用你 DSH 界面的语言问一次,记住后每个会话自动遵守。
29
29
 
@@ -75,7 +75,7 @@ dsh-switchman 是一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepse
75
75
  - **派发池**——六张卡片两行三列:上排轻量、机械、主力,下排高难、多模态、复审。卡片按供应商分组勾候选模型;勾上「手动序」变成编号优先列表,用 ↑ ↓ × 调顺序;每条路线可钉思考强度(默认「跟随泳道」)。顶部汇总行实时更新,图中即「已配置 4/6 池 · 排名 2 项 · 模式 建议」。
76
76
  - **能力排序 + 执行模式**——六池选中模型的并集按能力排序、最强在前(图中 glm-5.3 锚 S 档、glm-5.3-flash 锚 A 档),可调序、可删减;执行模式建议 / 强制(强制 = 池外模型直接拒绝)。
77
77
  - **智能体团队**——团队模式与白名单同步两个开关,默认全关;图中为开启状态,开关下方有同步状态行。
78
- - **上下文水位**——三档阈值(图中 80000 / 120000 / 150000)、单次读取预算(3000)、硬档行为(限流放行 / 拦截)、自动交接开关、子智能体独立上限,都在这区。底部一行命令:`/ctx-pause` 暂停干预 · `/ctx-resume` 恢复 · `/ctx-handover` 立即备份交接(它会把会话引导到空闲边界再压缩,结果可能要等几分钟)。
78
+ - **上下文水位**——三档阈值(图中 80000 / 120000 / 150000)、单次读取预算(3000)、硬档行为(限流放行 / 拦截)、自动交接开关、子智能体独立上限,都在这区。底部一行命令:`/ctx-pause` 暂停干预 · `/ctx-resume` 恢复 · `/ctx-handover` 立即交接(它会把会话引导到空闲边界再压缩,结果可能要等几分钟)。
79
79
  - **意图识别**——主开关、分类器小模型路由、分类器类型(auto / decision / chat)、独立候选列表(每项附一句能力描述)、缓存窗口 / 置信度阈值 / 分类超时 / 历史消息预算、可选兜底模型。开启后模型选择器出现「自动(意图识别)」入口,输入框上方显示当前路由模型与置信度 chip。
80
80
 
81
81
  4. **验证**——会话头部出现 ⚡「自主团队」徽标(旁边 ◇ 显示当前会话模型);或者直接问模型「你系统提示词最后一段标题是什么」,答案应提到 dsh-switchman 规程。
@@ -96,13 +96,56 @@ bash <安装目录>/skills/db-query/scripts/setup.sh
96
96
 
97
97
  **派发池**:六条泳道(economy/mechanical/main/hard/vision/review)候选均为空(= 用系统默认模型,不干预);手动排序与 effort 钉选均为关/空;`dispatchEnforce` = `advice`(建议不强制)。
98
98
 
99
- **上下文水位**:soft `80000` / hard `120000` / force `150000` tokens;单次读取预算 `3000`;超预算行为 `cap`(截断加警告);`wmAutoHandover` = 开(force 档自动备份+压缩交接);子会话硬顶 = 开(共享 150k)。
99
+ **上下文水位**:soft `80000` / hard `120000` / force `150000` tokens;单次读取预算 `3000`;超预算行为 `cap`(截断加警告);`wmAutoHandover` = 开(force 档自动写交接文档+压缩);子会话硬顶 = 开(共享 150k)。
100
100
 
101
101
  **Agent Teams**:`teamsMode` = 关;`syncWhitelist` = 关(两者相互独立)。
102
102
 
103
103
  **意图识别**(默认不可用,需配置分类器 + 至少一个候选 + 打开主开关):`intentEnabled` = 关(关闭时 Auto 入口隐藏,已选 Auto 的会话走兜底链);分类器路由未设置;分类器类型 `chat`;候选列表空(预置分类需填模型才参与路由);缓存窗口 `300` 秒 / 切换置信度 `80` / 分类超时 `60000` ms;历史 `6` 条 / `2400` 字符;`intentContextWindow` = `200000`。
104
104
 
105
- **开箱要点**:① 意图识别开箱不可用是有意设计,三步开启(分类器、候选、开关);② Auto 有效窗口开箱即为 200000——护栏保证 0.8×窗口 ≥ force 档(150000),核心压缩线(160000)永远在平滑交接线(150000)之后;③ 开箱最有感的是水位三件套:80k 提醒委派 / 120k 收敛读取 / 150k 自动备份交接,无需任何设置。
105
+ **上下文裁剪**:`trimEnabled` = 开;`trimMode` = `auto`(全自动);增长闸门 `22500` token + 增量 `50000` token;压力 `60%` / 紧急 `95%`;保护尾部 `6` 条;折叠节奏上限每小时 `6` 次;side model 未设置(继承会话模型)——详见下文「自动上下文裁剪」一节。
106
+
107
+ **开箱要点**:① 意图识别开箱不可用是有意设计,三步开启(分类器、候选、开关);② Auto 有效窗口开箱即为 200000——护栏保证 0.8×窗口 ≥ force 档(150000),核心压缩线(160000)永远在平滑交接线(150000)之后;③ 开箱最有感的是水位三件套:80k 提醒委派 / 120k 收敛读取 / 150k 自动交接(写文档+压缩),无需任何设置。
108
+
109
+ ## 自动上下文裁剪
110
+
111
+ 长会话也能保持便宜:已被消化掉的对话区间会被折叠成紧凑的确定性摘要——全自动完成,且不破坏服务商提示词缓存。在空闲边界上,裁剪引擎(`host/context-trim/`)挑出值得折叠的区间,由 side model 写出摘要(仅在紧急压力下才改走无 side model 调用的机械折叠),并把 fold 提交进该会话的折叠状态。此后每个出站请求都从该状态重建——摘要块顶替被折叠的区间,活动尾部原样保留。存储的会话从不被改写,也绝不在流式过程中动手。
112
+
113
+ 自动化层级(`trimMode`):
114
+
115
+ - **`off`**——只测量:用量与缓存命中采样照常运行;无横幅、无折叠。
116
+ - **`advisory`**——会话模型自己折叠:向其暴露 `ctx_trim_fold` 与 `ctx_trim_status` 工具和协议头。
117
+ - **`auto`**(默认)——以上全部,外加空闲边界上的自动折叠,分三档:增长闸门(自上次折叠后的低谷起算的增长 > `trimGrowthTokens`——该锚点保证高位折叠后闸门依然有效——且连续可压缩增量 ≥ `trimIncrementTokens`)、压力档(上下文窗口的 `trimPressurePercent`)、紧急档(`trimEmergencyPercent`——带独立冷却的机械折叠)。
118
+
119
+ 所有键位于 设置 → dsh-switchman;默认值出自 `host/config.js`:
120
+
121
+ | 键 | 默认值 | 含义 |
122
+ | --- | --- | --- |
123
+ | `trimEnabled` | `true` | 总开关。关闭后折叠与度量全部停用(监听器退化为纯透传)。 |
124
+ | `trimMode` | `auto` | `off` 只测量 / `advisory` 模型自折 / `auto` 系统自动折。 |
125
+ | `trimGrowthTokens` | `22500` | 增长闸门:出站 view 自上次 fold 以来增长到该值即触发折叠。 |
126
+ | `trimIncrementTokens` | `50000` | 闸门搭档:连续可压缩增量也须达到该规模。 |
127
+ | `trimPressurePercent` | `60` | 压力档:view 用量达到上下文窗口该百分比即折叠。 |
128
+ | `trimEmergencyPercent` | `95` | 紧急档:到该百分比强制机械折叠(无 side model 调用)。 |
129
+ | `trimWindowTokens` | `0` | 窗口上限:压力与紧急档按 min(模型窗口, 上限) 计算。`0` = 跟随 `wmForceTokens`。 |
130
+ | `trimProtectedTail` | `6` | 最后 N 条消息永不折叠。 |
131
+ | `trimMaxFoldsPerHour` | `6` | 每滚动小时 side model 折叠的节奏上限(紧急档豁免)。 |
132
+ | `trimSideModel` | 未设置 | 写摘要的 side model 路由。留空 = 继承会话当前模型。 |
133
+ | `trimStateDir` | 未设置 | 折叠状态目录覆盖。留空 = `<dshHome>/.dsh-switchman/state/trim/`。 |
134
+ | `surfaceFold` | `true` | 把已提交的 fold 送上会话表面(shadow-price 计量事件 + `surfaceOp` replace 事件对)。关闭则保持仅拼接的 view。 |
135
+
136
+ **触发路径。** 档位检查在空闲边界上运行;在空闲状态事件不可达的宿主上,兜底改为在每次模型流式完成后也检查一次(每个会话节流为每 60 秒至多评估一次)。每个决策——fold 与 no-op 皆然——都以「ISO 时间戳 + `key=value` 字段」的形式追加一行到裁剪状态目录(见 `trimStateDir`)下的 `decisions.log`,为 fold 留下离线审计轨迹;行词汇(`view-applied`、fold 决策/结果行、`surface-replace`、`surface-replace skipped reason=…`)收录于 [docs/context-trim.md](./docs/context-trim.md)。
137
+
138
+ **线上真值与稳态。** 决策行携带 `view=<n>`,响应落地后还会带上 `wire=<n>`——真实的供应商提示词(未缓存输入 + 缓存读取 + 缓存写入,从流的 usage 事件采样得到)。view 估算只统计 messages 数组,并且总是比线上读数小一个固定开销(实测 ≈ 17.3k:系统提示词 + 工具 schema + persona),所以压力档与紧急档按 `max(view, wire)` 评估,只有 `wire` 才是提示词大小的读数。150k 窗口下,健康的锯齿波在每次 fold 刚结束时线上 ≈ 40k ↔ 下次 fold 时 88–90k(占窗口的 ≈27%–60%),每 ~50k 原始增长折叠一次,上限由压力档而非窗口钉在 90k 附近。若 `view` 持续攀升却从不出现 `surface-replace` 行,就去读跳过行(`incomplete-seqs`、`no-service`、`compaction-lock`)。
139
+
140
+ **宿主压缩纪元。** fold 引用锚定在消息 id 与内容哈希上,因此宿主自身的压缩一旦替换了重发历史,整本账就会同时老化:所有块都无法定位,view 静默回退为原始历史(fail-open——决策日志会记录 `view-applied applied=0`),插件在下一次请求时把增长基线重锚到压缩后的视图。宿主压缩因此是兜底的裁剪层,插件 fold 负责单个宿主纪元内的细粒度工作。 部分派发改用完全冻结的请求数组或选项对象(当前的 agent loop 会深冻结装配好的请求);对这类派发,`injectView` 会在决策日志里报告结果:`nothing-to-apply`(没有定位到 fold 块——原始历史本身就是 view)、`in-place`/`swapped`(fold view 已随请求发出)或 `none`(fold 存在但冻结的派发带不动它——线上发送原始历史)。**Surface replace 是可用时的发送路径**(`surfaceFold`,默认开启):在串行的 `agent/turn-stopping` 边界外加一次空闲清扫,插件追加宿主风格的 shadow-price 计量事件和一条携带 `surfaceOp {op:'replace', startSeq, endSeq}` 的 `user/message` 替换事件,`sourceEventSeqs` 覆盖被遮蔽的每个表面节点——与宿主自身压缩用的是同一机制——已提交的 fold 由此从源头直达线上,冻结派发也不例外。当某会话近期派发都记录 `none` 时,view 仅用于计量:水位改用服务商上报的计数(force 档与自动交接照常工作)。折叠只在 surface 通道同样不可用时才暂停——由持久的可用性探测判定(会话在线且暴露 `append`/`surface.nodes`、没有打开的 compaction 锁),与「最近是否发过货」无关,因此重启不再把引擎冻住。只有当该会话从未成功发出过任何 surface 替换时,裁剪横幅才以 `wire=raw`(folds not shipping; watermark reads the tally)标记该行;没有 surface 机制的宿主(或开关关闭时)保持的正是这套旧行为。
141
+
142
+ **fold 如何保住缓存命中。** 每次 fold 都要为前缀重付一次;目标是摊销成本,不是满分命中率。引擎守住缓存契约:fold 只追加——frontier 只前进,冻结区永不再改;view 由(历史, 折叠状态)确定性重建,因此两次 fold 之间序列化前缀字节不变,服务商缓存持续命中;fold 边界绝不把 assistant 的 tool-call 与其 tool 结果拆开;最后 `trimProtectedTail` 条消息豁免;一切皆 fail-open——内部出错退化为透传,绝不产生坏请求。
143
+
144
+ **压缩仲裁。** 两个自动裁剪器抢同一份历史会互相重复摘要、互相摧毁 frontier,因此 `cordis.patch.yml` 给宿主的 `dsh-compaction-basic` 行设了 `auto: false`:context-trim 成为唯一语义裁剪器。手动 `/compact` 照常可用,force 档交接(借用的正是这条手动路径)不受影响。溢出恢复改由裁剪承担:超窗的那次尝试失败一次,随后紧急档折好 view,下一次尝试就能装下。
145
+
146
+ **fork 继承。** 被 fork 的会话(子代理 fork)按 copy-on-fork 继承父会话的折叠状态——但仅当父状态存在且子会话历史确实能解析到父 frontier;否则子会话从干净状态起步。每个子会话只继承一次,跨重启幂等。
147
+
148
+ **水位读的是 view。** 水位横幅报的是出站 view 的 token 数(折叠后模型真正看到的部分),不再是存储侧计数,重度折叠的会话因此不会再把水位钉死在 hard 档。
106
149
 
107
150
  ## 工作原理
108
151
 
@@ -113,9 +156,9 @@ bash <安装目录>/skills/db-query/scripts/setup.sh
113
156
 
114
157
  ## 维护注意
115
158
 
116
- - DSH 升级后若出厂预设插件列表有变,从新的 `presets/*.patch.yml` 重新同步 `cordis.patch.yml`(保留 doctrine suffix),然后重装。
159
+ - DSH 升级后若出厂预设插件列表有变,从新的 `presets/*.patch.yml` 重新同步 `cordis.patch.yml`(保留 doctrine suffix 与压缩仲裁——`compaction-basic` 行上的 `auto: false`),然后重装。
117
160
  - 协议行(`[SWITCHMAN:LANG|POOLS|WATERMARK|TEAMS]`)刻意保持英文且字节稳定——不要本地化。
118
- - `npm pack --dry-run` 应保持审计过的 50 文件 / ~205 kB 形态(`docs/` 截图不进包)。
161
+ - `npm pack --dry-run` 应保持审计过的 60 文件 / ~380 kB 形态(解压后 ~1.26 MB,`docs/` 不进包)。
119
162
  - **零运行时依赖——硬性不变量。** 宿主侧包(`@deepseek-ai/schemastery`、cordis 等)只允许声明为 `peerDependencies`,由运行中的 DSH 用它自己的副本满足。普通 `dependencies` 会被物理安装进 profile 工作区(`<profile>/node_modules/…`),从而对同处 profile 的其他插件遮蔽宿主副本——1.7.0 把 schemastery 声明为普通依赖时,正是这个机制让 dshmarket 插件市场崩溃(一个进程里出现两份 schemastery,市场产生的 schema 与宿主不再同类)。`scripts/validate.mjs` 会在 `dependencies` 再次出现时直接失败;因此安装、升级、卸载 dsh-switchman 都不会再影响 dshmarket 或任何其他社区包在运行时解析到的模块。
120
163
 
121
164
  ## License