Gateway /resume rewrites sessions.source to the resuming platform, which is
correct routing behavior (source feeds find_latest_gateway_session_for_peer,
Telegram listings, and the TUI ghost pruner) but destroys creation
provenance. Add a separate immutable created_source column stamped once at
session creation and preserved by all upsert paths; surface it in
hermes sessions list as '<created>→<current>' when the two diverge.
Fixes#56439
showSessionSections gated the whole session area — search, sessions,
messaging platforms, cron jobs — on normal-session visibility. A profile
whose only sessions are messaging threads, or that only has scheduled
jobs, collapsed the sidebar to the 'No sessions yet' blank state, hiding
its Telegram/Slack conversations and jobs until a normal session was
created (the messaging half of #63593).
Messaging sessions and cron jobs are already profile-scoped upstream
(sidebarProfileForScope / filterSessionsByProfileScope), so the sections
rendered inside the area are the active profile's own — no state is shared
between profiles.
Fixes#63593
The uninstaller only removed the current launchd label's plist, so an
older io.nousresearch.hermes-agent.gateway* agent kept respawning the
gateway after the code tree was gone. Boot out (gui and user domains) and
unload every plist matching either label pattern before unlinking.
Also remove per-user leftovers earlier uninstalls left behind: macOS
Library Caches/Logs/WebKit/HTTPStorages/Saved Application State entries
named *hermes* and the XDG cache dir; the XDG data dir
(~/.local/share/hermes) is removed only on a full uninstall and is
explicitly reported as preserved otherwise. ~/.hermes stays intentional
in keep-data mode and is already reported.
Fixes#62209
Moves the pin to the 0.3.1 docs release and states the disclosure once: only the configured server receives data, and a completed turn sends a fixed marker.
Bumps the pinned sha to the v1.1.2 release and version to 1.1.2.
v1.1.2 fixes the codex-telegram-notify attribution/link and suppresses
duplicate lifecycle notifications for Telegram-origin sessions, while
preserving existing subagent/smart-approval filtering.
Plugins may only import @hermes/plugin-sdk (no-restricted-imports); the
hermes-bots group-approval path pulled the constant from @hermes/shared and
failed check:lint. Re-export it from the SDK and import it there; the
plugin-sdk test mock gains the export.
The client rejected its own approval.respond RPC after the generic request
timeout (120s shared default, 30s desktop) while the backend still waits the
full approvals.timeout (300s) for the user: answering later surfaced a false
"request timed out" over an approval the backend then applied anyway.
approval.respond now carries APPROVAL_RESPOND_TIMEOUT_MS (300s, the backend
default); ambientRequestFor forwards the optional deadline so session-routed
approval RPCs can raise their timeout; the hermes-bots group-approval path
rides the same deadline.
Fixes#60654
Windows has no POSIX parent-death supervisor/killpg safety net, so an
ungraceful exit of the hermes process left every stdio MCP child tree
(npx.cmd -> node.exe) running as orphans with ParentId=null, piling up
across session restarts.
- _run_stdio now attaches the process to a KILL_ON_JOB_CLOSE job object
before spawning stdio children (self-guarded no-op off Windows), so the
whole child tree dies with the parent at the kernel level.
- Windows reaps kill the process tree (direct child + descendants) in the
lifecycle orphan sweep and the spawn-ledger startup sweep, where there
is no pgid to group-kill.
Fixes#61059
Standalone MemoryProvider plugin for CortexLayer (github.com/Cortex-Layer/cortexlayer-hermes-plugin),
a linked-page long-term memory service with link-expansion retrieval. Platform mode only
(hosted api.cortexlayer.net, API-key authenticated) — implements the required lifecycle, all
5 memory tools (memory_add/retrieve/update/delete/relink), and the on_session_end /
on_pre_compress hooks (checkpoint API v2).
Owner submission (I maintain the plugin repo). Manually verified end-to-end against a real
Hermes instance before this submission (multi-turn conversation, confirmed sync_turn/prefetch
working with the profile's built-in memory disabled to remove ambiguity).
The boot-budget tests hardcoded the old 45s (and a 60s cushion) budget, so
raising BACKEND_BOOT_WAIT_TIMEOUT_MS to 180s made them time out or assert
before the deadline elapsed. Drive them from the constant so the next
budget change moves both together.
A cold backend boot (plugin discovery + route mounting at web_server
import time) takes 45-60s on slower hardware, so the 45s readiness
deadline made first-boot readiness a coin flip. Every lost race tore
down a healthy-but-slow backend and re-drove boot, cascading into
minutes of 'not connected' and orphaned python processes.
Raise both ends of the paired budget in lockstep: the main-process
readiness poll (DEFAULT_BACKEND_READY_TIMEOUT_MS) and the renderer
cold-boot wait that mirrors it (BACKEND_BOOT_WAIT_TIMEOUT_MS). The
poll returns the moment the backend responds, so fast machines see
no change.
Fixes#63454
Based on #63456 by @frohsinnllc
Co-authored-by: frohsinnllc <frohsinnllc@users.noreply.github.com>
Review follow-ups on #123008 (andrexibiza):
- P1: key the in-memory mirror by the resolved OAuth partition (the same
owner resolveOauthPartition picks for the jar) + origin, so two
same-origin sub-path gateways on separate jars can never see each
other's credentials through the explicit Cookie header. Cookies keep
their effective Path (RFC 6265 path-match on attach) and an expiring
Set-Cookie (Max-Age<=0 / past Expires / empty value) deletes the entry.
Tests: A→B→A stays separate, a B-only 401 clears only B, path scope,
deletion.
- P2: the silent re-login + single retry now runs only when
shouldReplayAfterCookie401 holds — the 401 body is the dashboard auth
gate's structured pre-handler refusal ({error: unauthenticated |
session_expired, reason}) AND the operation is idempotent (GET/HEAD) or
vouched replay-safe by the caller (replayOn401, set by the WS-ticket
mint). Application-level 401s and arbitrary mutations keep the
no-replay rule of requestWithOauthFallback.
- lint: perfectionist/sort-imports for the new module import.
The persist:hermes-remote-oauth partition family drops its hermes_session*
cookies in the field (Windows %3A profile folders, lazy hydration, flush
races), and electronNet's useSessionCookies intermittently omits the cookie
entirely → every authed REST call and WS-ticket mint 401s as no_cookie right
after a successful sign-in (#61457).
- New remote-session-cookies.ts: process-lifetime per-origin Set-Cookie
mirror (memory only, never persisted).
- The login window snapshots the fresh jar into the mirror on success; every
authed REST response records its Set-Cookie headers.
- fetchJsonViaOauthSession attaches the mirror as an explicit Cookie header
(an explicit header bypasses the network stack's jar lookup) unless the
caller supplied its own Cookie.
- On a 401 the mirror is dropped for the origin, one silent re-login is
forced, and the request is retried once (safe: a 401 means the server
rejected the request before executing it).
Fixes#61457
The Windows update/uninstall hand-off aborted with 'venv shim still
locked' whenever an autostart Hermes process held the venv: the gateway
Startup item and the dashboard Scheduled Task are launched outside the
desktop app, so releaseBackendLock() never saw them and every hand-off
failed after the 15s gate.
- new isExternalVenvHolder() selector in venv-holder-select.ts: exe must
live under <venv>\Scripts\ AND be unambiguously a Hermes program
(hermes.exe shim, python -m hermes_cli, python -m hermes) — far
narrower than the install-root substring matching that sank #62445,
so unrelated processes mentioning the install root or borrowing the
venv interpreter are never tree-killed
- releaseBackendLock() kills those holders before the release gate and
re-scans inside every gate pass, so a respawning autostart holder
loses the race; the shim-lock probe remains the backstop abort for
non-Hermes holders
- the Win32_Process scan is shared with the hindsight-daemon sweep
Based on #62445 by @LeonSGP43 (approach credited; matching narrowed).
Fixes#62311
Drag-to-float gestures now default off on a fresh install. A composer that was already floating before the flip keeps its gestures, and a stored choice wins either way.
Co-authored-by: networthexplained <300128320+networthexplained@users.noreply.github.com>
A volume that already owns /workspace skipped the configured working
directory, so tools treated that host path as unmounted. Bind it at a
second mount, or point tools at the volume that already has it, for any
drive path.
Six fixes for the wave-7 picker/input cluster:
DeepSeek -> "Deepseek") and left the gemini- branch's words lowercase
("Gemini 2.5 pro"). Vendor casing + parameter counts now applied after
title-case (GLM, DeepSeek, MiniMax, OpenAI, ERNIE, MiMo, BGE, VL, IT,
FP8, AI; 8b -> 8B, a3b -> A3B), and the gemini branch title-cases like
every other branch.
and was unreachable by keyboard (rows are highlighted, never DOM-focused,
so Radix's own ArrowRight never fires). The chevron is now visible on
every model row and ArrowRight (caret parked at query end) hands focus
to the highlighted trigger and opens its sub; ArrowLeft returns focus
to the search field. Consolidates #86968 + #104532.
-fast/-thinking/-preview ids to the base label. The tag now rides the
display name on every surface, and formatModelPillLabel no longer
doubles Fast for a -fast variant id.
all MoA presets were disabled: manual picks are sticky by design
(d595e636c8), but the virtual moa provider's catalog row disappears
entirely once no preset is enabled, so that one absence is
authoritative (moaPickRemoved) and the pick reseeds from the profile
default. Narrow moa-only exception — no general catalog diff.
token (nimb -> nimb*); none of the CJK routes can honour it (bigram and
trigram routes quote tokens so the star matches literally; LIKE has no
star wildcard at all), so CJK searches returned zero results. The star
is now stripped per token on the CJK path only.
measure() effect deps (stale measurements after toggling Inbox style)
and the card estimate undershot the four-line/wrapped-title worst case
(74px), painting rows over their neighbours on cold start. Card
estimate raised to the worst-case-covering 96px and the deps fixed.
Linux uses apple-touch-icon.png as the window icon and Nix requires it to
match the full-bleed launcher icon, so keep it full-bleed and point the
dev-only app.dock.setIcon at assets/icon-mac.png instead.
Dev runs replace the Dock icon with public/apple-touch-icon.png, which the
generator rendered full-bleed, so it drew ~24% larger than its Dock
neighbors. Render it from the mac-grid master like the icns targets.
On the 824 grid the plate matches peers, but the girl inside a white tile
with a ring read small; scale her 1.12x about the plate center for every
mac target.
A pending clarify card waits on its gateway clarify.request. When that
frame is lost the card sat as a disabled preview until the 300s timeout
with no hint of what went wrong.
After a 4s grace the card now asks the owner socket for
session.events.since, which re-delivers the session's open requests, so a
request the backend still holds parks and the card goes live. If nothing
turns up, single and batch cards show an inline notice (all locales)
pointing at Stop, and drop the dead Skip/Continue actions.
Since the questions[]-only schema (#95907) every single question is a
one-entry batch on both the tool args and the gateway wire, yet no test
exercised that shape (called out in #98645). Lock the behavior down at
both layers:
- unit: a one-entry batch renders the batch card (not a blank/spinner
single card) both with the wire already parked and when the request
lands after the tool row, and answers with the qid-keyed lock
- e2e: a SINGLE_BATCH trigger drives the real chain (composer -> gateway
-> agent -> clarify tool -> clarify.request -> renderer) through mount,
pick, confirm, and settle for questions.length === 1
The e2e mock's trigger routing also learns to scope its has-tool-result
guard to the answering turn's own question text: the existing any-tool-
result check would false-positive once a second scripted clarify shares
the conversation history.
Adapted to main: the mock server now lives in tests-js/scripts, and a batch
confirm answers with clarify.lock.
Electron booted from active-profile.json and ignored argv. hermes
desktop and hermes -p <name> desktop never appended --profile, so
the packaged app kept the stored profile.
Parse both --profile spellings before startHermes, persist that
name, and append the same flag on the packaged launch. A missing
flag does not change the stored profile.