Two complementary cache-busting strategies for the stale cron skill picker:
1. On cron form open (toggleCronForm): always null _cronSkillsCache before
fetching, so freshly created skills are immediately visible without a
page reload. Previously the cache was only populated once and never
invalidated.
2. On skill save (submitSkillSave): null _cronSkillsCache after a successful
write so the next cron form open is forced to re-fetch. Mirrors the
existing _skillsData=null pattern one line above.
Fixes: #502
Co-authored-by: armorbreak001 <armorbreak001@users.noreply.github.com>
When a user has custom_providers configured in config.yaml, their custom
models should appear in the model picker even if active_provider is set
to a different provider (e.g. openrouter). Previously, the custom provider
was always discarded from detected_providers when active_provider != 'custom',
making custom models invisible.
Fix: only discard 'custom' if there are no custom_providers entries.
Co-authored-by: cloudyun888 <cloudyun888@users.noreply.github.com>
Co-authored-by: shruggr <shruggr@users.noreply.github.com>
Removes /root from _BLOCKED_SYSTEM_ROOTS in api/workspace.py, allowing
Hermes running as root (e.g. Docker, VPS) to use /root as a workspace
without a 'system directory' rejection.
Fixes a fragile string split in api/streaming.py: base_text extraction
now guards against msg_text that contains no '[Attached files:' marker,
preventing the split from producing empty-string on those messages.
Fixes: #510, partial fix from #521 (workspace + split guard only).
Co-authored-by: ccqqlo <ccqqlo@users.noreply.github.com>
- sessions.js _formatSourceTag(): return null for unrecognised tags
instead of raw string — prevents legacy 'N/A' values from surfacing
- sessions.js metaBits push: guarded with _stLabel null check so only
known platform labels appear in the session metadata line
- sessions.js [SYSTEM:] title fallback: drop raw s.source_tag middle
term, fall back directly to 'Gateway' for unknown sources
7 new tests in test_issue429.py.
1 updated test in test_sprint40_ui_polish.py (new guarded push pattern).
Closes#429
Bug: the autolink pass stashed <a> tags (via _al_stash) before running,
but did not stash <img> tags. When  was converted to an <img>
tag by the image pass, the subsequent autolink regex matched the URL
inside src="..." and wrapped it in <a href="...">url</a>, producing
src="<a href="...">url</a>" — a completely broken image source.
Fix: extend the _al_stash regex from:
(<a\b[^>]*>[\s\S]*?<\/a>)
to:
(<a\b[^>]*>[\s\S]*?<\/a>|<img\b[^>]*>)
This stashes both <a> and self-closing <img> tags before autolink runs,
then restores them after, so the URL inside src= is never touched.
Adds 7 regression tests in tests/test_issue487b.py.
1. ** inside was corrupted** — the outer bold/italic pass at line 480 ran
after the outer backtick→<code> pass at line 457, causing esc() to corrupt <code> tags
into <code> inside <strong>. Fix: add _ob_stash to protect <code> tags from
the outer bold/italic pass.
2. **Table cells with [label](url) produced double <a> tags** — the outer [label](url) pass
ran BEFORE the table regex, converting links to <a> tags in the raw table source.
Then inlineMd() processed those <a> tags again and autolink re-linked the URL inside
href="...". Fix: moved the outer link pass to AFTER the table pass so table cells
get their links from inlineMd() only, which has its own _link_stash protection.