Read agent.max_turns when constructing streaming WebUI AIAgent instances, pass it as max_iterations when supported, and include it in the per-session agent cache signature so budget changes take effect.
Add regression coverage for the config read, constructor kwarg, and cache key.
The dashboard banner 'Hermes agent is not responding' fires on every
multi-container deployment that doesn't set 'pid: "service:hermes-agent"'
in compose, because get_running_pid() relies on fcntl.flock and
os.kill(pid, 0) — both PID-namespace-scoped and invisible across container
boundaries.
Fix: when get_running_pid() returns None, fall back to a freshness check on
gateway_state.json. The gateway already writes that file on every tick with
gateway_state == 'running' and an aware ISO-8601 updated_at timestamp, so a
recent (<= 120s) timestamp is an equivalent live-process signal that needs
only a shared volume — no PID namespace, no compose workaround, no extra
HTTP probe URL.
Behavior preserved:
- In-namespace deployments still hit the PID-based path first; payload shape
unchanged (no 'reason' key) so #716 contract holds.
- Cross-container alive path adds reason='cross_container_freshness' so
support diagnostics can tell which signal succeeded.
- Stale updated_at, non-running gateway_state, malformed/naive/missing
timestamps, and timestamps far in the future all still report 'down' — the
fallback never produces a false positive.
- Same redaction rules: argv/command/executable/env/raw pid never leak.
Tests: 15 new cases in test_issue1879_cross_container_gateway_liveness.py
covering the cross-container alive path, every refusal case, clock-skew
tolerance, and backward compat with the #716 PID path. Existing #716
heartbeat tests (8) continue to pass.
Two bugs in get_available_models() conspired to duplicate the active
provider's auto-detected models under a phantom 'Custom' group whenever
custom_providers was also declared in config.yaml:
1. custom:* PIDs not in _named_custom_groups (e.g. stale slugs left from
prior configs) fell through to the auto_detected_models fallback, copying
the active provider's whole catalog into a phantom Custom: <slug> group.
Fix: continue unconditionally for ANY custom:* PID — the named-group
branch is the only legitimate population path.
2. The bare 'custom' PID, with the active provider being concrete (e.g.
ai-gateway), hit 'elif auto_detected_models: copy.deepcopy(...)' and
built a duplicate Custom group of the active provider's models with
mismatched provider prefixes. Fix: when pid == 'custom' and the active
provider is non-custom, leave models_for_group empty.
The reporter also suggested a third fix gating resolve_model_provider() on
config_provider — that's intentionally NOT applied because it conflicts with
the long-standing model-specific-override semantics covered by
test_model_resolver.py::test_custom_provider_*_routes_to_named_custom_provider
(custom_providers entries explicitly override the active provider's routing
when the user opted-in). The reporter's symptom (duplicate UI group) lives
entirely in get_available_models()'s group construction and is fully fixed
by the two changes above.
Tests: 6 new regression tests (3 in #1881 file + reuse), 774 broader
tests still green (model/provider/custom/config domain).
Use Path(__file__).resolve().parents[1] so the test survives being run
from a non-repo-root cwd, matching test_issue1112_csp_google_fonts.py.
Absorbed from PR #1852 follow-up commit 1448f42 by @ChaseFlorell.
Co-authored-by: Chase Florell <ChaseFlorell@users.noreply.github.com>
PR #1854 added a math stash to _renderUserFencedBlocks so backslash LaTeX
delimiters (\[..\], \(..\)) survive esc() and reach the KaTeX renderer in
user bubbles. The stash ran BEFORE the existing code-fence stash, so a
user-typed code block containing LaTeX-like syntax was extracted as
KaTeX and rendered as math inside <pre><code>:
```
\[ a + b \] is wrong
```
→ <pre><code><div class="katex-block"> a + b </div> is wrong</code></pre>
renderMd() (assistant path) handles this correctly by running fence_stash
before math_stash. The user-bubble path got the order inverted. Fix:
stash code fences first, then run the math regexes on the
outside-of-fence text only. Both top-level math and code-fenced literals
now render correctly:
- "math: \[ x + y \]" → KaTeX block
- "```\n\[ a + b \]\n```" → literal <pre><code>\[ a + b \]</code></pre>
Adds two regression tests:
- test_user_code_block_with_latex_syntax_renders_as_literal_code
(fails pre-fix, asserts no KaTeX wrappers inside <pre><code>)
- test_user_bubble_top_level_latex_still_renders_after_fence_reorder
(sibling guard against over-correcting and disabling math entirely)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>