dsh-restart
IdentifiedRestart DeepSeek Harness from the sidebar or from an agent tool, with a watchdog, profile rollback and auto-continue
dsh-restart
Перезапуск DeepSeek Harness из интерфейса или инструментом агента — с возможностью продолжить ту же сессию после старта.
Что делает
| Возможность | Где |
|---|---|
| Кнопка «Перезапустить» | футер сайдбара, рядом с аккаунтом (иконка питания) |
| Подтверждение | модалка с двумя кнопками, без лишних опций |
| Инструмент агента | dsh_restart({ fake }) — только для разработки расширений |
| Продолжение сессии | рестарт инструментом открывает сессию и пишет [auto.continue] |
Как работает рестарт
Перезапускающий процесс не может быть потомком приложения: taskkill /T убивает всё
дерево вместе с ним. Поэтому плагин:
- пишет маркер
%LOCALAPPDATA%\dsh-restart\pending.json; - создаёт одноразовую задачу планировщика
DSH-Restart(Windows) с helper-скриптом; - helper ждёт 1.5 с, закрывает дерево процесса приложения, ждёт 1.8 с, запускает
.exeи удаляет задачу.
На macOS/Linux используется отдельный sh-процесс (kill + open/запуск).
Автопродолжение
После старта клиент читает маркер через локальный мост 127.0.0.1:49233:
GET /claim → { marker } — атомарно: маркер получает только один клиент
POST /restart → записать маркер и перезапустить
GET /health → признак живого хоста для сторожа
GET /settings → текущие переопределения
POST /settings → сохранить переопределения (без перезапуска)
Маркер забирает ровно одна загрузка страницы, поэтому двойная инициализация не даёт
двух сообщений. Рестарт инструментом возвращает сессию сообщением [auto.continue]:
клиент открывает ту же сессию, дожидается готовности композера, вставляет текст и
отправляет его кнопкой отправки (Enter — запасной вариант). Кнопка в футере сайдбара
маркер не создаёт и сессию не продолжает.
Черновик пользователя не теряется: если в поле уже есть текст, он временно
откладывается в localStorage, отправляется [auto.continue], после чего текст
возвращается в поле ввода (без отправки). Очередь сообщений — состояние сессии,
плагин её не читает и не меняет.
Инструмент
Инструмент нужен для разработки и отладки самого DSH: плагин, cordis.patch.yml, профиль.
Он не предназначен для обычной работы, «обновления состояния» или вызовов без изменения
кода/конфигурации, которое требует свежего процесса.
dsh_restart { fake: true }
→ Preflight passed; fake restart requested, nothing was restarted.
Без fake инструмент перезапускает приложение и возвращает сессию сообщением
[auto.continue]. Флаг fake прогоняет все проверки, но ничего не перезапускает —
для тестов. Если инструмент отключён, вызов из старой памяти падает с
unknown tool "dsh_restart".
Настройки плагина
На странице Plugins → Restart → Configure:
| Настройка | По умолчанию | Что делает |
|---|---|---|
Команда dsh_restart доступна агенту | включено | при выключении инструмент исчезает; вызов из старой памяти падает с unknown tool "dsh_restart" |
| Сторож активен | включено | при выключении рестарт просто закрывает и открывает приложение, без отката профиля |
| Ожидание сторожа, сек | 10 | сколько ждать признака живого старта перед откатом (1–120) |
Переопределения хранятся в %LOCALAPPDATA%\dsh-restart\settings.json; значения из
cordis.patch.yml остаются умолчаниями. Сохранение настроек не требует перезапуска:
инструмент регистрируется и снимается на лету.
Когда инструмент отказывает
dsh_restart доступен агенту только в «тихом» режиме — когда перезапуск никому не помешает:
| Проверка | Отказ |
|---|---|
| Вызов из подагента | dsh_restart is only available to the main agent: a subagent cannot restart the app; do not retry, report the need to the parent agent instead |
| Режим доступа не Full access | dsh_restart requires the Full access permission preset (current: …) |
| Другая сессия или подагент ещё работает | dsh_restart refused: N other session(s) or subagent(s) are still working (…) |
| Есть работающая фоновая задача | dsh_restart refused: N background job(s) are still running (…) |
Проверки идут через ctx.permissionPresets.current(agent.session), ctx.agents.list()
и ctx.jobs.list(agentId); статус «работает» — running либо непустая очередь входа агента.
Признак подагента — exec.agent.options.subagentDepth (плюс exec.parent).
Кнопка в интерфейсе эти проверки не выполняет: пользователь сам решает, когда перезапускать.
Ограничения
- пункт в трее и в нативном меню Application плагином добавить нельзя: трей и меню живут в процессе Electron main, а плагин — в дочернем host-процессе;
- перезапуск выполняется через Планировщик задач Windows; если политика запрещает создание задач, рестарт не сработает;
- воркспейс-сессии, которые в этот момент выполняют ход, будут прерваны (как и предупреждает модалка).
Кнопка и инструмент ведут себя по-разному
dsh_restart(агент) — всегда открывает сессию и пишет[auto.continue](или[auto.continue.failed], если сторож откатил профиль).- Кнопка «Restart DeepSeek Harness» в сайдбаре — просто перезапускает приложение: маркер не создаётся, сессия не продолжается, авто-сообщение не отправляется.
Куки и «Web authentication failed»
Каждый запуск Desktop Host занимает новый порт и ставит куку dsh-auth-<random>
для 127.0.0.1. Chromium хранит куки по хосту, а не по порту, поэтому старые куки
не заменяются и копятся. Замерено на живой установке: около 122 таких куки дают
заголовок Cookie примерно на 17.5 КБ, он перестаёт влезать в лимит (16 КБ),
приветственный запрос получает 431, и приложение падает с
desktop welcome: Web authentication failed. Дополнительно срок жизни этих куки
ограничен в профиле: connection.cookieMaxAgeDays: 7.
Перед каждым запуском (после убийства приложения, до Start-Process) helper
запускает src/cookie-clean.py и удаляет только строки dsh-auth-* для
127.0.0.1/localhost. Остальные куки не трогаются.
Если приложение всё же упало именно на приветствии (лог падения содержит
Web authentication failed), сторож не откатывает профиль и не отключает
плагины: он чистит куки, запускает приложение ещё раз и ждёт здорового старта.
Привязка сторожа к рестарту
Каждый рестарт получает токен:
dsh_restart → marker { token, sessionId } + watchdog.json { token }
сторож → перед откатом сверяет токен в watchdog.json
старт → хост удаляет watchdog.json (токен аннулирован)
Поэтому сторож срабатывает только для того рестарта, который его запустил, и не может тронуть приложение, поднятое позже или другим рестартом.
Перед откатом сторож сравнивает SHA-256 файлов package.json, cordis.patch.yml,
cordis.yml с сохранёнными в момент запроса. Если файлы изменились вручную, откат
не выполняется — считается, что правки взял на себя человек.
Файлы профиля пишутся без BOM (WriteAllText + UTF8Encoding(false)), иначе хост
не может прочитать package.json.
Известное ограничение: задача подагента после его завершения
Если подагент запустил фоновую задачу и завершил ход, DSH удаляет запись об этой задаче вместе с агентом, хотя сама задача продолжает работать. Проверка перед рестартом такую задачу не увидит и рестарт не отклонит.
Что покрыто:
| Владелец работы | Отказ |
|---|---|
| Своя фоновая задача | да |
| Подагент или чужая сессия ещё работают | да |
| Их задачи, пока владелец жив | да |
| Задача подагента после завершения его хода | нет |
Почему так: реестр задач ctx.jobs ключуется по id агента и очищается при его
уничтожении; обёртка jobs.start видит только задачи, запущенные через этот путь, а
подагент запускает их иначе. Детект по дочерним процессам ненадёжен: рядом постоянно
появляются и исчезают служебные процессы приложения. Решение отложено.
Установка
npx --yes github:machine202502/dsh-restart
Или вручную:
"dsh-restart": "github:machine202502/dsh-restart#main"
и добавить dsh-restart в dsh.profile.bundles.
После установки нужен перезапуск приложения (можно кнопкой, когда она появится).
Проверка
node tests/smoke.mjs # инструмент, проверки, fake, мост
node tests/load-client.mjs client.js # клиентский модуль грузится и отдаёт apply
Лицензия
MIT
Comments
Loading…