feat: add backend workspace git APIs - #11
Merged
Merged
Conversation
Adds a "Download Folder" item to the workspace file-tree right-click menu and a GET /api/folder/download endpoint that streams the directory as a zip with Content-Disposition: attachment. Configurable caps: HERMES_WEBUI_FOLDER_ZIP_MAX_MB (default 1024) HERMES_WEBUI_FOLDER_ZIP_MAX_FILES (default 50000) Pre-flights the walk so cap-exceeded returns 413 + JSON BEFORE any zip bytes are sent. Symlinks resolving outside the workspace are skipped. Mirrors the existing _handle_file_raw shape (session_id resolution, safe_resolve, RFC 5987 filename via _content_disposition_value). Stdlib zipfile only; no new dependencies. Tests: 11 static-inspection tests matching the style of tests/test_issue1867_upload_size_preflight.py. All passing on Python 3.11/3.12/3.13.
When API server runs append messages directly to state.db, reconcile WebUI sidecar sessions with those canonical rows across API responses, model-facing streaming context, and active browser refresh. Add append-only state.db merge helpers, metadata-only counts for refresh polling, and regression coverage for API visibility, context incorporation, and frontend refresh behavior.
Force same-session external refreshes to dismiss stale approval and clarification prompts immediately so completed state.db updates do not leave the composer blocked.
When a queued message was waiting for the active stream to finish, the 120ms setTimeout drain in setBusy(false) would write the queued text to the shared #msg composer and call send(), which reads S.session.session_id at call time. If the user switched to a different chat during the 120ms window, the queued message was sent to the wrong session. Two fixes: 1. setBusy(false) drain: guard the setTimeout callback — if the currently viewed session no longer matches the drain session, put the message back into the original session's queue instead of sending it. 2. _sendInProgress re-queue: track _sendInProgressSid alongside _sendInProgress so that when a concurrent send() is caught by the guard, the re-queued message targets the in-flight session rather than the currently viewed one.
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
# Conflicts: # api/streaming.py
# Conflicts: # CHANGELOG.md
CI parity tests enforce that every key in the English locale block exists in zh, ja, ko, ru, and es. The PR introducing download_folder added it to en only, which broke the 5 hard-parity test files. Adds the English fallback to all 10 non-en blocks (it/ja/ru/es/de/zh/zh-Hant/pt/ko/fr) with the project's // TODO: translate marker so translators can refine later. Tests: tests/test_chinese_locale.py, test_japanese_locale.py, test_korean_locale.py, test_russian_locale.py, test_spanish_locale.py — 26/26 passing locally.
# Conflicts: # CHANGELOG.md
Unreleased section now reflects: - PR nesquena#2598 live tool event dedup (AJV20) - PR nesquena#2533 browser dashboard links (AJV20) - PR nesquena#2607 messaging transcript dedup (AJV20) - PR nesquena#2521 Geist Contrast skin (intellectronica) - PR nesquena#2524 SSE runtime diagnostics endpoint (AJV20) Removed merge markers and consolidated stray entries that leaked into the v0.51.94 release block.
PR nesquena#2521 (Geist Contrast skin) legitimately adds a scoped `:root[data-skin="geist-contrast"] .theme-pick-btn.active` override that appears earlier in style.css than the global `#mainSettings .theme-pick-btn.active` rule. The naive substring search in tests/test_1059_settings_picker_active_state.py found the skin-specific override first (which correctly uses --border2 for its palette), failing the global assertion that wanted --accent. Tighten both assertions to anchor on the `#mainSettings` selector prefix so they always match the global rule regardless of how many skin-specific overrides land in the file.
…assertion PR nesquena#2521 (Geist Contrast skin) adds a scoped `:root[data-skin="geist-contrast"] .session-item.active .session-title` rule that legitimately uses its own palette values. The existing assertion in test_sprint40_ui_polish.py matched on any line containing the `.session-item.active .session-title` substring, picking up the skin-scoped override and asserting against its palette. Exclude lines containing `:root[data-skin=` from the base-rule scan so skin-scoped overrides are free to use their own design tokens, while the global rule still enforces var(--gold) / var(--accent-text).
release: v0.51.95 (Release BS / stage-388 / 5-PR batch — live tool dedup + browser dashboard links + messaging dedup + Geist Contrast skin + SSE diagnostics)
Distinguish CRLF-only working tree changes from filemode-only noise when the ignored-CR diff path set is empty on GitHub Actions.
stocky789
pushed a commit
that referenced
this pull request
Jun 4, 2026
…rs (Codex review #11) Per-profile WebUI state lives at <root>/webui_state (api/workspace.py), so <base>/profiles/<name>/webui_state/sessions/*.json was reachable — it is not a direct child of the profile root, so the prior deny-subdir loop missed it. Add <root>/webui_state/<state-subdir> to the deny dirs for every Hermes root. Adds a regression assertion (profile webui_state/sessions/*.json → 403).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Scope
Backend slice only. The frontend/UI wiring is intentionally kept for a follow-up PR after this lands cleanly.
Verification
pytest tests/ -v --timeout=60pytest tests/ -v --timeout=60pytest tests/ -v --timeout=60Local Python 3.13 result:
Notes
This is an internal fork PR first so we can verify the GitHub Actions matrix before opening the upstream backend slice.