* feat(core): add registerHermesSessionOpenHandler + renderTranscript extension hooks
- registerHermesSessionOpenHandler(fn): register a handler that fires on
session open. Return {cancel:true} to prevent navigation.
- renderTranscript(container, messages, opts): render messages into any
DOM container using core's renderMd pipeline. Skip tool messages.
- Wire _hermesNotifySessionOpen into loadSession: pre-load guard at top,
post-load notification at end for extensions to hook into.
- Follows existing registerHermesTtsEngine extension registration pattern.
This gives extensions like chat-tiling a sanctioned API instead of DOM
hacking to intercept and render session transcripts.
* fix: address 3 gate-fail blockers from PR #5508 review
Fixes the three core issues identified by nesquena-hermes code review:
1. XSS sink in renderTranscript (boot.js)
- Fallback to textContent when window.renderMd is unavailable
- innerHTML exclusively for successful renderMd output
2. Stale _loadingSessionId reset in cancel branch (sessions.js)
- Remove premature nulling of _loadingSessionId
- Cancel path simply returns without touching loading-guard state
3. Pre-open veto bypasses profile/import side-effects (sessions.js)
- Move cancellable preload hook to start of _openSidebarSession,
before external session import and profile switching
- Pass internal _preloadNotified flag to skip duplicate preload
in loadSession while retaining post-load notification
Closes#5508 gate-fail items.
* fix: use module-level flag instead of call argument to avoid test regression
The _preloadNotified approach broke test_static_sessions_js_switches_profile
before_opening_all_profiles_row because it changed the loadSession call
signature from loadSession(sid, loadOpts) to loadSession(sid, Object.assign(...)).
Switch to a module-scoped boolean _hermesSessionOpenAlreadyFired set by
_openSidebarSession before calling loadSession, checked by loadSession's
pre-hook guard. The call signature stays unchanged.
Test 2 (test_load_session_rearms_stream_on_every_early_return) also passes.
* fix: compact pre-hook comment to keep loadSession within test window limits
* fix: replace global _hermesSessionOpenAlreadyFired flag with per-call opts._preloadNotified
The module-level boolean introduced in 381fa0ef had two problems:
1. ReferenceError on direct loadSession() calls — the flag was undeclared
when called outside _openSidebarSession, breaking saved-session restore.
2. Never reset after first sidebar open — all subsequent direct calls
silently skipped the cancellable preload handler.
Per the maintainer's review (PR #5508), replace the global with a per-call
option _preloadNotified passed by _openSidebarSession via Object.assign.
This keeps the call signature stable for existing tests and eliminates
the stale global state leak.
Test adjustments:
- test_issue1611: widened loadSession call literal assertion
- test_session_channel_option_x: body slice 14000→15000 to accommodate
the slightly longer function body
* chore: remove unrelated files from commit
* fix: address 3 review issues — preload-only cancel, drop inner wrapper, pass _preloadNotified on retry
- Only honor {cancel:true} when opts.preload===true (boot.js)
- Drop .msg-body-inner wrapper, render directly into .msg-body (boot.js)
- Carry _preloadNotified:true through cross-profile 409 retry (sessions.js)
* recommit
* Update static/boot.js
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix(extensions): resolve canonical sid before preload hook + pass _preloadNotified on continuation retry
PR #5508 review follow-up (review 4690636760):
1. Move _resolveSessionIdFromSidebarLineage() before the preload hook so
extensions always see the canonical sid, not the raw sidebar click id.
2. Pass _preloadNotified:true on the continuation-session retry path to
prevent duplicate preload events to extensions.
3. Add functional test_extension_session_hooks.py — actually drives the
new hook registration, preload-veto, transcript rendering, and
_preloadNotified bridge in Node (13 tests, all green).
* Remove unused pytest import
Removed unused import of pytest from test file.
* fix: address 2 gate-blocking veto-ordering defects (PR #5508)
Blocker 1 (CORE): cross-profile retry now passes _preloadNotified:true
so the pre-hook doesn't re-fire after destructive side-effects already
ran (stream teardown, message clear, profile switch). A {cancel:true}
on that second fire was stranding the UI profile-switched with a
cleared transcript.
Blocker 2 (SILENT): closeMobileSidebar() was called synchronously
BEFORE _openSidebarSession()'s veto guard in three places (tap-to-open,
child-session, lineage-segment). A {cancel:true} still closed the
sidebar out from under it. Removed the three premature calls; moved
a single closeMobileSidebar() inside _openSidebarSession AFTER the
veto guard so it only runs when the open actually proceeds.
Added 3 regression tests asserting {cancel:true} leaves NO side-effect.
* CHANGELOG: extension session-open handler + renderTranscript API (#5508)
---------
Co-authored-by: Sean <seanos1a@gmail.com>
Co-authored-by: ChonSong <85378550+ChonSong@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>