Bundle
dsh-goal-pause-guard
DSH plugin: asks for confirmation in a modal before pausing a running goal. Confirming pauses the goal, which also aborts the in-flight round so running subtasks stop immediately; cancelling does nothing.
- Source
- frederico-kluser
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 14 hours ago
Readme
<div align="center">
# dsh-goal-pause-guard
**Plugin para o [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): modal de confirmação antes de pausar uma atividade rodando.**
[](LICENSE)
[](package.json)
[](https://github.com/deepseek-ai/deepseek-harness)
[](../../actions/workflows/ci.yml)
[](https://github.com/topics/dsh-plugin)
*Pausar uma goal não é um gesto pequeno: ela interrompe a rodada atual e todas
as subtarefas em execução na hora. O plugin pede confirmação antes.*

</div>
---
## Porquê
No harness, **pausar uma goal armada dispara uma cascata** (motorizada no
`goal-round-driver`, `goal/changed` → `agent.cancel`, `keepInbox`):
1. a rodada em andamento é **abortada** — o agente para de executar no meio do
que estava fazendo;
2. as **subtarefas em execução** param junto (tools, comandos, subagentes);
3. a continuação automática fica **desarmada** — nada de novas rodadas até um
`resume` humano.
É exatamente esse efeito que o plugin coloca atrás de um portão: um clique
acidental no botão de pause não pode desligar uma atividade que está rodando.
## O que o plugin faz
```
clique em "Pause goal" (clique humano, trusted)
│
▼
┌──────────────┐ captura no document (capture phase, antes do React)
│ GUARD │ isPauseButton? ── não ──► o clique passa (fail-open)
└──────┬───────┘
▼ sim
MODAL DE CONFIRMAÇÃO (pt-BR / en / zh, tema claro-escuro do host)
├── Cancelar / Esc / fundo ──► nada acontece
└── Confirmar ──► re-clique UNTRUSTED no botão de pause ATUAL
│ (passa pelo guard sem re-gate)
▼
GoalBar → goals.pause (Remote RPC)
▼
host: goal paused + rodada abortada + subtasks param
```
- **Detecção por dois canais** — o botão é reconhecido pelo `aria-label`
localizado (`Pause goal` / `暂停目标`, os dois dicionários da barra de goal)
**ou** pelo path SVG do ícone de pause (`IconPauseOutline16`), sempre dentro
de `[data-goal-bar]` (o marcador que o próprio e2e oficial usa).
- **Fail-open por projeto** — se o botão não for identificável, o clique passa e
o pause se comporta exatamente como sem o plugin. Um portão de confirmação
nunca pode quebrar a ação que protege.
- **Sem loop, sem referência velha** — o confirm re-localiza o botão de pause
**atual** antes de re-clicar (um re-render entre abrir e confirmar nunca
transforma "confirmar" em "clicar outra coisa"); botão desabilitado ou
removido nesse meio-tempo = nada acontece.
- **Um modal por vez** — Esc, fundo e Cancelar fecham; o foco vai ao botão de
confirmar e volta ao botão de pause no fechar.
- **Determinismo** — sem timers, sem rede, sem storage. A metade host é um
`apply` vazio (mesmo padrão do `@deepseek-ai/dsh-client-ui-goal`): nenhum
estado do harness é tocado.
## Instalação
Como bundle do profile `web` — o `dsh.bundle.patch` do pacote ativa a camada
sozinho (insert próprio, sem colidir com nenhum outro id):
```bash
dsh plugin --profile web add /caminho/para/dsh-goal-pause-guard
dsh web # reinicie a instância; plugins entram no boot
```
Desinstalar: remova a linha do bundle do `~/.dsh/profiles/web/package.json`
(`dsh plugin --profile web remove dsh-goal-pause-guard`) e reinicie.
## Verificação
Toda a verificação roda **isolada** — nenhum teste toca uma instância real do
DSH (nem a porta 3080, nem o `$DSH_HOME`):
```bash
pnpm install
pnpm run typecheck # tsc estrito
pnpm run build # dist/ (host) + lib/client.js (browser, closure-factory)
pnpm test # 23 testes: detector + comportamento + e2e do artefato
```
| Camada | Prova |
| --- | --- |
| **Detector** (unit, jsdom) | os dois canais de identificação; rejeição de todo o resto (resume/edit/clear, botões fora da barra, ícones estrangeiros, prefixos parecidos) |
| **Comportamento** (jsdom) | clique humano abre o modal e nunca chega ao app; confirmar re-clica UMA vez como untrusted; cancelar/Esc/fundo não pausam; segundo clique engolido; botão removido/desabilitado no confirm não dispara; disposer remove tudo |
| **E2E do artefato** | o `lib/client.js` enviado é carregado como o harness carrega (`window.__ModuleLoader__.load({id, factory})`) e o fluxo inteiro é dirigido sobre ele |
O screenshot acima é real: capturado num boot isolado ( `$DSH_HOME`
temporária + porta própria) com Chromium headless, clicando no botão de pause
de verdade (evento trusted via CDP) — o guard abriu o modal e o cancelamento
não enviou pausa nenhuma.
## Estrutura
```
dsh-goal-pause-guard/
├── package.json # dsh.bundle.patch + dsh.client (platform web) + exports["./client"]
├── cordis.patch.yml # camada 1 (bundle): insert próprio, sem !!js, sem config
├── src/index.ts # host half: apply vazio (entrada do Loader)
├── client/
│ ├── guard-core.ts # TODA a lógica: detector, textos, modal, install (0 imports)
│ ├── index.ts # plugin body: inject [] + apply(ctx) via ctx.effect
│ ├── guard.css # modal com tokens --dsw-* (claro/escuro), classes gpg-
│ └── client.d.ts # declaração do subpath ./client
├── scripts/build-client.mjs # esbuild → lib/client.js (closure-factory, CSS embutido)
├── test/ # jsdom + node:test, offline por projeto
└── dist/, lib/ # produtos do build (host half / browser bundle)
```
## Compatibilidade
Verificado contra **DSH 0.1.5-alpha.2** (a linha viva instalada, bootada do
source). O detector ancora em dois fatos estáveis da superfície — `data-goal-bar`
e o `aria-label` do `action.pause` do dicionário `goal`. Se o harness mudar os
dois ao mesmo tempo, o guard fica fail-open (pause sem modal) — nunca quebrado.
## Licença
[MIT](LICENSE) © Frederico Kluser
Install
dsh plugin --profile web add github:frederico-kluser/dsh-goal-pause-guard
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-goal-pause-guard from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.