11707 Commits

Author SHA1 Message Date
Brooklyn Nicholson
4bc20abbd0 feat(pets): wire all six animation states across every surface
The pet engine defined idle/run/review/wave/jump/failed, but the live
signals only ever drove a subset. Feed the dormant beats from signals
each surface already tracks, sharing one trigger via todos_all_done().

- CLI: review while reasoning, plus a transient end-of-turn flash —
  failed on a tool error, jump on a finished plan, else wave.
- TUI: petFlashStore flashes jump/wave on message.complete and failed
  on error; usePet honors the flash over derived state, auto-expiring.
- Desktop: wake the dead setPetActivity path — reasoning/tool/complete/
  error now flash via flashPetActivity (gated to the active session);
  the $petState computed drops stale tool/reasoning flags once at rest.
2026-06-15 14:00:24 -05:00
Brooklyn Nicholson
0586c8073e feat(pets): split /pets (list) from /pet (select)
/pets is the collection — installed pets with the active one marked, plus
/pets gallery to browse the petdex catalog. /pet is selection only: /pet
<slug> to bring one out, /pet off to put it away, bare /pet for status.

Both are cli_only — a sprite can't render in a Telegram/Slack message (which
is why /pet never had a gateway handler), so they stay on the CLI/TUI/desktop
surfaces and out of the messaging menus, keeping Slack under its 50-slash cap.
2026-06-15 12:50:26 -05:00
Brooklyn Nicholson
c176054c33 feat(pets): make display.pet.scale the single master size knob
One scalar now shrinks every surface together. The desktop canvas already
multiplied native pixels by scale; the CLI/TUI now derive their terminal
width from it via constants.resolve_cols() instead of a separate pinned
unicode_cols (which is now an optional override, default 0 = auto).

Two sizing bugs fixed along the way:
- kitty placement sized its c×r cell box from a native-aspect column count,
  so small pets got upscaled ~2× to fill it. It now derives the box from the
  scaled frame pixels (_cell_box, shared with the half-block frame() path),
  so kitty tracks scale like the GUI does.
- half-blocks can't follow scale all the way down — a cell samples the sprite
  at 1 horizontal + 2 vertical taps, so a tiny width turns the pet to mush.
  cols_for_scale() clamps to a legibility floor (UNICODE_MIN_COLS) and only
  grows above it, while kitty/GUI keep shrinking on true pixels.

Default scale lowered 0.7 -> 0.33 (glanceable corner sprite); the 0.7 literal
fallbacks in pets.py and the desktop sprite now reference the shared default.
2026-06-15 01:49:27 -05:00
Brooklyn Nicholson
fdcfa44584 feat(pets): crisp Kitty images in the TUI + reactive pet pane in the base CLI
The TUI now renders pets through Kitty's Unicode-placeholder protocol on
Kitty/Ghostty: the image transmits once per frame under a stable id and the
static placeholder grid (U+10EEEE + diacritics, image id in the fg color)
animates underneath without Ink ever repainting. Only Kitty is grid-safe in
Ink, so iTerm/Sixel/tmux/dashboard keep the half-block fallback.

The base CLI gains parity with the TUI's PetPane: a right-aligned half-block
sprite above the prompt, reactive to agent activity and animated by an
invalidate timer. Half-blocks only — raw image escapes can't survive
prompt_toolkit's patch_stdout output layer.

Also: right-align the pet in the TUI (justifyContent) and in `hermes pets
show` (graphics path), and render lone-opaque half-blocks fg-only so
transparent sprite edges stop painting black boxes.
2026-06-15 01:27:40 -05:00
Brooklyn Nicholson
2572617d5a fix(pets): live pet switching in the TUI, steady-redraw + right-align in CLI
Three rendering/state bugs surfaced while testing:

- TUI never reacted to a pet adopted/switched elsewhere (picker, /pet,
  hermes pets select). usePet stopped polling once enabled and keyed its
  frame cache by state only, so a new slug couldn't take over. Poll
  pet.cells steadily, treat its slug/enabled as source of truth, and key
  the cache by slug so a switch re-pulls the new sprite live.
- `hermes pets show` climbed up the screen each frame: the cursor-up count
  was rows+2 but only rows+1 lines were drawn. Move up exactly what we
  wrote so frames overwrite in place.
