diff --git a/gateway/slash_commands.py b/gateway/slash_commands.py index 6c32606974..001d6b7469 100644 --- a/gateway/slash_commands.py +++ b/gateway/slash_commands.py @@ -3487,6 +3487,14 @@ class GatewaySlashCommandsMixin: "gateway.rollback.kept_oversize", files=shown + more, ) + failed = result.get("failed_deletes") or [] + if failed: + shown = ", ".join(failed[:5]) + more = f" (+{len(failed) - 5})" if len(failed) > 5 else "" + msg += "\n" + t( + "gateway.rollback.failed_deletes", + files=shown + more, + ) return msg return t("gateway.rollback.restore_failed", error=result["error"]) diff --git a/hermes_cli/cli_commands_mixin.py b/hermes_cli/cli_commands_mixin.py index d399f2fa1e..d16050a495 100644 --- a/hermes_cli/cli_commands_mixin.py +++ b/hermes_cli/cli_commands_mixin.py @@ -170,6 +170,11 @@ class CLICommandsMixin: shown = ", ".join(oversize[:5]) more = f" (+{len(oversize) - 5} more)" if len(oversize) > 5 else "" print(f" ↷ Kept (too large for checkpoints, no stored copy to revert to): {shown}{more}") + failed = result.get("failed_deletes") or [] + if failed: + shown = ", ".join(failed[:5]) + more = f" (+{len(failed) - 5} more)" if len(failed) > 5 else "" + print(f" ⚠️ Could not remove (left in place): {shown}{more}") print(" A pre-rollback snapshot was saved automatically.") # Also undo the last conversation turn so the agent's context diff --git a/locales/af.yaml b/locales/af.yaml index b1606af7bd..4a5aa89bd6 100644 --- a/locales/af.yaml +++ b/locales/af.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ Herstel na kontrolepunt {hash}: {reason}\n'n Voor-terugrol-momentopname is outomaties gestoor." kept_user_edits: "↷ Jou handwysigings is behou: {files}\nGebruik /rollback --all om dié ook te herstel." kept_oversize: "↷ Behou (te groot vir kontrolepunte, geen gestoorde kopie om na terug te keer nie): {files}" + failed_deletes: "⚠️ Kon nie verwyder nie (in plek gelaat): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/ar.yaml b/locales/ar.yaml index 8230c2c2c9..933b10f361 100644 --- a/locales/ar.yaml +++ b/locales/ar.yaml @@ -301,6 +301,7 @@ gateway: restored: "✅ استُعيد إلى نقطة التحقّق {hash}: {reason}\nحُفظت لقطة ما قبل التراجع تلقائيًا." kept_user_edits: "↷ احتُفظ بتعديلاتك اليدوية: {files}\nاستخدم ‎/rollback --all لاستعادتها أيضًا." kept_oversize: "↷ احتُفظ به (أكبر من أن يُحفظ في نقاط التفتيش، لا توجد نسخة مخزنة للرجوع إليها): {files}" + failed_deletes: "⚠️ تعذّرت الإزالة (تُرك الملف في مكانه): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/de.yaml b/locales/de.yaml index c7006020f8..8a18ff65cf 100644 --- a/locales/de.yaml +++ b/locales/de.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ Auf Checkpoint {hash} wiederhergestellt: {reason}\nEin Pre-Rollback-Snapshot wurde automatisch gespeichert." kept_user_edits: "↷ Deine manuellen Änderungen wurden behalten: {files}\nNutze /rollback --all, um auch diese wiederherzustellen." kept_oversize: "↷ Behalten (zu groß für Checkpoints, keine gespeicherte Kopie zum Zurücksetzen): {files}" + failed_deletes: "⚠️ Konnte nicht entfernt werden (an Ort und Stelle belassen): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/en.yaml b/locales/en.yaml index dbce02e0c4..34b0904cf0 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -293,6 +293,7 @@ gateway: restored: "✅ Restored to checkpoint {hash}: {reason}\nA pre-rollback snapshot was saved automatically." kept_user_edits: "↷ Kept your hand-edits: {files}\nUse /rollback --all to restore those too." kept_oversize: "↷ Kept (too large for checkpoints, no stored copy to revert to): {files}" + failed_deletes: "⚠️ Could not remove (left in place): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/es.yaml b/locales/es.yaml index 65fa1aab15..a51c05ef82 100644 --- a/locales/es.yaml +++ b/locales/es.yaml @@ -278,6 +278,7 @@ gateway: restored: "✅ Restaurado al checkpoint {hash}: {reason}\nSe guardó automáticamente un snapshot previo al rollback." kept_user_edits: "↷ Se conservaron tus ediciones manuales: {files}\nUsa /rollback --all para restaurarlas también." kept_oversize: "↷ Conservado (demasiado grande para los checkpoints, no hay copia guardada a la que revertir): {files}" + failed_deletes: "⚠️ No se pudo eliminar (se dejó en su lugar): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/fr.yaml b/locales/fr.yaml index 481494a8d3..df65467285 100644 --- a/locales/fr.yaml +++ b/locales/fr.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ Restauré au point de contrôle {hash} : {reason}\nUn instantané pré-rollback a été enregistré automatiquement." kept_user_edits: "↷ Vos modifications manuelles ont été conservées : {files}\nUtilisez /rollback --all pour les restaurer aussi." kept_oversize: "↷ Conservé (trop volumineux pour les checkpoints, aucune copie enregistrée vers laquelle revenir) : {files}" + failed_deletes: "⚠️ Suppression impossible (laissé en place) : {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/ga.yaml b/locales/ga.yaml index 4658541457..ff061d4471 100644 --- a/locales/ga.yaml +++ b/locales/ga.yaml @@ -285,6 +285,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ Aischurtha go seicphointe {hash}: {reason}\nSábháladh roghchóip réamh-rollback go huathoibríoch." kept_user_edits: "↷ Coinníodh do chuid athruithe láimhe: {files}\nÚsáid /rollback --all chun iad sin a aischur freisin." kept_oversize: "↷ Coinnithe (ró-mhór do sheicphointí, níl aon chóip stóráilte le filleadh uirthi): {files}" + failed_deletes: "⚠️ Níorbh fhéidir a bhaint (fágtha ina áit): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/hu.yaml b/locales/hu.yaml index 334a6f9838..d85807ae21 100644 --- a/locales/hu.yaml +++ b/locales/hu.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ Visszaállítva a(z) {hash} ellenőrzőpontra: {reason}\nA visszaállítás előtti pillanatkép automatikusan elmentve." kept_user_edits: "↷ A kézi szerkesztéseid megmaradtak: {files}\nHasználd a /rollback --all parancsot, hogy azokat is visszaállítsd." kept_oversize: "↷ Megtartva (túl nagy az ellenőrzőpontokhoz, nincs tárolt másolat, amire vissza lehetne állni): {files}" + failed_deletes: "⚠️ Nem sikerült eltávolítani (a helyén maradt): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/it.yaml b/locales/it.yaml index 4124945df3..8a7c55b861 100644 --- a/locales/it.yaml +++ b/locales/it.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ Ripristinato al checkpoint {hash}: {reason}\nUno snapshot pre-rollback è stato salvato automaticamente." kept_user_edits: "↷ Le tue modifiche manuali sono state conservate: {files}\nUsa /rollback --all per ripristinare anche quelle." kept_oversize: "↷ Conservato (troppo grande per i checkpoint, nessuna copia salvata a cui tornare): {files}" + failed_deletes: "⚠️ Impossibile rimuovere (lasciato al suo posto): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/ja.yaml b/locales/ja.yaml index c96bf559a6..7f1bed5918 100644 --- a/locales/ja.yaml +++ b/locales/ja.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ チェックポイント {hash} に復元しました: {reason}\nロールバック前のスナップショットが自動的に保存されました。" kept_user_edits: "↷ 手動編集は保持されました: {files}\nそれらも復元するには /rollback --all を使用してください。" kept_oversize: "↷ 保持しました(チェックポイントには大きすぎるため、戻せる保存コピーがありません): {files}" + failed_deletes: "⚠️ 削除できませんでした(そのまま残っています): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/ko.yaml b/locales/ko.yaml index 07990df2e4..b240547aaf 100644 --- a/locales/ko.yaml +++ b/locales/ko.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ 체크포인트 {hash}(으)로 복원됨: {reason}\n롤백 전 스냅샷이 자동으로 저장되었습니다." kept_user_edits: "↷ 직접 수정한 내용은 유지되었습니다: {files}\n해당 파일도 복원하려면 /rollback --all 을 사용하세요." kept_oversize: "↷ 유지됨 (체크포인트에 저장하기엔 너무 커서 되돌릴 저장본이 없습니다): {files}" + failed_deletes: "⚠️ 제거할 수 없었습니다 (그대로 남아 있음): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/pt.yaml b/locales/pt.yaml index c40b0c0389..7a8a131c5d 100644 --- a/locales/pt.yaml +++ b/locales/pt.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ Restaurado para o checkpoint {hash}: {reason}\nFoi guardado automaticamente um snapshot anterior ao rollback." kept_user_edits: "↷ As suas edições manuais foram mantidas: {files}\nUse /rollback --all para restaurar essas também." kept_oversize: "↷ Mantido (grande demais para os checkpoints, sem cópia guardada para reverter): {files}" + failed_deletes: "⚠️ Não foi possível remover (mantido no lugar): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/ru.yaml b/locales/ru.yaml index be08f04435..a4220d3d28 100644 --- a/locales/ru.yaml +++ b/locales/ru.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ Восстановлено до контрольной точки {hash}: {reason}\nСнимок перед откатом сохранён автоматически." kept_user_edits: "↷ Ваши ручные правки сохранены: {files}\nИспользуйте /rollback --all, чтобы восстановить и их." kept_oversize: "↷ Сохранено (слишком большой для контрольных точек, нет сохранённой копии для отката): {files}" + failed_deletes: "⚠️ Не удалось удалить (файл оставлен на месте): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/tr.yaml b/locales/tr.yaml index 791c364c16..c0ffa802a3 100644 --- a/locales/tr.yaml +++ b/locales/tr.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ {hash} kontrol noktasına geri yüklendi: {reason}\nGeri alma öncesi anlık görüntü otomatik olarak kaydedildi." kept_user_edits: "↷ Elle yaptığınız düzenlemeler korundu: {files}\nOnları da geri yüklemek için /rollback --all kullanın." kept_oversize: "↷ Korundu (denetim noktaları için çok büyük, geri dönülecek kayıtlı kopya yok): {files}" + failed_deletes: "⚠️ Kaldırılamadı (yerinde bırakıldı): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/uk.yaml b/locales/uk.yaml index 123b70f079..43ccae3911 100644 --- a/locales/uk.yaml +++ b/locales/uk.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ Відновлено до контрольної точки {hash}: {reason}\nЗнімок перед відкатом збережено автоматично." kept_user_edits: "↷ Ваші ручні правки збережено: {files}\nВикористайте /rollback --all, щоб відновити і їх." kept_oversize: "↷ Збережено (завеликий для контрольних точок, немає збереженої копії для відкату): {files}" + failed_deletes: "⚠️ Не вдалося видалити (залишено на місці): {files}" restore_failed: "❌ {error}" diff: diff --git a/locales/zh-hant.yaml b/locales/zh-hant.yaml index f61216e45d..1b26f787fd 100644 --- a/locales/zh-hant.yaml +++ b/locales/zh-hant.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ 已還原至檢查點 {hash}:{reason}\n已自動儲存回復前的快照。" kept_user_edits: "↷ 已保留您的手動編輯:{files}\n若也要還原這些檔案,請使用 /rollback --all。" kept_oversize: "↷ 已保留(檔案過大無法納入檢查點,沒有可還原的儲存副本):{files}" + failed_deletes: "⚠️ 無法移除(保留原檔):{files}" restore_failed: "❌ {error}" diff: diff --git a/locales/zh.yaml b/locales/zh.yaml index 686cb1ea55..d7bed9149d 100644 --- a/locales/zh.yaml +++ b/locales/zh.yaml @@ -281,6 +281,7 @@ Future messages in this room will use that transcript until `/reset` or another restored: "✅ 已恢复到检查点 {hash}:{reason}\n已自动保存回滚前的快照。" kept_user_edits: "↷ 已保留您的手动编辑:{files}\n如需一并恢复这些文件,请使用 /rollback --all。" kept_oversize: "↷ 已保留(文件过大无法纳入检查点,没有可恢复的存储副本):{files}" + failed_deletes: "⚠️ 无法移除(保留原文件):{files}" restore_failed: "❌ {error}" diff: diff --git a/tests/tools/test_checkpoint_manager.py b/tests/tools/test_checkpoint_manager.py index 62499fe71c..67cdaac9f6 100644 --- a/tests/tools/test_checkpoint_manager.py +++ b/tests/tools/test_checkpoint_manager.py @@ -527,6 +527,7 @@ class TestSafeRestore: assert not scratch.exists() assert "scratch.txt" in result["restored_files"] assert result["skipped_oversize"] == [] + assert "failed_deletes" not in result def test_safe_restore_does_not_report_a_failed_delete_as_restored( self, mgr, work_dir, monkeypatch, @@ -554,6 +555,7 @@ class TestSafeRestore: assert result["success"] is True assert stubborn.exists() assert "stubborn.txt" not in result["restored_files"] + assert result["failed_deletes"] == ["stubborn.txt"] def test_unsafe_restore_overwrites_everything(self, mgr, work_dir): base = self._checkpoint(mgr, work_dir) diff --git a/tools/checkpoint_manager.py b/tools/checkpoint_manager.py index 7c821fbeb2..1061874b41 100644 --- a/tools/checkpoint_manager.py +++ b/tools/checkpoint_manager.py @@ -1098,7 +1098,10 @@ class CheckpointManager: With ``safe=True`` (full-directory restores only), files the user hand-edited after Hermes' last write — per the agent-write ledger — are left untouched, and only Hermes-authored changes are reverted. - The result gains ``skipped_user_edits`` listing the preserved paths. + The result gains ``skipped_user_edits`` listing the preserved paths, + ``skipped_oversize`` listing paths kept because the size cap excluded + them from every checkpoint, and — only when a delete failed — + ``failed_deletes`` listing paths that could not be removed. """ hash_err = _validate_commit_hash(commit_hash) if hash_err: @@ -1146,6 +1149,7 @@ class CheckpointManager: "directory": abs_dir, "restored_files": [], "skipped_user_edits": skipped_user_edits, + "skipped_oversize": [], } # Take a pre-rollback snapshot so you can undo the undo.