# ⏪ dsh-checkpoint-rewind - **Canal 1024 store**: `npm i -g dsh1024` una vez, luego `dsh1024 plugin --profile web add dsh-checkpoint-rewind` (cuenta para el ranking de instalaciones de [deepseek1024.com](https://deepseek1024.com)). **Checkpoints unificados de DeepSeek Harness: instantáneas de tres estados — sesión + workspace + configuración — con reversión de un solo paso.** *El equivalente a los Checkpoints de Claude Code, construido como plugin de costura de capacidad (capability-seam): captura antes de cada mutación y restaura cualquiera de los tres estados con un único comando aprobado.* [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) [![Gitee](https://img.shields.io/badge/Gitee-mirror-c71d23?logo=gitee)](https://gitee.com/perrylink/dsh-checkpoint-rewind) [![DSH plugin](https://img.shields.io/badge/dsh--plugin-✅-green)](https://github.com/topics/dsh-plugin) [![dsh-doctor](https://raw.githubusercontent.com/PerryLink/dsh-plugin-doctor/main/badges/PerryLink__dsh-checkpoint-rewind.svg)](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章) [![DSH Market](https://raw.githubusercontent.com/2BingLing/dsh-market/master/assets/readme/badge-listed-en.svg)](https://dsh.market/) [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen.svg)](#) [![CI](https://img.shields.io/github/actions/workflow/status/PerryLink/dsh-checkpoint-rewind/ci.yml?branch=main&label=CI)](https://github.com/PerryLink/dsh-checkpoint-rewind/actions) [![Version](https://img.shields.io/github/v/tag/PerryLink/dsh-checkpoint-rewind?label=version)](https://github.com/PerryLink/dsh-checkpoint-rewind/releases) [![npm version](https://img.shields.io/npm/v/dsh-checkpoint-rewind)](https://www.npmjs.com/package/dsh-checkpoint-rewind) [![npm downloads](https://img.shields.io/npm/dm/dsh-checkpoint-rewind)](https://www.npmjs.com/package/dsh-checkpoint-rewind) [![dshfind](https://dshfind.com/api/badge/PerryLink/dsh-checkpoint-rewind?metric=downloads&lang=es)](https://dshfind.com/es/plugins/PerryLink/dsh-checkpoint-rewind?ref=badge) [English](README.md) · [简体中文](README-zh.md) · [Español](README-es.md) · [Português](README-pt.md) · [हिन्दी](README-hi.md)
--- ## ⭐ 如果它帮到了你 Este plugin forma parte de la [familia de plugins DSH](https://github.com/PerryLink) (más de 40, todos Apache-2.0). Si te resulta útil, **dale una estrella**: no desbloquea nada, pero ayuda a que la siguiente persona lo encuentre antes. *English:* part of a 40+ plugin family for DeepSeek Harness. If it is useful, **a star helps the next person find it** — nothing is gated behind it. ## Compatibilidad | Superficie | Estado | |---|---| | Harness | DeepSeek Harness `dsh-v0.1.7-rc.2` (tag de GitHub, verificado el 2026-09-25; pin npm `0.1.7-rc.2`, peers `>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0`) (adaptado el 2026-09-22): el host eliminó `@deepseek-ai/dsh-settings-file` y sustituyó la superficie de registro de namespaces de `ctx.settings` por `SettingsForms`, que proyecta el `Config` de cada entrada del Loader en el formulario de Ajustes y persiste las ediciones en el patch del perfil — los campos de configuración del plugin ahora se declaran `volatile()` y se leen en vivo, los códecs estrictos de typert solo llevan su fábrica `create()`, y el aviso de rebobinado declara su propio `kind` de origen de mensaje. La ruta heredada `settings.register` se mantiene para los hosts `0.1.5-rc.2`/`0.1.6-alpha.2`. Verificado el 2026-09-22 contra el checkout de `dsh-v0.1.7-alpha.1` (typecheck contra la superficie de tipos de alpha.1 + suite de unidades completa + integración headless ensamblada). | | Node | `^22.19.0 \|\| >=24.0.0` | | Plataformas | Todas (comandos + listeners de host; línea de tiempo de Ajustes opcional mediante la capacidad settings) | | Modelo | Cualquiera (sin llamadas al modelo — las instantáneas y restauraciones son deterministas) | ## Qué obtienes `dsh-checkpoint-rewind` captura un **checkpoint unificado de tres estados** — workspace, cursor de sesión y configuración del plugin — y restaura uno o los tres con un único comando aprobado: 1. **Registro de tres estados** — cada checkpoint guarda el estado del workspace (SHA del árbol git, o un manifiesto de copia), el cursor de eventos de la sesión (`seq` + límite de turno) y una instantánea de configuración, etiquetado por origen (`manual` / `auto` / `guard` / `mutation`). 2. **Cuatro disparadores de captura** — antes de cada herramienta de mutación (`fs/write-intent`, `fs/edit-intent`, `tools/pre-execute`), en el intervalo automático (`autoCheckpoint`, por defecto cada paso), manualmente (`/checkpoint` y la herramienta `checkpoint`), y como guardia antes de cada reversión. 3. **Proveedor git primero** — `git stash create` / `commit-tree` producen objetos de instantánea no referenciados que nunca tocan tu worktree, índice o historial; la restauración es solo-worktree y por rutas explícitas. Los directorios no git (y los repos con HEAD no nacido) degradan a un proveedor `copy` incremental con reutilización de hardlinks. 4. **Reversión de un solo paso** — `/rewind workspace|session|config|all ` restaura los estados seleccionados; `preview` es un informe de impacto de solo lectura, `diff ` compara dos checkpoints, `clear` los elimina (esta sesión; `clear --all` abarca todas las sesiones y workspaces). 5. **Reversión de sesión por fork** — la reversión de sesión reproduce eventos hasta el límite del checkpoint mediante la primitiva oficial `SessionStore.fork`, creando una nueva sesión hija (con respaldo a la ruta `sessions.create` con semilla cuando el host no tiene fork o el checkpoint no tiene límite); la sesión original conserva su historial completo. 6. **Línea de tiempo en Ajustes** — la pestaña `Plugins → Checkpoints` muestra los checkpoints de la sesión con diffs línea a línea entre pares. ## ¿Por qué otro plugin de rewind? | Plugin | Qué vende | ¿Restaura archivos? | ¿Rebobina la sesión? | |---|---|---|---| | **dsh-checkpoint-rewind** (este) | instantáneas de objetos git + reversión de tres estados + restauración de un solo paso | ✅ estado completo del workspace | ✅ sesión hija por reproducción de semilla | | [Anionex/dsh-turn-rewind](https://github.com/Anionex/dsh-turn-rewind) | Change Ledger persistente de deltas por mutación | ✅ reproduciendo deltas inversos | ✅ su propio modelo de ledger | | [LingLambda/dsh-undo](https://github.com/LingLambda/dsh-undo) | reversión pura de contexto al último paso completado | ❌ | ✅ solo contexto | | [Mongfayi/dsh-recall](https://github.com/Mongfayi/dsh-recall) | recall de mensajes (elimina un turno y todo lo posterior) | ❌ (explícitamente) | ✅ eliminación de turno | La diferencia en una frase: **dsh-checkpoint-rewind captura el *estado del workspace* con primitivas git sin efectos secundarios antes de cada mutación, y convierte “volver al paso N” en un único comando aprobado — primero el checkpoint de guardia, segundo los archivos restaurados, tercero la configuración restaurada, cuarto la sesión reproducida, cada fase registrada.** Sin contabilidad de deltas que pueda derivar, sin edición a nivel de mensaje (eso pertenece a otro plugin), sin sincronización entre dispositivos. ## Inicio rápido ```sh # 1. instala el bundle en tu perfil dsh plugin --profile web add "github:PerryLink/dsh-checkpoint-rewind#main" # o desde npm (versiones publicadas) dsh plugin --profile web add dsh-checkpoint-rewind # 2. reinicia y verifica la fila dsh --profile web --dump-config | grep -A4 'id: checkpoint-rewind' ``` Los checkpoints persisten a través del servicio `storageDomain`. El plugin se monta sin él y nunca bloquea el arranque del perfil — los comandos checkpoint/rewind devuelven entonces un error estructurado indicando las filas exactas que hay que añadir. Compón la pila de almacenamiento una vez para habilitarlos: ```yaml - insert: - id: checkpoint-rewind-storage name: '@deepseek-ai/dsh-storage' - id: checkpoint-rewind-storage-json name: '@deepseek-ai/dsh-storage-json' config: root: !!js dshHomePath('checkpoint-rewind/storage') - id: checkpoint-rewind-storage-domain name: '@deepseek-ai/dsh-storage-domain' config: backend: json ``` El paquete es ESM puro sin paso de build — `index.mjs` y `lib/` son los artefactos enviados. Las mutaciones del workspace ahora crean checkpoints automáticamente; ejecuta `/rewind` para listarlos: ```text rewind: 3 checkpoints (newest last): #a1b2c3d4 · (git) · turn 2 step 1 · 2026-08-14 12:00:01 (3 min ago) · trigger: bash · 4 files · 1.2 MiB #b2c3d4e5 · (git) · turn 2 step 3 · 2026-08-14 12:00:41 · trigger: str_replace_editor · 2 files · 310 KiB #c3d4e5f6 · (copy) · turn 3 step 1 · 2026-08-14 12:01:10 · trigger: write · 1 file · 90 KiB run "/rewind " to restore files and fork the session from that checkpoint ``` Dirígete a un checkpoint por su prefijo de id único, por número de paso o por `latest`: ```text /rewind b2c3d4e5 /rewind step 2 /rewind latest /rewind preview b2c3d4e5 # solo lectura: muestra qué archivos cambiarían, no toca nada /rewind clear # eliminación confirmada de los checkpoints de esta sesión (archivos intactos) /rewind clear --all # eliminación confirmada en TODAS las sesiones y workspaces (archivos intactos) ``` `preview` se resuelve con el mismo direccionamiento e imprime el impacto sin pedir confirmación ni escribir nada. ## Instalación y desinstalación - **Canal git** (último `main`): `dsh plugin --profile web add "github:PerryLink/dsh-checkpoint-rewind#main"` — ESM puro, sin paso de `prepare` ni `allowBuilds`. - **Canal npm** (versiones publicadas): `dsh plugin --profile web add dsh-checkpoint-rewind`. - **Canal tarball**: `npm pack` en este repo y luego `dsh plugin --profile web add ./dsh-checkpoint-rewind-.tgz`. - **Pila de almacenamiento** (necesaria para los checkpoints, opcional para montar): `@deepseek-ai/dsh-storage` + `@deepseek-ai/dsh-storage-json` (config `root`) + `@deepseek-ai/dsh-storage-domain` (config `backend: json`) — ver Inicio rápido; el plugin se monta igualmente sin ella y cada comando explica la solución. - **Desinstalación**: `dsh plugin --profile web remove dsh-checkpoint-rewind` — los archivos de instantánea permanecen hasta que borres `$DSH_HOME/dsh-checkpoint-rewind`; los objetos git se recogen con el recolector de basura. ## Configuración Todas las opciones son campos Schemastery `Config` (modificables desde cordis.yml). Nada está hardcodeado. Las opciones de proveedor (`gitBin`, `snapshotDir`, `excludeGlobs`, `verifyByHash`) se leen de la configuración viva en el momento de uso, de modo que los cambios en cordis.yml se aplican sin reiniciar. | Clave | Por defecto | Significado | |---|---|---| | `enabled` | `true` | Interruptor maestro; en `false`, elimina comandos, listeners y proveedores por completo | | `provider` | `auto` | Proveedor de instantáneas: `auto` (git si está disponible, si no copy) · `git` · `copy` | | `gitBin` | `git` | Ruta del ejecutable de git | | `snapshotDir` | `$DSH_HOME/dsh-checkpoint-rewind` (repliegue `~/.dsh/dsh-checkpoint-rewind` si `$DSH_HOME` no está definido) | Raíz de las instantáneas del proveedor copy | | `maxSnapshots` | `50` | Checkpoints conservados por sesión (los más antiguos se podan primero) | | `maxSnapshotBytes` | `536870912` (512 MiB) | Cuota blanda global de bytes incrementales (siempre se conserva el más nuevo por sesión viva) | | `pruneOnTurnEnd` | `true` | Ejecuta la poda de cuota al terminar un turno | | `mutationTools` | `['bash','write','edit','str_replace_editor','pwsh','terminal_send']` | Herramientas tratadas como mutantes en `tools/pre-execute` | | `excludeGlobs` | `['node_modules','.git','.dsh','dist','build']` | Patrones glob omitidos por el proveedor copy | | `confirmVia` | `auto` | Canal de confirmación: `auto` (userQuestions primero) · `userQuestions` · `approval` | | `listLimit` | `10` | Checkpoints mostrados por `/rewind` sin argumentos | | `preRewindCheckpoint` | `warn` | Checkpoint de guardia antes de restaurar: `warn` · `require` · `off` | | `verifyByHash` | `false` | Comparación por hash de contenido y verificación de restauración del proveedor copy | | `autoCheckpoint.enabled` | `true` | Instantáneas automáticas por intervalo en `step/start` | | `autoCheckpoint.intervalMinutes` | `0` | Intervalo; `0` = cada paso | | `workspaceRestore` | `restore` | Reversión del workspace: `restore` (sobrescritura segura) · `reset-hard` (estilo CC, opt-in) | | `diffRenderer` | `pairwise` | Renderizador de diff de la página de ajustes: `pairwise` (texto línea a línea) · `side-by-side` (dos columnas por archivo) | | `selectiveRestore` | `true` | Restauración selectiva por archivo (`/rewind … --files`) y la casilla por archivo del panel + total de tamaño | | `promptSection` | `true` | Inyecta una sección breve de rol en el prompt | | `checkpointTool` | `true` | Registra la herramienta de modelo `checkpoint` | ```yaml - insert: - id: checkpoint-rewind name: dsh-checkpoint-rewind config: provider: auto maxSnapshots: 50 maxSnapshotBytes: 536870912 pruneOnTurnEnd: true confirmVia: auto preRewindCheckpoint: warn ``` ## Herramientas y superficies | Superficie | Tipo | Notas | |---|---|---| | `/rewind` | comando | `[workspace\|session\|config\|all] \|latest>` · `diff ` · `preview ` · `clear [--all]` | | `/checkpoint` | comando | `[note \|list\|diff ]` — captura un checkpoint manual | | `checkpoint` | herramienta | Captura un checkpoint manual con nota opcional | | `fs/write-intent` · `fs/edit-intent` · `tools/pre-execute` | listeners | Captura pre-mutación (prepend pass-through; nunca roba el hueco de política) | | `session/event` | listener | Seguimiento de turno/paso, intervalo automático, relleno de límites, poda al fin de turno | | Proyección `checkpoints` | proyección de sesión | Franja de línea de tiempo plegada desde el log de sesión | | Línea de tiempo de Ajustes | cliente | Pestaña `Plugins → Checkpoints` con diffs entre pares | ## Modelo de seguridad - **El historial de git es intocable.** El proveedor git solo ejecuta primitivas sin efectos secundarios de la lista blanca — `stash create`, `commit-tree`, `restore --worktree`, `ls-tree`, `diff-tree`, `ls-files`, `status`, `rev-parse`, `cat-file -e` — impuestas por una aserción en tiempo de ejecución, y las referencias de objetos se validan como ids hexadecimales antes de pasarlas a git (un registro manipulado no puede inyectar opciones de git). **Nunca `reset --hard` por defecto, nunca `clean`, nunca mutación de índice/historial** (ver `workspaceRestore` abajo). - **Reversión por sobrescritura, nunca borrado.** La restauración solo sobrescribe archivos capturados, y el proveedor git restaura **rutas explícitas** (`git restore … -- .` borraría archivos añadidos con `git add` después del checkpoint). Los archivos creados después del checkpoint (no rastreados **o** staged) se *informan* y se dejan en su sitio. - **Sin escrituras a través de enlaces, sin path traversal.** El proveedor copy valida las referencias de checkpoint antes de unirlas a las rutas del directorio de instantáneas, y se niega a restaurar a través de un destino (o ancestro) que se haya convertido en un enlace simbólico — así una restauración nunca puede seguir un enlace fuera del workspace. - **La restauración requiere aprobación.** Sobrescribir archivos del usuario siempre pasa por la costura de confirmación con semántica `ask`; un answerer ausente, que lanza error o que responde “no” **cierra en fallo**. `/rewind preview` es la forma de solo lectura de inspeccionar el impacto primero. - **La reversión es reversible.** Antes de restaurar, un checkpoint de guardia captura el estado actual; restaurar la guardia deshace la reversión. `preRewindCheckpoint: require` aborta la reversión cuando la guardia no puede capturarse. - **Transacción de orden fijo.** Primero la guardia, segundo el workspace, tercero la configuración, cuarto la reproducción de la sesión; cada fase se registra; una restauración fallida deja archivos, checkpoints y sesión intactos. - **`workspaceRestore: 'reset-hard'` equivale a CC y es opt-in.** Ejecuta `git reset --hard ` (la cabeza de la rama se mueve al commit de la instantánea; el historial previo a la instantánea sigue siendo recuperable vía reflog; los archivos no rastreados no se tocan). Está desactivado por defecto. - **Visible para el modelo ⟺ registrado.** Todo lo que un usuario o modelo ve se reconstruye a partir de `command/run` + `command/done` (y, una vez que el host los conoce, los eventos `checkpoint/*`) más el dominio durable `checkpoints`. ## Cómo funciona ```text capture ── fs/write-intent · fs/edit-intent · tools/pre-execute (prepend, pass-through) ── step/start auto interval ── /checkpoint · checkpoint tool ── pre-rewind guard │ ▼ ProviderRegistry.resolve(auto) → git: stash create / commit-tree │ copy: incremental dir + hardlinks ▼ checkpoints storage domain (SQLite rows / JSON file) + checkpoint/* event (adaptive gate) /rewind ── confirm (userQuestions / approval, fail-closed) ──▶ guard checkpoint ├─ workspace: provider.restore(ref) (restore | reset-hard) ├─ config: settings write-back (persisted on the profile patch) └─ session: SessionStore.fork(source, boundary) → new child session (original untouched) ``` Registro de decisiones completo, vocabulario de eventos y contrato de la costura de proveedores: [ARCHITECTURE.md](ARCHITECTURE.md). ## Eventos de sesión (nota rc.2) El plugin declara `checkpoint/snapshot`, `checkpoint/bound`, `checkpoint/prune` y `checkpoint/rewind` como miembros `SessionEventMap` solo de log. El harness rc.2 **no tiene superficie de registro de eventos para plugins** y `Session.append` descarta silenciosamente las claves de opciones desconocidas, por lo que añadir tipos desconocidos haría la sesión ilegible al recargarla. Por eso el plugin añade a través de una **puerta adaptativa**: una sonda en tiempo de ejecución (sobre un almacén de sesión separado, nunca persistido) detecta si el `append` del host sella el sobre `ignorable` — en rc.2 la puerta permanece cerrada; en hosts que lo soportan, los eventos `checkpoint/*` se añaden automáticamente con `ignorable: true`. Hasta entonces, la cadena de auditoría autoritativa es `command/run` + `command/done` (conocidos por el harness) más el dominio de almacenamiento durable `checkpoints`. ## Ancla de Web UI El plugin devuelve el id de la nueva sesión en el resultado del comando (`session: `) y el shell web puede navegar allí. **La unidad de proyección de sesión `checkpoints` viene incluida**: siempre que `ctx.sessionProjections` exista, el plugin registra la unidad vía `ctx.inject` (pliega `checkpoint/snapshot|bound|prune|rewind` en una lista de valor completo) — permanece como lista vacía en hosts rc.2 hasta que una build del harness incluya el vocabulario `checkpoint/*` o el sobre `ignorable`, y entonces se llena sin cambios en el plugin. ## FAQ **¿Esto reemplaza a git?** No — lo *usa* donde está disponible. En un repo git obtienes objetos de instantánea exactos al byte, deduplicados, sin tocar el historial; en cualquier otro directorio, el proveedor copy hace lo mismo con archivos normales. Los commits regulares siguen siendo tu historial a largo plazo. **¿Por qué no usar `git reset --hard` por defecto?** Porque destruir estado no es el trabajo de una red de seguridad. El plugin solo crea objetos no referenciados y realiza restauraciones exclusivas del árbol de trabajo y explícitas por ruta por defecto, de modo que un retroceso defectuoso nunca pueda perder el historial, el índice o los archivos creados después del checkpoint. `reset-hard` está disponible detrás de `workspaceRestore: 'reset-hard'` para usuarios que desean explícitamente paridad con CC. **¿Cuánto tiempo es restaurable un checkpoint?** No se mantiene ninguna referencia para los snapshots de git-provider. Son objetos no referenciados de `stash create`/`commit-tree` por diseño, y `discard` solo elimina el registro de metadatos, dejando el objeto para `git gc`. Dos límites independientes definen la ventana de restauración real: la cuota del plugin (`maxSnapshots` por defecto 50 por sesión; `maxSnapshotBytes` 512 MiB de cuota blanda global aplicada mediante `pruneAll()` en cada captura y limpiada al final de turnos) y el git gc del repositorio host (`gc.pruneExpire` por defecto 2 semanas). Un `git gc --prune=now` manual, repack agresivo o clon fresco los elimina inmediatamente. Los objetos purgados ahora se detectan por adelantado: el proveedor git sondea el objeto de instantánea antes de restaurar (`git cat-file -e`) y falla en voz alta si falta, en lugar de no restaurar nada, y un paso doctor recorre cada registro de checkpoint para sondear la existencia del objeto y marcar los no restaurables (`[UNRESTORABLE: object missing]` en la lista). El paso doctor está implementado y probado, pero aún no está conectado a la ruta de restauración (restore aún no consulta la marca `unrestorable`; seguimiento en discussion #13). **¿Puedo retroceder a un paso en medio de un turno?** La restauración de archivos es precisa por pasos (`/rewind step ` = snapshot más cercano ≤ N). La reproducción de la sesión, sin embargo, respeta la granularidad de reproducción del harness: la sesión hija se inicializa hasta el límite del turno del checkpoint. **¿Qué pasa si nadie puede responder a la confirmación?** No se toca nada — el plugin cierra en fallo (`unavailable`/`rejected`), conserva el checkpoint y devuelve un error explicativo. Con `confirmVia: approval` en rc.2 el mensaje dice que montes userQuestions, porque approval requiere un turno abierto y los comandos se ejecutan entre turnos. **¿Puedo deshacer una reversión?** Sí — cada reversión aprobada captura primero un checkpoint de guardia del estado previo a la reversión; el resultado imprime `rewind guard: `, y `/rewind ` restaura ese estado. **¿Qué hace `preview` — y qué no hace?** Resuelve el checkpoint y ejecuta una comparación de solo lectura: qué archivos se sobrescribirían (o recrearían), cuáles ya coinciden y qué archivos creados después del checkpoint se dejarían en su sitio. Nunca pregunta, nunca escribe, nunca bifurca y no registra ningún evento `checkpoint/rewind` — la puerta de aprobación solo se ejecuta en un `/rewind ` real. ## Demo Una ejecución real de integración headless ensamblada (`npm run test:integration`) recorre el flujo completo: el agente modifica archivos a lo largo de dos turnos, luego `/rewind preview` inspecciona el impacto en modo solo lectura (sin puerta de confirmación, sin escrituras) y `/rewind ` restaura los archivos y reproduce la sesión en una nueva sesión hija. La ejecución verifica el contenido de los archivos, el contexto del hijo reproducido, el checkpoint de guardia y la supervivencia de los archivos creados después del checkpoint — para los flujos de proveedor copy y git (el flujo git también verifica que `HEAD` y el reflog quedan intactos). El controlador vive en `test/integration/rewind-headless.mjs`. ## Permisos y datos - **Permisos**: el manifiesto del workshop declara `workspace:read`, `workspace:write`, `git:read`, `git:write`, `snapshot-storage:write`, `session-log:read`, `settings:write` y `network:none`. - **Datos**: los registros de checkpoint viven en el dominio de almacenamiento `checkpoints` (filas SQLite o un archivo JSON); las instantáneas de copia viven bajo `snapshotDir`. Totalmente local — sin red, sin credenciales. El dominio se abre en doble versión: los medios de la era 0.4.x (dominio v1) se abren en modo de compatibilidad que mantiene legibles los registros antiguos y guarda las nuevas capturas con la forma v2, de modo que actualizar el plugin nunca deja huérfano un medio existente. - **Registro de sesión**: los eventos `checkpoint/*` se añaden mediante la puerta adaptativa; la cadena de auditoría autoritativa es `command/run` + `command/done` más el dominio durable. ## Límites de seguridad - **El historial de git es intocable.** Primitivas sin efectos secundarios de la lista blanca; `reset --hard` solo detrás del modo opt-in `workspaceRestore: 'reset-hard'`. Nunca `git clean`. - **Reversión por sobrescritura, nunca borrado.** La restauración sobrescribe solo archivos capturados; los archivos creados después del checkpoint se informan y se dejan en su sitio. - **Sin escrituras a través de enlaces, sin path traversal.** Los `ref` de copy se validan como ids de instantánea; la restauración se niega a seguir enlaces simbólicos fuera del workspace. - **La restauración requiere aprobación.** Un answerer ausente o que niega cierra en fallo. - **La reversión es reversible.** Primero se captura un checkpoint de guardia del estado previo a la reversión. ## Limitaciones conocidas - En rc.2, los eventos de sesión `checkpoint/*` son suprimidos por la puerta adaptativa; la cadena de auditoría usa `command/run` + `command/done` más el dominio de almacenamiento hasta que un host incluya el vocabulario o el sobre `ignorable`. - `confirmVia: approval` necesita un turno abierto, y los comandos se ejecutan entre turnos — monta userQuestions (o define `confirmVia: userQuestions`) en rc.2. - La reversión de sesión **bifurca (fork) una nueva sesión hija** en el límite del checkpoint; nunca reescribe ni trunca la sesión original. - `workspaceRestore: 'reset-hard'` mueve la cabeza de la rama al commit de la instantánea; está desactivado por defecto. - Un checkpoint capturado antes de cualquier turno cerrado no tiene límite de reproducción — la reversión de sesión crea entonces una sesión hija nueva con contexto vacío. ## Solución de problemas | Síntoma | Causa / solución | |---|---| | `/rewind ` dice `rewind cancelled: no confirmation answerer` | No hay ningún canal userQuestions/approval montado — el plugin cierra en fallo. Ejecútalo en la Web UI (o monta un proveedor de preguntas); `confirmVia` selecciona el canal. | | `/rewind ` dice `approval requires an open turn …` | Los comandos se ejecutan entre turnos y approval necesita un turno — monta userQuestions o define `confirmVia: userQuestions`. | | `rewind: checkpoint registry unavailable` | El dominio de almacenamiento `checkpoints` no pudo abrirse. O el servicio `storageDomain` no está compuesto (añade las filas de la pila de almacenamiento de Inicio rápido: `@deepseek-ai/dsh-storage` + `@deepseek-ai/dsh-storage-json` con config `root` + `@deepseek-ai/dsh-storage-domain` con config `backend: json`) o el backend tiene errores; revisa los logs del harness. | | Un checkpoint aparece como `fork: pending (turn not closed)` | Su turno aún no tiene `turn/end`; los archivos aún pueden restaurarse, pero la reproducción de la sesión espera a que el turno se cierre. | | `files restored … but the session was NOT replayed` | La fase de sesión de la transacción falló (sin límite cerrado, o reproducción rechazada). Los archivos siguen restaurados; usa el `rewind guard: ` impreso para deshacer. | | `rewind: aborted — the pre-rewind guard checkpoint could not be captured` | `preRewindCheckpoint: require` rechazó la reversión porque falló la captura de la guardia; arregla el almacenamiento (o define `warn`/`off`). | | Un checkpoint aparece como `(copy)` aunque el directorio es un repo | HEAD no nacido (sin commit inicial): las primitivas de instantánea git requieren HEAD, así que el plugin degrada a `copy` hasta el primer commit. | | `MISSING_CREDENTIAL` en ejecuciones headless | No relacionado con este plugin: no hay `DEEPSEEK_API_KEY` configurada para el proveedor del modelo. | | El almacenamiento de instantáneas crece | La poda corre después de cada instantánea y en `turn/end` (`pruneOnTurnEnd`); baja `maxSnapshots` / `maxSnapshotBytes`, ejecuta `/rewind clear`, o borra `$DSH_HOME/dsh-checkpoint-rewind` después de desinstalar. | ## Desarrollo ```sh npm install # peer deps: @deepseek-ai/dsh-session@0.1.2-rc.1, schemastery, zod npm test # node --test test/**/*.test.mjs (incl. suites de proveedores) npm run test:integration # verificación headless ensamblada (test/integration/) ``` Sin paso de build: ESM puro — `index.mjs`/`lib/` son los artefactos publicados. ## Temas `deepseek-harness`, `dsh`, `dsh-plugin`, `rewind`, `checkpoint`, `snapshot`, `session-replay`, `session-fork`, `config-restore`, `workspace-safety`, `undo`, `cordis-plugin` ## Contribuidores - [@PerryLink](https://github.com/PerryLink) — creador y mantenedor: el modelo de checkpoint de tres estados, la costura de proveedores git/copy, la transacción de reversión en tres fases, la línea de tiempo de Ajustes, la documentación, CI/CD y releases. - [@tmpdot](https://github.com/tmpdot) (rmUnlucky) — compatibilidad de doble versión para medios v1 (#8), resolución perezosa de `storageDomain` (#9), pistas de respuesta en la deduplicación de checkpoints (#10) y el informe del bug del directorio de instantáneas con `$DSH_HOME` sin definir (#4). - [@shipinliang](https://github.com/shipinliang) — informes del contrato wire del panel de checkpoints: el `acceptsUndefined` ausente en el parámetro `limit` (#5) y el fallo de proxy por el campo privado `#deps` (#6). - [@hwz1456](https://github.com/hwz1456) — informe de captura de checkpoints en el perfil web (#3). - [@Andiii208](https://github.com/Andiii208) — informe de la especificación de publicación de `plugin_check` (#2). - [@alexchenzl](https://github.com/alexchenzl) (Ashu) — invitación al listado del DSH Directory (#7). ### Instalar desde el mercado de DSH Desktop Todos los plugins de PerryLink pueden explorarse en el mercado integrado de DSH Desktop: **Market → Sources → add source → pegar** `https://perrylink-dsh-catalog.perrylink.workers.dev/catalog-source.json` **→ seleccionarlo**. La instalación sigue pasando por la verificación de identidad npm del mercado y tu confirmación. ## Licencia [Apache License 2.0](LICENSE) © 2026 dsh-checkpoint-rewind contributors ## PerryLink DSH Plugin Family This project is one of the **45 DeepSeek Harness plugins** maintained by [PerryLink](https://github.com/PerryLink). If this one helps you, the others likely will too: | Plugin | One-liner | |---|---| | **[dsh-auto-review](https://github.com/PerryLink/dsh-auto-review)** | Second-model auto-review on the approval chain, fail-closed by default | | | **[dsh-autotier](https://github.com/PerryLink/dsh-autotier)** | Automatic strong/cheap model-tier routing with deterministic risk guards and a `/tier` command | | | **[dsh-background-agents](https://github.com/PerryLink/dsh-background-agents)** | Durable background child agents with a Web UI sidebar, messaging and interrupt | | | **[dsh-budget](https://github.com/PerryLink/dsh-budget)** | Cost governance for DeepSeek Harness: budgets, carbon, and latency in one panel. | | | **[dsh-catalog](https://github.com/PerryLink/dsh-catalog)** | DSH Desktop Market standard catalog source for the PerryLink family | | | **[dsh-cert-mcp](https://github.com/PerryLink/dsh-cert-mcp)** | Read-only MCP server exposing the certification registry: grades, snapshots and five-dimension evidence | | | **[dsh-checkpoint-rewind](https://github.com/PerryLink/dsh-checkpoint-rewind)** | Claude Code /rewind-equivalent: snapshots, session forks, one-shot restore | | | **[dsh-claude-move](https://github.com/PerryLink/dsh-claude-move)** | Migrate Claude Code sessions, memory, skills and CLAUDE.md into DSH | | | **[dsh-click](https://github.com/PerryLink/dsh-click)** | Cross-platform native desktop control for DeepSeek Harness — Windows first. | | | **[dsh-composer-history](https://github.com/PerryLink/dsh-composer-history)** | Terminal-style input history for the web composer: arrows, Ctrl+R search | | | **[dsh-data-quality](https://github.com/PerryLink/dsh-data-quality)** | Dataset quality checks and citation cross-checks (the optional numeric bridge consumed here) | | | **[dsh-defend](https://github.com/PerryLink/dsh-defend)** | Prompt-injection, jailbreak, and secret-leak defense for DeepSeek Harness. | | | **[dsh-doublecheck](https://github.com/PerryLink/dsh-doublecheck)** | Engineering-discipline guard: requirements grill, test gates, adversary review | | | **[dsh-draw](https://github.com/PerryLink/dsh-draw)** | Unified static-image generation routing for DeepSeek Harness. | | | **[dsh-fast](https://github.com/PerryLink/dsh-fast)** | Read-only performance diagnostics for DeepSeek Harness. | | | **[dsh-fund-research](https://github.com/PerryLink/dsh-fund-research)** | Deterministic research reports for Chinese public mutual funds | | | **[dsh-github](https://github.com/PerryLink/dsh-github)** | GitHub PR/issues integration for DSH, every write gated by approval | | | **[dsh-industry-research](https://github.com/PerryLink/dsh-industry-research)** | Industry research orchestration that seals its deliverables through this plugin's `ctx.researchReport.assemble` | | | **[dsh-laya](https://github.com/PerryLink/dsh-laya)** | Laya typed decisions (`noul`/`choice`/`score`) as a first-class Cordis service and model-visible tools | | | **[dsh-library](https://github.com/PerryLink/dsh-library)** | Local document knowledge base for DeepSeek Harness. | | | **[dsh-local-ai](https://github.com/PerryLink/dsh-local-ai)** | Local-model (Ollama) integration for DeepSeek Harness. | | | **[dsh-lsp-actions](https://github.com/PerryLink/dsh-lsp-actions)** | LSP diagnostics, formatting, completion, code actions and rename over language servers | | | **[dsh-mask](https://github.com/PerryLink/dsh-mask)** | PII masking middleware: anonymize at the model boundary, restore at the display layer | | | **[dsh-mcp-panel](https://github.com/PerryLink/dsh-mcp-panel)** | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors | | | **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool | | | **[dsh-observe](https://github.com/PerryLink/dsh-observe)** | OpenTelemetry and Langfuse observability exporter for DeepSeek Harness. | | | **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Claude Code outputStyles-equivalent runtime style switching | | | **[dsh-permission-rules](https://github.com/PerryLink/dsh-permission-rules)** | Claude Code-style declarative allow/deny/ask permission rules with audit | | | **[dsh-plugin-certification](https://github.com/PerryLink/dsh-plugin-certification)** | Community certification registry with repro-checkable grades and badges | | | **[dsh-plugin-doctor](https://github.com/PerryLink/dsh-plugin-doctor)** | Zero-dependency static + sandbox smoke detector for DSH plugins | | | **[dsh-plugin-guide](https://github.com/PerryLink/dsh-plugin-guide)** | Plugin-development knowledge base as an on-demand agent skill | | | **[dsh-plugin-kit](https://github.com/PerryLink/dsh-plugin-kit)** | Shared zero-runtime-dependency toolkit for the PerryLink DSH plugins | | | **[dsh-plugin-upgrade](https://github.com/PerryLink/dsh-plugin-upgrade)** | One-package, one-corridor-index plugin upgrade skill: routes a repository to the matching closed corridor card | | | **[dsh-plugin-upgrade-015](https://github.com/PerryLink/dsh-plugin-upgrade-015)** | Merged `0.1.3-alpha.1` → `0.1.5-rc.1` upgrade corridor card plus a zero-dependency seam scanner | | | **[dsh-reach](https://github.com/PerryLink/dsh-reach)** | Multi-channel approval/question bridge: WeChat/Telegram/Feishu, session console | | | **[dsh-research-report](https://github.com/PerryLink/dsh-research-report)** | Verifiable research-report engine: content-addressed evidence ledger and sealed versions | | | **[dsh-score](https://github.com/PerryLink/dsh-score)** | Multi-dimensional quality scoring for DeepSeek Harness plugins. | | | **[dsh-session-pin](https://github.com/PerryLink/dsh-session-pin)** | Pin sessions in the Web sidebar with durable ordering | | | **[dsh-session-sync](https://github.com/PerryLink/dsh-session-sync)** | Cross-device session sync for DeepSeek Harness — a dedicated git mirror of your session store. | | | **[dsh-skill-pack-security](https://github.com/PerryLink/dsh-skill-pack-security)** | Security-audit skill pack: secret scan, dependency and supply-chain review | | | **[dsh-talk](https://github.com/PerryLink/dsh-talk)** | Voice-first session loop for DeepSeek Harness: talk to it, hear it answer. | | | **[dsh-team-rooms](https://github.com/PerryLink/dsh-team-rooms)** | Cross-session team rooms: shared message bus, task board and timeline | | | **[dsh-test-drive](https://github.com/PerryLink/dsh-test-drive)** | Isolated install-and-smoke test drives for DeepSeek Harness plugins. | | | **[dsh-ticktick](https://github.com/PerryLink/dsh-ticktick)** | TickTick/Dida365 task bridge: session-header panel + 11 tools | | | **[dsh-translate](https://github.com/PerryLink/dsh-translate)** | Vendor parameter translation and deterministic JSON repair for DeepSeek Harness. | |