- Right-align the sprite against the terminal edge (CLI) and the pane
  (TUI width=100%), per request.
2026-06-15 00:37:24 -05:00
Brooklyn Nicholson
4db86e349c feat(pets): interactive /pet picker overlay in the TUI
/pet (and /pet list) opened the text-only slash worker, so the TUI just
printed a catalog you couldn't act on. The TUI already runs interactive
overlays (sessions, model, skills) — pets just never got one.

Add a PetPicker overlay (mirrors SkillsHub): it pulls pet.gallery, filters
as you type, ranks active→installed→curated, and adopts the highlight via
pet.select — the mascot lights up live on usePet's next poll. /pet <slug>,
/pet off, /pet status keep their text behaviour through the slash worker.
2026-06-15 00:30:06 -05:00
Brooklyn Nicholson
db00cbfd56 fix(pets): render half-blocks in the VS Code/Cursor terminal
detect_terminal_graphics() mapped TERM_PROGRAM=vscode to the iTerm2 inline
image protocol, but the integrated terminal doesn't render inline images
unless terminal.integrated.enableImages is on — so `hermes pets show`
emitted image escapes xterm.js silently drops, leaving just the label.
It also inherits ITERM_SESSION_ID/KITTY_WINDOW_ID when launched from those
terminals, which false-positived the same way.

Trust the authoritative TERM_PROGRAM=vscode and fall back to truecolor
half-blocks (always renderable in its grid); users who enabled images can
still pin display.pet.render_mode explicitly.
2026-06-15 00:20:34 -05:00
Brooklyn Nicholson
6681bef707 feat(pets): petdex animated mascots across CLI, TUI, and desktop
Adopt an animated petdex pet that reacts to agent activity (running on
tool calls, celebrating on success, sulking on errors) across all three
surfaces, driven by a shared Python pet engine so the base CLI and TUI
don't duplicate logic.

- agent/pet/: shared engine — manifest fetch, on-disk store, state
  mapping, and terminal-graphics/half-block encoding.
- hermes pets CLI (list/install/select/remove/off/doctor/show) +
  display.pet config block; petdex skill.
- TUI half-block PetPane via pet.cells RPC; desktop floating mascot with
  a canvas renderer + an Appearance opt-in picker (install/select/remove,
  lazy server-cropped thumbnails).
- gateway pet.* RPCs run on the worker pool so picker previews fetch
  concurrently instead of serializing on the reader thread.
