Merge pull request #5663 from nesquena/release/stage-phase1-2026-07-06

Release: Phase-1 batch — table whitespace (#5644), empty error-hint guard (#5653), Playwright CI (#5661)
This commit is contained in:
nesquena-hermes authored and GitHub committed 2026-07-05 21:17:40 -07:00
commit 3403870ef9
6 files changed
+113 -3

No files matched your search

+16 -1
View File
@@ -103,7 +103,7 @@ jobs:
run: |
python -m pip install --upgrade pip
# Office parsers stay optional at runtime; CI installs them explicitly for the workspace preview tests.
pip install "pyyaml>=6.0" pytest pytest-timeout pytest-asyncio pytest-shard python-docx openpyxl python-pptx
pip install "pyyaml>=6.0" pytest pytest-timeout pytest-asyncio pytest-shard python-docx openpyxl python-pptx playwright
# ruff is installed so tests/test_ruff_forward_lint.py runs its E9/F821
# tree-clean assertions in-suite (mirrors how eslint is available for
# tests/test_static_js_runtime_lint.py). If install fails the test
@@ -117,5 +117,20 @@ jobs:
# importorskip and the matrix stays green.
pip install mcp || echo "mcp install failed — test_mcp_server.py will importorskip"
- name: Get Playwright version
id: pw-version
run: echo "version=$(pip show playwright | grep '^Version:' | cut -d' ' -f2)" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ steps.pw-version.outputs.version }}
restore-keys: |
${{ runner.os }}-playwright-
- name: Install Playwright browsers
run: python -m playwright install --with-deps chromium
- name: Run tests (shard ${{ matrix.shard }} of 5)
run: pytest tests/ -v --timeout=60 --shard-id=${{ matrix.shard }} --num-shards=5
+9
View File
@@ -19,6 +19,15 @@
- **Completed the remaining UI translations across all 8 non-English locales.** 664 strings that still fell back to English (marked `TODO` in the locale table) are now translated, so the German / Spanish / French / Japanese / Korean / Portuguese / Chinese / Russian interfaces are fully localized. No new selectable language was added — this fills in the languages already offered. Interpolation tokens are preserved per key. Thanks @mo7al876any. (#5535, #5530)
- **Recover from "context compression exhausted" with one click.** When a long conversation grows too large for automatic compression to make room, the terminal error now shows a **Start focused continuation** action. It opens a fresh linked session that keeps your workspace, model, profile, and toolset but starts with an empty model-facing transcript — so you can describe the next narrow task without replaying the oversized context. Bare "continue"/"go on" on an exhausted conversation is intercepted and pointed at the action (substantive prompts still go through), and repeated clicks or extra tabs converge on the same continuation instead of spawning duplicates. Thanks @franksong2702. (#5538, #4685)
### Fixed
- **Markdown tables render even when a row has trailing whitespace or a small leading indent.** A table whose header or separator row ended in a stray space (something LLMs emit routinely) previously fell through to raw literal pipes instead of a real `<table>`; the renderer's table matcher now tolerates optional trailing whitespace and up to a 3-space leading indent, matching CommonMark. Thanks @rodboev. (#5644, #5641)
- **No more empty italic hint under settlement error cards.** When a live turn ended in an error with no additional detail to show, the error card could render a dangling empty italic line; the hint block is now only emitted when there's actual hint text. Thanks @franksong2702. (#5653)
### Internal
- **Browser (Playwright) tests actually run in CI.** The pytest CI job now installs Playwright + Chromium (with a version-pinned cache), so the browser-backed test files that were silently skipping now execute — closing a real coverage gap. Thanks @rodboev. (#5661, #5658)
### Documentation
- **Documented the `HERMES_WEBUI_*` environment variables and refreshed stale version/test/locale references.** README, ARCHITECTURE, TESTING, ROADMAP, SPRINTS, and the `.env` example files now describe the supported runtime env vars (host/port/state-dir/agent-dir/home) and no longer carry stale hardcoded version/test-count/locale figures. Thanks @mo7al876any. (#5536)
+5 -1
View File
@@ -8675,9 +8675,13 @@ def _run_agent_streaming(
_snapshot_and_append_partial_on_error(s, stream_id)
except Exception:
logger.debug("Failed to snapshot partials on error for %s", stream_id, exc_info=True)
_error_content = (
f'**{_err_label}:** {_error_payload.get("message") or _err_label}'
+ (f'\n\n*{_err_hint}*' if _err_hint else '')
)
_error_message = {
'role': 'assistant',
'content': f'**{_err_label}:** {_error_payload.get("message") or _err_label}\n\n*{_err_hint}*',
'content': _error_content,
'timestamp': int(time.time()),
'_error': True,
}
+1 -1
View File
@@ -6391,7 +6391,7 @@ function renderMd(raw){
// Tables: | col | col | header row followed by | --- | --- | separator then data rows
// NOTE: table pass runs BEFORE outer link pass so [label](url) in table cells
// is handled by inlineMd() only — prevents double-linking.
s=s.replace(/((?:^\|.+\|\n?)+)/gm,block=>{
s=s.replace(/((?:^ {0,3}\|.+\|[ \t]*\n?)+)/gm,block=>{
const rows=block.trim().split('\n').filter(r=>r.trim());
if(rows.length<2)return block;
const isSep=r=>/^\|[\s|:-]+\|$/.test(r.trim());
@@ -455,6 +455,44 @@ def test_non_auth_silent_failure_still_uses_no_response(tmp_path, monkeypatch):
assert saved.messages[-1]["_error"] is True
def test_live_settlement_empty_hint_does_not_append_empty_emphasis(tmp_path, monkeypatch):
session = _prepare_session(
"empty_hint_failure",
"stream_empty_hint_failure",
pending_user_message="Please fail plainly",
)
class EmptyHintFailureAgent(MockAgent):
def run_conversation(self, **kwargs):
return {
"status": "failed",
"messages": list(kwargs.get("conversation_history") or []),
"error": "synthetic hard failure",
}
fake_queue = _run_stream(
monkeypatch,
session,
"stream_empty_hint_failure",
EmptyHintFailureAgent,
workspace=str(tmp_path),
)
saved = Session.load("empty_hint_failure")
assert saved is not None
events = _queue_events(fake_queue)
apperrors = [data for event, data in events if event == "apperror"]
assert apperrors, "expected apperror for generic terminal failure"
assert apperrors[-1]["type"] == "error"
assert apperrors[-1].get("hint") in (None, "")
error_content = saved.messages[-1]["content"]
assert saved.messages[-1]["_error"] is True
assert error_content == "**Error:** synthetic hard failure"
assert "\n\n**" not in error_content
assert not error_content.endswith("**")
def test_completed_assistant_answer_with_stale_partial_flag_settles_done(tmp_path, monkeypatch):
session = _prepare_session(
"completed_answer_stale_partial",
+44
View File
@@ -0,0 +1,44 @@
import importlib.util
from pathlib import Path
import pytest
_HELPERS_PATH = Path(__file__).with_name("test_renderer_js_behaviour.py")
_SPEC = importlib.util.spec_from_file_location("issue5641_renderer_helpers", _HELPERS_PATH)
_HELPERS = importlib.util.module_from_spec(_SPEC)
assert _SPEC.loader is not None
_SPEC.loader.exec_module(_HELPERS)
NODE = _HELPERS.NODE
_render = _HELPERS._render
driver_path = _HELPERS.driver_path
pytestmark = pytest.mark.skipif(NODE is None, reason="node not on PATH")
@pytest.mark.parametrize(
"src",
[
"| a | b | \n|---|---|\n| 1 | 2 |",
"| a | b |\n|---|---| \n| 1 | 2 |",
" | a | b |\n |---|---|\n | 1 | 2 |",
" | a | b |\n |---|---|\n | 1 | 2 |",
],
)
def test_table_rows_with_edge_whitespace_still_render_as_table(driver_path, src):
out = _render(driver_path, src)
assert "<table><thead>" in out
assert "<td>1</td>" in out
assert "<p>" not in out
def test_four_space_indented_table_like_block_stays_outside_table(driver_path):
src = (
" | a | b |\n"
" |---|---|\n"
" | 1 | 2 |"
)
out = _render(driver_path, src)
assert "<table" not in out