Include the Hermes client name and version with Gemini inference, model and tier checks, and TTS requests. Add focused coverage for the request headers and keep the Gemini-specific context scoped to Google Gemini endpoints.
Salvage of #63862. is_output_cap_error() returns False for vLLM/LM Studio
error messages that contain 'prompt contains ... input tokens' (treated as
input-overflow signal). But parse_available_output_tokens_from_error() CAN
extract a valid available_tokens from those same messages. The
compression-disabled guard only checked is_output_cap_error(), so vLLM/LM
Studio users with compression off still got a terminal failure instead of
the max-tokens retry.
Fix: also exempt when parse_available_output_tokens_from_error() returns a
value — that function determines whether the retry path can actually handle
the error, so it's the right predicate for the exemption.
Added test: verify vLLM-format error with compression_disabled=False still
triggers the max-tokens retry path.
Co-authored-by: dmabry <dmabry@users.noreply.github.com>
Baseline bars were positioned at their absolute timeline offset, which
included the baseline run's own wait time — making duration comparisons
hard since the bars were visually offset. Now each baseline bar starts
at the same left position as its corresponding current job, so the two
bars directly overlap for at-a-glance duration comparison.
Also removed the now-unused bl_t0 / bl_max / bl_jobs_timed variables
that tracked the baseline timeline position.
Add wait time computation: for each job, wait_s = started_at - max(completed_at
of all jobs that finished before it started). This is a timestamp heuristic
(no workflow YAML dependency parse needed) that's accurate for pipeline-shaped
CI where the critical path is linear at each stage.
Shows up in:
- Job table: new Wait + Δ Wait columns
- Gantt chart: hatched bar segment before the run bar
- Step details: '(wait Xs)' annotation in the summary line
- Markdown summary: Total wait row with delta vs baseline
- Stats: total_wait / bl_total_wait in compute_stats
Also completes the skipped-jobs UI:
- Skipped stat card in stats cards
- Skipped row in markdown summary table
Backward compatible: old cached baselines without wait_s annotate on load.
Skipped jobs (conclusion == 'skipped') have null/zero-duration timestamps
that polluted every downstream computation: they counted as 'unchanged'
(0 vs 0) in faster/slower tallies, showed meaningless '0.0s (0%)' deltas
in the job table, rendered phantom gantt bars, and inflated wall/compute
totals.
Add is_skipped() helper and apply it consistently:
- compute_stats: exclude from wall/compute + faster/slower/unchanged;
add 'skipped' and 'bl_skipped' counts
- _gantt_bars: filter from current bars, baseline bars, and axis calc
- _job_table: show 'skipped' label instead of durations/deltas
- _step_details: skip entirely (no meaningful step data)
- _regressions: exclude from both current and baseline sides
Normalize Anthropic setup-token metadata for every PooledCredential construction path, persist corrected manual entries, heal legacy rows on load without copying global fallback credentials into profiles, and map the contributor email for release attribution.
Align the workspace status-bar dropdown with the rest of the status bar: drop
the per-item icons (they mixed lucide size-4 with a Codicon 1rem glyph and were
the only status-bar menu carrying item icons), leaving text-only items on the
shared DropdownMenuItem primitive with default typography. The status-bar
trigger keeps its FolderOpen glyph, consistent with sibling items.
Also map true@supersynergy.de → Supersynergy in AUTHOR_MAP.
scripts/desktop-sandbox.sh runs a Hermes desktop instance in an isolated
sandbox — separate HERMES_HOME, separate Electron userData, and a
distinct
app name (HERMES_DESKTOP_APP_NAME) so it doesn't compete with the main
desktop instance's single-instance lock.
Two modes:
- Ephemeral (default): temp dir, cleaned up on exit
- --persistent: stored under .hermes-sandbox/ in the worktree git root,
survives restarts for repeat testing
In the Nix devShell the script is available as 'sandbox'.
Also makes APP_NAME overridable via HERMES_DESKTOP_APP_NAME in main.ts —
app.setName() runs before requestSingleInstanceLock(), so the overridden
name changes the lock key. collectRelaunchEnv already preserves
HERMES_DESKTOP_* vars through self-update relaunches; test updated to
cover the new env var.
Follow-ups for salvaged PR #43819: the registry key was
str(Path(db_path).expanduser()) — a symlinked or relative path to the
same DB file got its own connection, silently reintroducing the exact
multi-writer contention the registry prevents. Key on Path.resolve()
(OSError-tolerant fallback). Adds a symlink regression test and the
AUTHOR_MAP entry for adambiggs.
Structural completion of the malformed-job freeze fixes (#61382 id-less,
#61525 non-dict schedule, #61581 bad next_run_at): wrap the per-job body
of _get_due_jobs_locked in try/except so any FUTURE malformed-field
variant degrades to skipping that one job for the tick instead of
aborting the scan before save_jobs() and freezing the whole profile's
scheduler.
Also: restore test_repeated_concurrent_runs_accumulate_completed_count
to TestMarkJobRunConcurrency (accidentally re-parented by the #61581
diff), add a containment regression test, and AUTHOR_MAP for hydracoco7.
E2E: one jobs.json carrying all five malformed shapes (drifted job_id,
missing id, null schedule, garbage next_run_at, non-string last_run_at)
plus a healthy sibling — single tick contains all five, sibling fires,
repairs persist, second tick stable. 670 cron tests green.
The salvaged fix sets HERMES_YOLO_MODE in main()'s dispatch path before
_prepare_agent_startup(); this follow-up also sets it inside
_prepare_agent_startup() itself so every launcher that triggers plugin/tool
discovery (incl. the Termux fast-CLI path) gets the same ordering guarantee
before tools.approval freezes _YOLO_MODE_FROZEN (#60328).
Structured review (2a/2b/2c) findings, all fixed:
- MAJOR: detect_local_server_type memo was process-lifetime with no
invalidation, permanently pinning a URL's server type. Now a bounded
1h TTL ((type, monotonic) tuples) so a backend swap on the same port
is re-detected. Test covers ollama->lm-studio swap after expiry.
- MAJOR: legacy disk-row compat was one-way. get_cached_context_length
and _invalidate_cached_context_length now consult the same key-shape
set {canonical, literal, canonical+slash} in both directions, so an
old slashed row is found (and cleared) when the runtime passes the
normalized URL. Tests pin both migration directions.
- MINOR: _localhost_to_ipv4 did whole-string replacement, which could
corrupt a proxy URL embedding http://localhost in its query. Now a
scheme-anchored host-only regex; localhost.example.com and embedded
substrings pass through. Tests added.
- MINOR: _invalidate_cached_context_length now also drops the
in-memory TTL probe rows for the pair, so a resolution inside the
TTL window can't re-persist the value just declared stale.
- Test gaps closed: detect-type cache hit + TTL-expiry re-detection,
ollama-show TTL expiry re-probe, reverse legacy-row lookups.
- Attribution gate: added zhchl@hermes-agent.local -> 8294 (PR #50572
author) to AUTHOR_MAP; the strict CI grep needs bare non-plus emails
literal in release.py.
Gates: ruff clean; targeted suites 202 passed / 0 failed; full
tests/agent 5426 passed with 17 failures identical on clean
upstream/main (pre-existing env-dependent anthropic/bedrock/credpool
tests); mypy delta vs base: 0 new errors; live smoke 6/6 PASS.
Every 5 minutes fetch_endpoint_model_metadata() re-runs the full server-type
waterfall (LM Studio -> Ollama -> llama.cpp -> vLLM), spraying 404s at
endpoints the server never exposes (e.g. /api/v1/models and /api/tags on a
vllm backend).
Add _endpoint_probe_path_cache (base_url -> server type) so the first
successful probe's result is reused for the lifetime of the process.
Subsequent refreshes skip straight to the known-good path.
Fixes#29971.
(cherry picked from commit f3d7a8960a)
- derive the compact_rows projection from SCHEMA_SQL (parse once, cache)
instead of a hardcoded column list: the original #47437 list was cut
against a June schema and silently dropped session_key/chat_id/chat_type/
thread_id/display_name/origin_json/expiry_finalized/git_branch/
git_repo_root/compression_failure_* — including desktop sidebar fields.
Schema-derived means declaratively reconciled new columns are included
automatically; only system_prompt is excluded.
- guard test pinning the schema<->projection contract (mutation-verified:
dropping a column from the projection fails it)
- wire compact_rows=(not full) into /api/sessions and /api/profiles/sessions
so the SQL projection pairs with the API-level field strip (?full=1 still
returns complete rows end-to-end)
- pass compact_rows at the remaining hot list callers: /api/status active
count, _session_latest_descendant fallback, /api/sessions/stats by-source
- thread compact_rows through the compression-tip projection
(_get_session_rich_row) so projected tips can't reintroduce the blob
- add pagination tests for get_messages (#60347 shipped none): paging order,
offset-past-end, active-flag interaction; add tip-projection compact test
- AUTHOR_MAP entries for mahdiwafy + CodeForgeNet (plain emails)
- run_route_script shells out with subprocess.run (up to 30s timeout); wrap
the call in asyncio.to_thread so a slow script can't stall every other
webhook and gateway task on the loop.
- scripts/release.py: map grace@weeb.onl -> evelynburger for the salvaged
contributor commit.