v0.50.25: mobile scroll, import timestamps, profile security, mic fallback (#404)
* fix: restore mobile chat scrolling and drawer close (#397) - static/style.css: add min-height:0 to .layout and .main (flex shrink chain fix for mobile scroll) - static/style.css: add -webkit-overflow-scrolling:touch, touch-action:pan-y, overscroll-behavior-y:contain to .messages - static/boot.js: call closeMobileSidebar() on new-conversation button onclick and Ctrl+K shortcut - tests/test_mobile_layout.py: 41 new lines covering all three CSS fixes and both JS call sites Original PR by @Jordan-SkyLF * fix: preserve imported session timestamps (#395) - api/models.py: add touch_updated_at: bool = True param to Session.save(); import_cli_session() accepts created_at/updated_at kwargs and saves with touch_updated_at=False - api/routes.py: extract created_at/updated_at from get_cli_sessions() metadata and forward to import_cli_session(); use touch_updated_at=False on post-import save - tests/test_gateway_sync.py: +53 lines — integration test verifying imported session keeps original timestamp and sorts correctly vs newer sessions; also fix: add WebUI session file cleanup in finally block Original PR by @Jordan-SkyLF * fix(profiles): block path traversal in profile switch and delete flows (#399) Master was vulnerable: switch_profile and delete_profile_api joined user-supplied profile names directly into filesystem paths with no validation. An attacker could send '../../etc/passwd' as a profile name to traverse outside the profiles directory. - api/profiles.py: add _resolve_named_profile_home(name) — validates name with ^[a-z0-9][a-z0-9_-]{0,63}$ regex then enforces path containment via candidate.resolve().relative_to(profiles_root); use in switch_profile() - api/profiles.py: add _validate_profile_name() call to delete_profile_api() entry - api/routes.py: add _validate_profile_name() call at HTTP handler level for both /api/profile/switch and /api/profile/delete (fail-fast at API boundary) - tests/test_profile_path_security.py: 3 tests — traversal rejected, valid name passes Cherry-picked commit aae7a30 from @Hinotoi-agent (PR was 62 commits behind master) * feat: add desktop microphone transcription fallback (#396) Mic button now works in browsers that support getUserMedia/MediaRecorder but lack SpeechRecognition (e.g. Firefox desktop, some Chromium builds). - static/boot.js: detect _canRecordAudio (navigator.mediaDevices + getUserMedia + MediaRecorder); keep mic button enabled when either SpeechRecognition or MediaRecorder is available; MediaRecorder fallback records audio, sends blob to /api/transcribe, inserts transcript into the composer; _stopMic() handles all three states (recognition, mediaRecorder, neither) - api/upload.py: add transcribe_audio() helper — saves uploaded blob to temp file, calls transcription_tools.transcribe_audio(), always cleans up temp file - api/routes.py: add /api/transcribe POST handler — CSRF protected, auth-gated, 20MB limit, returns {text:...} or {error:...} - api/helpers.py: change Permissions-Policy microphone=() to microphone=(self) (required to allow getUserMedia in the same origin) - tests/test_voice_transcribe_endpoint.py: 87 new lines — 3 tests with mocked transcription - tests/test_sprint19.py: +1 regression guard (microphone=(self) in Permissions-Policy) - tests/test_sprint20.py: 3 updated tests for new fallback-capability checks Original PR by @Jordan-SkyLF * docs: v0.50.25 release — version badge and CHANGELOG --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com>
This commit is contained in:
@@ -275,6 +275,64 @@ def test_gateway_session_messages_readable():
|
||||
post('/api/settings', {'show_cli_sessions': False})
|
||||
|
||||
|
||||
def test_importing_older_gateway_session_preserves_original_timestamps_and_order():
|
||||
"""Importing an older gateway session should not bump it above newer WebUI sessions."""
|
||||
conn = _ensure_state_db()
|
||||
older_started_at = time.time() - 1800
|
||||
imported_sid = 'gw_import_old_001'
|
||||
newer_webui_sid = None
|
||||
try:
|
||||
newer_webui, status = post('/api/session/new', {'model': 'openai/gpt-5'})
|
||||
assert status == 200, newer_webui
|
||||
newer_webui_sid = newer_webui['session']['session_id']
|
||||
|
||||
rename, rename_status = post(
|
||||
'/api/session/rename',
|
||||
{'session_id': newer_webui_sid, 'title': 'Newer WebUI Session'},
|
||||
)
|
||||
assert rename_status == 200, rename
|
||||
|
||||
_insert_gateway_session(
|
||||
conn,
|
||||
session_id=imported_sid,
|
||||
source='discord',
|
||||
title='Older imported gateway session',
|
||||
started_at=older_started_at,
|
||||
)
|
||||
post('/api/settings', {'show_cli_sessions': True})
|
||||
|
||||
imported, imported_status = post('/api/session/import_cli', {'session_id': imported_sid})
|
||||
assert imported_status == 200, imported
|
||||
imported_session = imported['session']
|
||||
assert abs(imported_session['created_at'] - older_started_at) < 2, imported_session
|
||||
assert abs(imported_session['updated_at'] - older_started_at) < 5, imported_session
|
||||
|
||||
sessions_payload, sessions_status = get('/api/sessions')
|
||||
assert sessions_status == 200, sessions_payload
|
||||
ordered_ids = [item['session_id'] for item in sessions_payload.get('sessions', [])]
|
||||
assert newer_webui_sid in ordered_ids, ordered_ids
|
||||
assert imported_sid in ordered_ids, ordered_ids
|
||||
assert ordered_ids.index(newer_webui_sid) < ordered_ids.index(imported_sid), ordered_ids
|
||||
finally:
|
||||
try:
|
||||
_remove_test_sessions(conn, imported_sid)
|
||||
conn.close()
|
||||
except Exception:
|
||||
pass
|
||||
if imported_sid:
|
||||
try:
|
||||
post('/api/session/delete', {'session_id': imported_sid})
|
||||
except Exception:
|
||||
pass
|
||||
if newer_webui_sid:
|
||||
try:
|
||||
post('/api/session/delete', {'session_id': newer_webui_sid})
|
||||
except Exception:
|
||||
pass
|
||||
post('/api/settings', {'show_cli_sessions': False})
|
||||
|
||||
|
||||
|
||||
def test_gateway_sse_stream_endpoint_exists():
|
||||
"""GET /api/sessions/gateway/stream returns a response (200 or 200-range)."""
|
||||
# The SSE endpoint requires show_cli_sessions to be enabled
|
||||
|
||||
Reference in New Issue
Block a user