2026-06-14 23:38:41 -05:00
leo4226
f795513782
fix(windows): kill hermes before recreating venv to release _bcrypt.pyd lock (#45120)
On Windows, native Python extensions such as _bcrypt.pyd are loaded as
DLLs by any running hermes process. When the installer tries to recreate
the venv (Remove-Item -Recurse -Force "venv"), Windows denies the delete
because the DLL is still mapped into the running process.

Add a taskkill /F /T /IM hermes.exe call before the Remove-Item so any
hermes process tree is stopped first, releasing the file lock. A short
sleep gives the OS time to unload the image before deletion proceeds.

This mirrors the existing force_kill_other_hermes() guard already present
in the --update flow (update.rs), applying the same pattern to the full
reinstall/repair path through install.ps1.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 02:27:00 +00:00
Alli
8fe334b056
fix(desktop): inset hover-reveal trigger past the adjacent scrollbar (#44159)
The collapsed-pane hover-reveal trigger strip (14px wide, 6px edge
gutter) overlapped the neighboring scroller's 8px .scrollbar-dt
scrollbar, which sits flush with the window edge when the rail panes
are collapsed. Hovering the scrollbar revealed the file browser over
it, and clicks on the overlapped band hit the trigger instead of the
scrollbar thumb.

Widen the edge gutter to calc(0.5rem + 2px) so the strip clears the
scrollbar (rem-coupled to the .scrollbar-dt width) while still
covering the OS window-resize grab area inset.

Part of #44140 (item 2).

Co-authored-by: AIalliAI <285906080+AIalliAI@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 21:23:38 -05:00
Teknium
40d7c264f0
fix(s6): register profile gateways without auto-starting (#46266)
* fix(s6): prevent profile create from auto-starting gateway service

When hermes profile create runs inside an s6 container,
_maybe_register_gateway_service() calls register_profile_gateway()
which creates the service directory and triggers s6-svscanctl -a.
Previously the service always started immediately, causing profiles
that share the main gateway's bot token (e.g. Kanban worker profiles)
to fail with a token-lock conflict and persist gateway_state: running
— becoming zombies that resurrect on every container restart.

Wire the existing start_now parameter through the S6 implementation:
when start_now=False, write a  marker file (same pattern as
container_boot.py _register_gateway_slot) so s6-supervise leaves the
service stopped until the user explicitly runs hermes -p <profile>
gateway start.

4 files, +61/-6, 4 new tests (all passing).

* test(docker): wait for gateway running state before restart

---------

Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
2026-06-15 11:43:23 +10:00
Teknium
4eb0ff639b
Remove is_container check when restarting over dashboard (#46290)
Co-authored-by: IAvecilla <ignacio.avecilla@lambdaclass.com>
2026-06-15 11:09:23 +10:00
Teknium
f3fe99863d
revert(web): remove keyless Parallel search fallback (#46350)
Remove the free Parallel Search MCP path and restore the keyed Parallel backend behavior from before it was introduced.

Also drops the keyless fallback registration/display labeling tests and returns the Parallel SDK pin to the prior version.
2026-06-14 16:47:57 -07:00
Teknium
a829e04d62
fix: migrate cloned profile configs (#46345) 2026-06-14 16:30:23 -07:00
Teknium
2a14e8957d
fix(kimi): surface K2.7 Code in native picker (#46309) 2026-06-14 14:01:03 -07:00
mr-r0b0t
bff78a34dc feat(zai): add GLM-5.2 with verified 1M context window
GLM-5.2 ships with a 1M (1,048,576) token context window. Without this
entry, Hermes falls through to the generic 'glm' key (202,752 tokens),
under-reporting the context bar and prematurely compressing conversations.

The 1M limit was verified empirically via needle-in-a-haystack retrieval
at 789,240 prompt tokens on api.z.ai/api/coding/paas/v4 — zero errors,
zero truncation, correct retrieval at every tested size (25K through 789K).

Changes:
- agent/model_metadata.py: add 'glm-5.2': 1_048_576 before 'glm' fallback
- hermes_cli/models.py: add glm-5.2 to zai curated models
- hermes_cli/setup.py: add glm-5.2 to setup wizard zai list
- hermes_cli/auth.py: add glm-5.2 to coding plan endpoint probes
- plugins/model-providers/zai/__init__.py: add glm-5.2 to fallback_models
- tests/agent/test_model_metadata.py: context resolution + vendor-prefix tests
2026-06-14 13:50:36 -07:00
Teknium
4e6d05c6a5
perf(skills): share raw config cache in skill utils (#46149) 2026-06-14 11:14:58 -07:00
Teknium
a1f51feb72
fix(telegram): avoid rich final duplicate previews (#46206) 2026-06-14 11:13:38 -07:00
kshitij
6c34088a17
Merge pull request #46237 from kshitijk4poor/salvage/46095-cross-process-cache
fix(gateway): cross-process agent-cache coherence (#45966) + preserve prompt caching
2026-06-14 23:05:17 +05:30
kshitij
fc2b8b3d31
Merge pull request #46236 from kshitijk4poor/salvage/disabled-skills-union
fix(skills): platform-disabled skills still appear in <available_skills> + unify all resolution sites (#46201)
2026-06-14 23:00:11 +05:30
kshitijk4poor
3bc4a2ff78 fix(gateway): re-baseline agent-cache message_count after each turn
The #45966 cross-process coherence guard snapshots a session's on-disk
message_count next to the cached agent and rebuilds the agent when the
count changes.  But the snapshot is taken at agent-BUILD time — before
the turn writes its own user + assistant (+ tool) rows — and the cache
entry is never rewritten on a reuse.  So this process's OWN turn grows
message_count, and the very next turn sees a mismatch and rebuilds the
agent.  That happens every turn, for every conversation, silently
destroying the per-conversation prompt caching the cache exists to
protect (AGENTS.md: prompt caching is sacred).

Add _refresh_agent_cache_message_count(): after a turn completes and the
agent has flushed its rows to the SessionDB, re-baseline the stored count
to the now-current value.  The guard then fires ONLY when a DIFFERENT
process changes the transcript — preserving the #45966 fix while keeping
the cache warm for normal single-process operation.

Tests drive the real SessionDB + the real guard condition: 5 consecutive
same-process turns now all REUSE the cached agent (0 before the fix); a
cross-process append still invalidates; and the re-baseline is fail-safe
(no DB, falsy session_id, raising probe, legacy 2-tuple, pending sentinel
all no-op).
2026-06-14 22:58:55 +05:30
kshitijk4poor
ce19fdb7ce fix(skills): apply global|platform disabled union to all resolution sites
The platform-disabled fix landed only in agent.skill_utils.get_disabled_skill_names
(the system-prompt path). Two sibling resolvers still used the old
replace-not-union semantics, so the same skill could be hidden from the
<available_skills> prompt yet reported enabled elsewhere:

- hermes_cli/skills_config.get_disabled_skills (the 'hermes skills config' UI)
  returned only the platform list, so a globally-disabled skill showed as
  enabled (unchecked) on any platform with a platform_disabled entry.
- tools/skills_tool._is_skill_disabled (gates whether skill_view loads a skill)
  ignored the global list when a platform list existed, so a globally-disabled
  skill could still be loaded on such a platform.

Both now union the global list with the platform list, matching
get_disabled_skill_names. An explicit empty platform list no longer re-enables
a globally-disabled skill — global disables hold on every platform (#46201).

Also: fix the now-stale get_disabled_skill_names docstring and drop a stray
blank line. Regression tests added for both sites (proven to fail on the old
replace semantics).
2026-06-14 22:54:54 +05:30
kyssta-exe
7f245b0035 fix(gateway): invalidate agent cache on cross-process session writes (#45966)
(cherry picked from commit 6d0f79defe8fdbfb9256b6d05c24f5bac42a5439)
2026-06-14 22:54:39 +05:30
ibrahim özsaraç
7bbe7024c2 fix: filter platform-disabled skills from <available_skills> prompt (#46201)
build_skills_system_prompt() already resolved _platform_hint but called
get_disabled_skill_names() with no argument, so the resolved platform never
reached the filter and the prompt cache_key varied by platform while the
disabled set did not. Pass _platform_hint or None.

get_disabled_skill_names() also fully ignored the global 'disabled' list once
a platform-specific list was found. Return the union (global | platform) so a
globally-disabled skill stays disabled on every platform.

Salvaged from #46203 by @iborazzi; the unrelated apps/shared/tsconfig.json
ES2023 bump is intentionally dropped (one concern per PR).
2026-06-14 22:52:57 +05:30
Teknium
7433d5f0eb fix(gateway): scope early duplicate guard to pid file 2026-06-14 08:42:06 -07:00
konsisumer
1436793051 fix(gateway): block shell gateway run when a service supervises the profile 2026-06-14 08:42:06 -07:00
brooklyn!
08d89e7aba
fix(desktop): limit thinking shimmer to the disclosure label (#46197)
Reasoning body text was inheriting tw-shimmer while streaming even though
the "Thinking" header already pulses — keep shimmer on the label only.
2026-06-14 10:14:58 -05:00
Teknium
2c174bce24 fix(gateway): preserve new input on interrupted replay cleanup 2026-06-14 05:10:39 -07:00
Arnaud L
5191c1c2ce fix(gateway): stop replaying interrupted tool-call tails and auto-continue notes
Three changes to prevent infinite re-execution loops when a user sends
a new message while long-running tools are executing:

1. Filter interrupted tool results in _build_gateway_agent_history:
   skip tool messages whose content contains [Command interrupted] or
   exit_code 130 — they represent partial execution, not valid results.

2. Don't replay auto-continue notes as user messages: detect
   gateway-injected [System note: ...] / [IMPORTANT: ...] prefixes
   and skip them in _build_gateway_agent_history so the LLM doesn't
   see 4+ messages from 'the user' telling it to finish old work.

3. Fix the wording: the system note now instructs the model to
   address the user's NEW message FIRST, IGNORE pending results,
   and NOT re-execute old tool calls.

Closes #45230
2026-06-14 05:10:39 -07:00
Teknium
0f3670ba79 chore(release): map Diyoncrz18 author email 2026-06-14 04:52:54 -07:00
Diyon18
288f7026e3 fix(messaging): correct Weixin personal account labeling 2026-06-14 04:52:54 -07:00
Teknium
efbe1635dd
fix(gateway): include replied-to media attachments (#46107) 2026-06-14 04:51:50 -07:00
Teknium
a27d7e68cc
fix(mcp): block suspicious stdio configs before probe (#46112) 2026-06-14 04:46:54 -07:00
Teknium
13a1bd0f83
perf(model-metadata): persist OpenRouter metadata cache (#46114) 2026-06-14 04:45:46 -07:00
Teknium
0e22bf6439
docs(gateway): document exact silence tokens (#46105) 2026-06-14 04:37:18 -07:00
Teknium
972a9885ee
fix(mcp): block exfil-shaped stdio server configs (#46083) 2026-06-14 04:24:14 -07:00
Teknium
9459057d7f
fix(telegram): guard rich details math crash (#46102) 2026-06-14 04:22:22 -07:00
Teknium
cf7d5932f8 fix(email): make IPv4 SMTP fallback use supported sockets 2026-06-14 04:16:26 -07:00
liuhao1024
04d4471d79 fix(email): use SMTP_SSL for port 465 and fall back to IPv4 on timeout
Port 465 expects implicit TLS (SMTP_SSL) from the first byte. The email
adapter always used SMTP() + starttls(), which is correct for port 587
but hangs/fails on port 465 providers (e.g., Swiss ISPs).

Additionally, when the SMTP host has AAAA DNS records but IPv6 is
unreachable, socket.create_connection() tries IPv6 first and hangs
until timeout. Add an IPv4 fallback via AF_INET socket.

Extract _connect_smtp() helper to consolidate the 4 duplicate SMTP
connection sites into a single method with correct protocol selection
and IPv6 fallback logic.
2026-06-14 04:16:26 -07:00
Teknium
5105c3651a
perf(api-server): normalize chat content linearly (#46079) 2026-06-14 03:25:49 -07:00
Aldo
293c04fef6 fix(gateway): suppress exact silence tokens without mutating history 2026-06-14 03:25:08 -07:00
Teknium
10bad2faf1
fix(gateway): serialize startup auto-resume before inbound (#46074)
Gateway startup now queues real inbound messages until restart-interrupted auto-resume turns have completed, preventing duplicate agents for the same session after a restart.
2026-06-14 03:21:06 -07:00
Teknium
2b4873f7fb
fix(agent): persist repaired-turn responses (#46071) 2026-06-14 03:20:25 -07:00
Teknium
723c2331bd fix: make profile subprocess HOME policy explicit 2026-06-14 03:20:21 -07:00
zccyman
b00060ce54 fix(agent): expose HERMES_REAL_HOME in subprocess envs for profile isolation
When profile isolation activates ({HERMES_HOME}/home/ exists), child
processes receive HOME={HERMES_HOME}/home/ for tool config isolation
(git, ssh, gh). However, scripts using Path.home() to locate
~/.hermes/ would incorrectly resolve to the isolated profile home,
breaking helpers that rely on the real user home directory.

New get_real_home() helper in hermes_constants resolves the actual
user home independently of profile isolation. All four subprocess
spawners now inject HERMES_REAL_HOME alongside the profile HOME:

- tools/code_execution_tool.py (execute_code)
- tools/environments/local.py (terminal background, run_env)
- agent/copilot_acp_client.py (Copilot ACP)

Child scripts can now use:
  Path(os.environ.get("HERMES_REAL_HOME", os.environ.get("HOME", "")))

to reliably find the real user home regardless of profile isolation.

Closes #25114
2026-06-14 03:20:21 -07:00
Teknium
0428945b5b
fix(desktop): keep profile homes out of bootstrap (#46073) 2026-06-14 03:08:52 -07:00
Teknium
afc8615509
perf(webhook): prune request caches incrementally (#46065) 2026-06-14 02:40:54 -07:00
LeonSGP43
89bdb1e546 fix: read dashboard spa assets as utf-8
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-14 02:31:04 -07:00
Teknium
7b9dc7cd0a test(gateway): align web profile wrapper expectation 2026-06-14 02:20:55 -07:00
helix4u
d76a58bd15 fix(gateway): resolve sudo profile system installs 2026-06-14 02:20:55 -07:00