Commit Graph

1737 Commits

Author SHA1 Message Date
Teknium bacb90fe20 feat(delegation): honor delegation.request_overrides on all three resolution branches with explicit-over-runtime merge precedence
Completes the #90953 salvage on post-#98237 main:

- New _merge_request_overrides helper defines the precedence contract:
  explicit delegation.request_overrides merges OVER runtime/parent-derived
  overrides — explicit top-level keys win; extra_body is deep-merged one
  level so runtime extra_body keys survive unless redefined. Inputs are
  copy.deepcopy'd so transport-side mutation can't leak into config or the
  provider runtime cache.
- Direct base_url branch: explicit key now merges over the #98237
  provider-alongside-base_url runtime overrides instead of being a separate
  return shape; max_output_tokens preserved.
- Named-provider branch and parent-inherit branch now honor the key too, so
  delegation.request_overrides never silently no-ops.
- _build_child_agent honors override_request_overrides whenever set
  (previously only when override_provider was set), enabling the inherit
  branch's merged value to reach the child.
- DEFAULT_CONFIG: delegation.request_overrides entry with comment.
- Tests: expanded tests/tools/test_delegate_request_overrides.py — deep-copy
  proofs, explicit-over-runtime precedence on the provider-alongside-base_url
  path, named-provider branch, inherit branch, and merge-helper unit tests.
- Docs: configuration.md delegation section + features/delegation.md document
  the key, precedence, and example YAML (OpenRouter extra_body.provider.sort).
2026-08-29 19:13:23 -07:00
Teknium 556777ddb1 docs(cron): note request settings carry into scheduled runs 2026-08-29 19:12:51 -07:00
Teknium 86a2fdc634 feat(tui): status rule shows cache-hit %, latency, t/s and honors display.status_bar.fields
Extends PR #98250's classic-CLI status-bar upgrades to the Ink TUI:
- tui_gateway/server.py _get_usage() now emits cache_hit_pct,
  avg_latency_s, avg_tps (reads the same per-call deque history from
  agent/conversation_loop.py; keys omitted when no data — Codex
  app-server has no latency, zero cache reads show no %)
- StatusRule renders the three read-outs as width-budgeted tail
  segments (breakpoints 96/104/110 cols, lowest priority — they shed
  first on narrow terminals)
- display.status_bar.fields (the SAME key the classic CLI honors)
  filters TUI segments too: cache_hit, latency, tps, duration,
  compressions, bg_tasks, bg_subagents, voice, battery, title,
  context_pct, context_detail
- values ride the existing usage payload/ticker; constants between
  events so the usage==last dedup keeps suppressing repaints
- 3 new server tests, 5 new TUI tests; full ui-tui suite 1727 green
2026-08-29 19:12:24 -07:00
Teknium 6cb6aeb168 feat(desktop): real-profile browsing toggle in Capabilities → Tools → Browser
Users reported no GUI switch for browser.use_real_profile — the only
desktop home was the generic Settings → Config editor, which nobody
found. The Browser toolset detail pane now renders a 'Use My Real
Browser Profile' ToggleRow above the backend/provider matrix.

- new BrowserRealProfilePanel: reads the shared profile-scoped config
  record cache, optimistic write-through, rollback on failure
- saveHermesConfigRecord: capability-scoped PUT /api/config counterpart
  of getHermesConfigRecord, so the Capabilities scope selector writes
  the profile it points at (possibly another gateway)
- i18n: en/ja/zh/zh-hant keys (ar inherits en via defineLocale)
- docs: browser.md desktop pointer corrected to the real location

Live E2E on the built app over CDP: clicking the switch flipped
browser.use_real_profile true→false→true in the sandbox HERMES_HOME
config.yaml, GET reflected it, no layout glitches (screenshots in PR).
2026-08-29 19:10:12 -07:00
Brin Shadewater c8bbde7770 feat: allow configured background review tools
Profiles can now grant narrowly scoped tools to the background review runtime whitelist while unrelated tools remain denied. Document the configuration and cover it with a real-config regression test.

Agent: codex
2026-08-29 19:10:06 -07:00
Teknium f8546c2eac fix(browser): real-profile follow-ups — reap launched Chrome, headless display-less Linux, register real_profile_pin default + docs
- _terminate_real_profile_chrome(): directly-launched real browsers are ours
  to reap (agent-browser only attaches); wired into the atexit emergency
  cleanup and both launch-failure paths so orphaned Chrome processes can't
  accumulate.
- Display-less Linux gate: append --headless=new (shares the profile's normal
  cookie store, unlike legacy headless) so the direct-launch path doesn't
  regress servers without DISPLAY/WAYLAND_DISPLAY.
- Register browser.real_profile_pin in config_defaults.py and document the
  new launch model + pin in website/docs/user-guide/features/browser.md.
- Drop unused tempfile import from the cherry-picked commit.
2026-08-29 18:35:12 -07:00
Teknium 9e017428ba fix: unify status-bar field keys, docs, and tests for salvaged cluster
Follow-up to the cherry-picked #41909/#92696 + #39760 + #97970 cluster:
- single field-key namespace (display.status_bar.fields) instead of the
  second tui_statusbar_fields list; cache_hit/latency/tps/stash/battery/
  title join the existing key set
- cache-hit % prefers the baseline-delta regime (resets on model switch
  and compression) and hides on zero cache reads instead of alarming 0%
- latency/tps segments added to the styled fragment renderer too
- docs updated in website/docs/user-guide/configuration.md
- 7 new tests: rolling latency/t/s, NaN/negative guard, field filtering,
  baseline resets, title badge gating
2026-08-29 18:34:51 -07:00
liuhao1024 fb786d2f5b feat(cli): add display.status_bar.fields config for customizing status bar
Allow users to control which fields appear in the interactive CLI status
bar via display.status_bar.fields in config.yaml.

Available fields: model, context_pct, context_detail, compressions,
bg_tasks, bg_processes, duration, prompt_elapsed, yolo, total_tokens.

When the list is empty (default), all fields are shown as before.
The field order is fixed (model always first); the config controls
visibility only. Narrow terminals (<76 cols) automatically drop
context_detail regardless of config.

total_tokens is opt-in only (not shown by default) to avoid width
overflow in the prompt_toolkit fragment renderer.

Closes #41909
2026-08-29 18:34:51 -07:00
Teknium 22420eb0d6 docs: delegation.provider alongside base_url carries request overrides (#65035) 2026-08-29 18:16:04 -07:00
Teknium b6d535dd88 feat(browser): Brave Origin works for real-profile browsing and default-browser detection
Extends the real-profile machinery (PR #95620) to Brave Origin — Brave's
standalone paid build with a fully separate install identity:

- new canonical key 'brave-origin' in _CHROMIUM_BROWSERS
- Windows: BraveOHTML ProgId -> brave-origin; channel ProgIds BraveOBHTML/
  BraveODHTML/BraveOSHTM fail closed (identifiers from brave-core
  install_static)
- macOS: com.brave.Browser.origin bundle id (exact match); .beta/.dev/
  .nightly channel bundles fail closed; /Applications/Brave Origin.app
- Linux: brave-origin.desktop matched BEFORE the bare 'brave' fragment
  (substring scan would otherwise resolve an Origin default to stable
  Brave and drive the wrong profile — #95549 wrong-principal invariant);
  brave-origin-{beta,nightly,dev} fail closed
- profile dirs: BraveSoftware/Brave-Origin on all three OSes (per
  brave-core kProductPathName + Homebrew cask zap paths)
- /browser connect launch tables: Brave Origin split into its OWN group
  so a 'brave' executable lookup can never resolve to the Origin binary
- user-facing strings/docs/desktop tooltip updated

Tests: progid/bundle/desktop map params + data-dir resolution for all
three OSes; 125 passed in the three browser test files.
2026-08-29 18:13:33 -07:00
H-Ali13381 bca0a865c8 fix: detect Brave Origin browsers for CDP connect 2026-08-29 18:13:33 -07:00
nftpoetrist 0610291b5a fix(prompt): sync DEFAULT_SOUL_MD with the #95681 identity rewrite
DEFAULT_AGENT_IDENTITY was rewritten in agent/prompt_builder.py (behavior
spec, exploration-thrift line deliberately removed) but the actual seed
written to disk on first run, hermes_cli/default_soul.py's
DEFAULT_SOUL_MD, was never updated. ensure_hermes_home() writes
DEFAULT_SOUL_MD into SOUL.md on every fresh install before the agent's
first turn, so virtually all real users end up as "SOUL.md users" seeded
with the pre-rewrite text -- including the exact "targeted and efficient
exploration" line the rewrite explicitly banned -- while the new
DEFAULT_AGENT_IDENTITY fallback essentially never serves the "fresh
install" audience its own PR body named as the target.

- DEFAULT_SOUL_MD now matches DEFAULT_AGENT_IDENTITY exactly.
- The pre-rewrite text is added to _LEGACY_TEMPLATE_SOULS so installs
  already seeded with it self-heal via the existing upgrade-in-place
  mechanism (same guarantee as the comment-only scaffold entries: the
  string carries zero user intent, so it's safe to replace).
- Synced the other places install.sh's own comment says "MUST match
  DEFAULT_SOUL_MD": scripts/install.sh, scripts/install.ps1,
  docker/SOUL.md, and the docs/i18n pages that quote the fallback text
  verbatim.
2026-08-29 18:10:47 -07:00
Teknium e05c91ac71 fix(cli): slow /handoff transfers no longer misreported as "gateway not running"
Live-reproduced on main: /handoff poll-waited a flat 60s for a TERMINAL
state, but the gateway's dispatch is a full synthetic agent turn (whole
transcript replay + delivery) that routinely exceeds 60s on long sessions.
The CLI then printed "Timed out waiting for the gateway. Is `hermes
gateway` running?" (false diagnosis), called fail_handoff() on the RUNNING
row (stomping the gateway's claim), and promised "Your CLI session is
intact" after switch_session had already re-pointed the session. The
watcher later overwrote failed -> completed: split-brain.

- hermes_state.fail_handoff gains only_states CAS; waiters can only fail
  rows still pending. Owner (gateway watcher) keeps the unconditional form.
- CLI wait loop is two-phase: 60s for the CLAIM (pending) — a timeout
  there really does mean no gateway — then up to 15 min for the claimed
  dispatch with 30s heartbeats; a running row is never failed by the CLI.
- Desktop handoff.fail RPC now CAS-fails pending rows only; a running row
  returns {failed: false, state: running} instead of stomping the claim.

Repro (real _handoff_watcher, real state.db, CLI as separate process,
75s dispatch): before — CLI timeout @60s + false message + row stomped;
after — pending->running@5s->completed@80s, clean CLI exit.
2026-08-29 18:08:55 -07:00
Teknium 1a47a36422 feat(loop): first wakeup fires immediately by default
Flip the salvaged --start-now behavior (PR #97958) into the unconditional
default: /loop's first iteration is due the moment the loop is set, then
recurs on the normal cadence. The flag is dropped — it was never released,
so there is nothing to deprecate.

- LoopManager.set(): next_due_at = now for both cadence modes
- drop --start-now parsing, the persisted LoopState.start_now field, and
  the flag from help text; confirmation now always says the first wakeup
  fires now
- tests updated to pin the new default (incl. the TUI not-due test, which
  now has to push next_due_at out explicitly)
- docs: quick-start and command table describe the immediate first run
2026-08-29 17:43:38 -07:00
oxngon 796babaaed feat(loop): add --start-now to fire the first wakeup immediately
/loop [interval] <prompt> currently schedules the first wakeup one full
interval after the command runs (next_due_at = now + interval). When the
user just told Hermes what to check, waiting the whole interval before
any output feels like the command was ignored.

Add an opt-in --start-now flag that keeps Claude Code parity as the
default but lets the user run the first iteration immediately, then
continue on the cadence:

  /loop 1h check the deploy status            # first run in 1h (unchanged)
  /loop 1h --start-now check the deploy       # first run now, then hourly

- parse_loop_args(): parse and strip --start-now (leading or trailing)
- LoopState: new persisted start_now field (default False, survives
  serialization round-trip and old rows missing the field)
- LoopManager.set(): next_due_at = now when start_now, for both fixed
  interval and self-paced modes
- dispatch_loop_command(): wire start_now through, update help text, and
  report "First wakeup fires now" in the confirmation
- website/docs: document the flag in the /loop guide
- tests: parse (trailing/leading/absent/self-paced/combo/prompt-word),
  tick lifecycle (due immediately vs after interval), serde round-trip,
  and dispatch-level confirmation
2026-08-29 17:43:38 -07:00
kshitijk4poor 360761c8cf docs: surface Tencent TokenPlan + hy4-preview across provider docs
Follow-up for salvaged PR #96939 (docs completeness audit): setup
table, quick commands (TokenHub example refreshed to hy4-preview),
fallback tables, env-var reference, quickstart, --provider choices,
and .env.example (TokenHub block was missing there too).
2026-08-29 20:51:17 +05:30
kshitijk4poor a8e11d21c0 docs: surface Nebius Token Factory across user-facing provider docs
Follow-up for salvaged PR #28253 (docs completeness audit): setup
table, quick command, fallback tables, env-var reference, quickstart,
--provider choices, and .env.example.
2026-08-29 20:39:44 +05:30
kshitijk4poor fbef1ca805 docs: surface Ramp Router across user-facing provider docs
Follow-up for salvaged PR #93548 (docs completeness audit):
- integrations/providers.md: setup table row, quick command example,
  fallback supported-providers list
- reference/environment-variables.md: RAMP_ROUTER_API_KEY / _BASE_URL
- reference/cli-commands.md: --provider choices
- getting-started/quickstart.md: provider table
- user-guide/features/fallback-providers.md: fallback table
- .env.example: commented key block
2026-08-29 20:29:04 +05:30
Teknium 74a95a3ddf feat: /btw now answers side questions with conversation context; /background renamed to /bg
/bg (formerly /background, which is retired) keeps the existing semantics:
spawn a fresh, independent agent session in the background.

/btw is now its own command matching the convention other harnesses use:
ask a quick side question ABOUT the current conversation without
interrupting it. A one-shot auxiliary LLM call (main model by default,
overridable via auxiliary.side_question.* in config.yaml) answers from a
read-only transcript snapshot — the live session's history, role
alternation, and prompt cache are untouched, and the current turn keeps
running.

Surfaces wired: CLI (inline mid-run dispatch), gateway (all messengers,
busy-dispatch table + idle dispatch, i18n across all 17 locales), TUI
(prompt.btw RPC + btw.complete event), Discord native slash, relay
command manifest, desktop exec routing, docs (EN + zh-Hans).
2026-08-29 07:25:17 -07:00
Teknium 2c232761e4 fix(skills): restore grill-me name, keep frontier-rounds upgrade
Reverts the plan-interrogation rename from #97831 per maintainer decision —
the skill keeps its original grill-me name. The content upgrade (design-tree
frontier-rounds interview mechanic from mattpocock/skills' grilling) stays.
Docs page, catalog row, and sidebar entry renamed back.
2026-08-29 06:30:12 -07:00
Teknium b1ff8722a5 feat(optional-skills): add decision-questionnaire — turn a blocked decision into an async questionnaire
Ports the MIT-licensed 'to-questionnaire' skill from mattpocock/skills as
decision-questionnaire (optional-skills/productivity). Interviews the user
about the send only (recipient + needed outcomes), then drafts a
most-important-first discovery questionnaire with answer stubs to
decision-questionnaire-<slug>.md. Includes staging tests for frontmatter
standards, template sections, and de-upstreaming.
2026-08-29 05:04:13 -07:00
Teknium 7b17d02a09 feat(optional-skills): add setup-wizard-generator — bash wizard for human-only setup steps
Ports the MIT-licensed 'wizard' skill from mattpocock/skills as
setup-wizard-generator (optional-skills/devops). Generates an interactive
bash wizard that walks a human through manual procedures: opens dashboard
URLs, captures values (hidden entry for secrets), writes .env / GitHub
secrets idempotently, and confirms each stage. Vendors upstream's
template.sh library verbatim (bash -n verified) plus a staging test suite
covering frontmatter, template integrity, and de-upstreaming.
2026-08-29 05:03:52 -07:00
Teknium bfeeb50257 feat(skills): rename grill-me to plan-interrogation, fold in frontier-rounds interview mechanic
Renames the optional grill-me skill to the more descriptive plan-interrogation
and upgrades it with the design-tree frontier-rounds mechanic from
mattpocock/skills' MIT-licensed 'grilling' skill: batch all currently-askable
questions per round in dependency order, agent finds facts itself, decisions
stay with the user. Docs page, catalog row, and sidebar entry renamed.
2026-08-29 05:03:20 -07:00
teknium1 71c823bbf7 refactor(skills): move grill-me to optional-skills
Per the 'when in doubt, optional' rule — plan-interview is an
on-request capability, not a weekly daily-driver for most users.

Install via: hermes skills install official/software-development/grill-me
2026-08-29 03:12:20 -07:00
teknium1 260adeb075 chore(skills/grill-me): contributor mapping + docs catalog/sidebar regen 2026-08-29 03:12:20 -07:00
Machan-Army 543c85acbf fix(gateway): bound the hygiene-compression turn-hold so a streaming summary cannot freeze the turn
Session hygiene auto-compression runs inline on the incoming-message path
and awaits the summary worker with a progress-aware inactivity budget
(hygiene_timeout_seconds) that extends up to hygiene_total_ceiling_seconds
(default 600s). A summary model that keeps streaming tokens keeps resetting
the inactivity slice, so the wait can stretch toward the ceiling while zero
bytes reach the user — chat transports (Telegram ~30s idle-timeout) drop the
connection and the turn appears frozen, even though the gateway is healthy.

Add hygiene_max_turn_hold_seconds (default 10), a turn-hold budget that caps
the wall-clock the incoming message waits on hygiene compression. The wait
slice is additionally capped at the remaining budget so the budget is
re-evaluated even when the worker keeps the inactivity slice large. On
exceeding the budget the gateway abandons the inline wait and proceeds on
the uncompressed transcript via the existing timeout path, which revokes the
worker's commit admission (CompressionCommitFence) and defers cleanup — so a
stale compression finishing later can never overwrite the turns appended
after the wait was abandoned.

Well under the typical transport idle-timeout, this guarantees the message
is answered promptly while the detached compression completes in the
background. Configurable via compression.hygiene_max_turn_hold_seconds.

Adds a regression test: a worker that streams progress continuously (so the
inactivity slice never fires) must be abandoned once it exceeds the
turn-hold budget, the turn proceeds uncompressed, and the stale commit is
fenced (no session mutation, role alternation intact).
2026-08-29 13:02:13 +05:30
Teknium 95cf7dc9e8 feat: session temp root moves off tmpfs /tmp to ~/.hermes/cache/terminal by default; auto-pruned after 72h
Follow-up on top of @rahlquist's terminal.temp_dir knob (#97182): the
default itself now avoids RAM-backed tmpfs. Resolution order on the
local backend: terminal.temp_dir > TMPDIR/TMP/TEMP > HERMES_HOME/cache/
terminal (managed, pruned) > /tmp fallback. Pruning: hourly via gateway
housekeeping + once-per-process best-effort sweep; hermes_bg_* triplets
are aged as a group so a live server's fresh .log protects its .pid.
2026-08-28 07:50:33 -07:00
Shaun Eccles 2c6938dc3a fix(compression): retry a stalled summary on the fallback chain (#78981)
A stalled compression summary never raises, so the auxiliary client's
exception-path fallback is unreachable from it. When the progress-aware
timeout aborts a stalled worker, re-run the summary once pinned to the
first auxiliary.compression.fallback_chain entry before degrading to
continue-without-compression.

The pin is a single-use ContextVar consumed by the context compressor's
summary call, so it cannot leak into the detached stalled worker or the
compressor's own main-model retry. A fresh fence is minted through the
host factory so a /stop during the retry still admits against the live
commit boundary.
2026-08-28 02:29:27 +05:30
Teknium d91e4376f1 Merge pull request #65108 from NousResearch/hermes/hermes-793f4fd9
feat(skills): rewrite AgentMail optional skill CLI-first (salvages #60811)
2026-08-27 12:45:01 -07:00
Gille 0dfba37b11 fix(dashboard): trust configured reverse proxies (#94126)
* fix(dashboard): trust configured reverse proxies

* fix(dashboard): trust IPv6 loopback proxies
2026-08-27 10:35:22 -07:00
Teknium 82e1856720 docs: document the database section (journal_mode, synchronous, WAL sizing)
Covers the new operator signals from #85609/#89393 and the
database.synchronous key from #90892.
2026-08-27 07:52:26 -07:00
Teknium 7105657f2c fix: scope /approve-/deny direct-send confirmations to native-streaming adapters; drop leftover debug log; refresh WeCom docs
- The direct adapter.send() confirmation path in /approve and /deny is only
  needed on native-streaming platforms (WeCom) where the reply stream is
  already finalized; other platforms keep the return-text contract (fixes
  4 approve/deny regression tests, guarded with 'is not True' against
  MagicMock auto-attributes).
- Remove a stray [DEBUG] logger.info left in _deliver_media_from_response.
- website/docs wecom.md: replace the 'does not stream' notes with the native
  msgtype:stream behavior and document the stream keepalive extra keys.
2026-08-27 07:33:36 -07:00
teknium1 034c6ac77b refactor(skills): move publish-site to optional-skills
Per the 'when in doubt, optional' rule — site publishing is an
on-request capability, not a weekly daily-driver for most users.
Joins cloudflare-temporary-deploy/page-agent under
optional-skills/web-development (existing category, existing
DESCRIPTION.md kept; the new bundled category dir is dropped).

Install via: hermes skills install official/web-development/publish-site
2026-08-27 07:30:15 -07:00
teknium1 5c57e77541 feat(skills): publish-site — versioned website publishing to GitHub/Cloudflare/Netlify Pages
Zero-core-footprint equivalent of ChatGPT Work Sites: preview, version-before-deploy, provider ladder, rollback, verification.
2026-08-27 07:30:15 -07:00
teknium1 b4e4d59386 docs(skills): regen agentmail optional page + catalog row 2026-08-27 07:09:43 -07:00
fangliquanflq 091cc0e8be fix(hermes_cli): scope hook timeouts and fail closed on pre_tool_call
Allowlist hot-path hooks for abandon-on-timeout, keep subagent_stop on the caller thread, suppress re-fires of hung callbacks, and block tools when pre_tool_call times out.
2026-08-27 16:13:45 +05:30
Ben Barclay ff9930037b docs(slack): note caption ordering and streaming fallback for unfurl knobs
When either unfurl key is set, media captions post as a separate
message before the file (the upload API cannot carry unfurl controls)
and native draft streaming falls back to edit-based delivery. Surface
both side effects in the config reference table.
2026-08-27 15:33:32 +10:00
Andrew Bennett aeaa0c784e feat(slack): add link unfurl controls 2026-08-27 15:33:32 +10:00
Teknium e4451ec6e5 feat(browser): close-with-approval flow for Windows real-profile (toggle arms, agent asks, blocked if still locked) [proof do-not-merge]
Refines the Windows path per three requirements:
1. Only when the toggle is set — closing is offered only if
   browser.real_profile_autoclose is on.
2. Blocked when locked — snapshot_real_profile NEVER kills; a locked profile
   always returns the [profile-locked] signal and the copy is refused. A later
   attempt that is still locked blocks again (no loop, no auto-kill).
3. Ask approval to close — closing is an explicit, user-approved step:
    (new CLI subcommand) runs
   close_browser_holding_profile only when the agent has the user's OK. The
   locked error tells the agent to ask first, then run it, then retry.

- browser_connect: snapshot blocks with _PROFILE_LOCKED_PREFIX (autoclose-armed
  message offers the close; off message says fully-quit); no in-snapshot kill.
- main.py:  subcommand (identity+binding-verified
  tree kill via close_browser_holding_profile); added to _BUILTIN_SUBCOMMANDS.
- browser_tool: surfaces the locked signal + the exact approved-close command.
- Docs/config: toggle arms + agent asks + blocked-if-still-locked.

Tests: snapshot blocks-not-kills with autoclose on AND off; process matcher
identity/binding. 73 real-profile tests pass. Windows live E2E (proof): locked
blocks fast without killing → approved close terminates Chrome → snapshot then
copies a valid DB; autoclose-off blocks with quit guidance.
2026-08-26 19:25:33 -07:00
Teknium 9e9e1b2245 feat(browser): consented auto-close of a running browser for Windows real-profile [proof workflow do-not-merge]
Live Windows CI proved copy-while-running is impossible (Chrome opens the cookie
DB deny-all). So to make Windows actually WORK — not just fail cleanly — add
opt-in auto-close: browser.real_profile_autoclose (default false). When the
profile is locked and consent is on, snapshot_real_profile terminates the
browser process tree bound to THAT user-data-dir (psutil, identity+binding
verified like the daemon reaper — browser binary AND this exact --user-data-dir
in cmdline, fail-closed on ambiguity), waits for the lock to release, then
snapshots. Destructive (loses unsaved tabs) so it's off by default and the agent
asks first; the fail-fast message names the option. No effect on POSIX.

- close_browser_holding_profile: graceful terminate → kill → poll until the
  cookie DB is openable again (bounded); reports relaunch/tray failure clearly.
- _processes_holding_profile: identity+binding matcher (never kills an
  unrelated same-name process on a different dir).
- Config key + docs admonition.

Tests: autoclose closes-then-snapshots, autoclose-failure-reports, fail-fast
names the option, process-matcher identity/binding. 74 real-profile tests pass.

Windows live E2E (PROOF workflow, reverted before merge): autoclose-off fails
fast <30s; autoclose-on terminates real Chrome, lock releases, valid cookie DB
copied.
2026-08-26 19:25:33 -07:00
Teknium 931bf613b1 fix(browser): Windows real-profile fails fast when the browser is running
Live windows-latest proof settled it: a running Chrome opens its cookie DB
deny-all (even CreateFile with FILE_SHARE_READ|WRITE|DELETE fails; sqlite
mode=ro/immutable/nolock all 'unable to open'), so copy-while-running is
impossible on Windows without VSS/admin — and the prior code HUNG ~24min on the
locked file.

Fix: a fast up-front lock probe (_profile_is_locked: one open() of the active
profile's cookie DB; PermissionError = locked) runs BEFORE any copy in
snapshot_real_profile. If locked, bail immediately with 'fully quit the browser
(incl. background/tray) and retry, or turn browser.use_real_profile off'. Never
hangs, never a silent signed-out copy. POSIX has no mandatory locking so the
probe never trips there — copy-while-running still works on macOS/Linux.

Docs: admonition stating Windows needs the browser fully closed (background
apps included); the live-drive-while-running path is #95669.

Tests: lock-probe unit coverage (readable/no-db/PermissionError), snapshot
fails-fast-no-copytree when locked. Windows live E2E asserts the fast-fail
contract (returns <30s with the quit message) + the read-strategy diagnostic.
2026-08-26 19:25:33 -07:00
Teknium 6e854595e2 fix(browser): real-profile review round 3 — overlay-ordering race, torn-copy marker, consent cleanup, active-only copy
Addresses the round-3 findings from @Adolanium + @kshitijk4poor on #95620:

1. Overlay-before-reuse race (blocker): _real_profile_cdp ran snapshot_real_profile
   BEFORE the session-reuse check, so a cold resolve that ends in reuse rewrote
   Cookies/Login Data under a live Chromium holding the user-data-dir open (torn
   DBs, locked txns, phantom logouts). Now: resolve copy dir as a PATH, probe
   reuse first, return early on a hit; snapshot/overlay only on the relaunch
   path when no live browser owns the dir.
2. Torn first copy poisoned freshness forever: freshness keyed on isdir(Default),
   so a half-written copy (disk full / Ctrl+C) was treated as populated and only
   ever got auth overlays. Now gated on a .hermes-snapshot-complete marker
   written only after a full copy succeeds; a torn copy is rebuilt from scratch.
3. Consent revocation left copied credentials on disk: turning use_real_profile
   off now deletes ~/.hermes/browser-profile/ on next browser use
   (cleanup_real_profile_snapshots), so cookies/logins don't outlive consent.
4. Stale non-active profile copies: only the ACTIVE profile (last_used) is copied
   into the copy's Default now — other Chrome profiles are never snapshotted
   (smaller copy, no stale credential dirs lingering).
5. Docs/config/desktop wording aligned to actual behavior (active-profile only,
   refresh on fresh session, consent-off cleanup).

Tests: overlay-skipped-on-reuse + overlay-runs-on-relaunch, done-marker gating +
torn-copy rebuild, active-only copy, consent-off cleanup (removes store +
idempotent + triggered from _real_profile_cdp). 206 browser + 222
backup/file_safety pass. Live: reuse skips re-snapshot; direct launch on the
active-only copy loads the real signed-in Gmail inbox.
2026-08-26 19:25:33 -07:00
Teknium 1f4d095fd8 feat(browser): real-profile browsing via agent-browser copy + browser-use CDP
Copy the user's default-Chromium profile (auth state only) into a managed
snapshot, launch Hermes' packaged Chromium on it via agent-browser, and hand
the CDP endpoint to the Browser Use CLI (and built-in tools) to drive. The
snapshot is a non-default dir, so it sidesteps Chrome 136+'s default-profile
remote-debugging block and never contends with the user's running browser;
launched without mock-keychain switches so keyring-encrypted cookies decrypt.

- consent-gated browser_exec 'local' arg (schema only appears with consent)
- fail-closed on non-Chromium default / snapshot failure
- stale-session guard: reuse only when the live session is on our copy dir
- snapshot excludes extensions/service-workers (renderer wedge) + caches
2026-08-26 19:25:33 -07:00
Nikita Barkov 1f92c5d4ce feat(kanban): carry the review handoff summary into the wake turn
`completed` already puts the worker's summary inside the synthetic wake
turn, so the woken creator sees what was done. `review_requested` did
not: the summary rode the passive ping only, and the wake turn said just
"handed off for review", forcing the woken reviewer to re-read the board
(and losing the PR link the worker had already written).

Reuse the same first-line handoff the `completed` branch builds, so the
existing `gateway.kanban.wake.handoff` string renders it — no new locale
keys, no change to the passive message.
2026-08-26 10:25:33 -07:00
Nikita Barkov 7700d3a011 fix(kanban): wake the origin on review handoffs and triage escalations
`review_requested` and `block_loop_detected` are terminal event kinds that
hand a decision back to the origin subscriber, but neither was listed in the
gateway notifier's `_WAKE_KINDS`. A `notify+wake` subscription therefore got
the passive ping only and the origin agent never took a turn — so an agent
that delegated implementation work slept through the "ready for review"
handoff and through a task being routed to triage, while the equivalent
`blocked` event woke it.

Add both kinds to the wake set, add their status strings to the synthetic
wake message in every locale, and document which events wake.
2026-08-26 10:25:33 -07:00
Zeus-Deus fd565c80e9 feat(desktop): fleet profile rail — every registered gateway's agents on one strip
With several gateways registered, the Sessions profile rail only ever showed
the active gateway's profiles; reaching a bot on another machine meant a
gateway switch first, then a click on the rail that appeared afterwards. Bot
Mode (#91134) and Capabilities already read the union agent roster; the rail
is now its third consumer.

- Every registered gateway's profiles sit on the one strip, in registry order
  (This device first, then by label), each group headed by that gateway's
  kind glyph. The active gateway's squares are unchanged; the others are
  "at rest" (dimmed) with tooltips/accessible names qualified by machine
  (`inbox · Homelab`), so same-named profiles never read alike.
- Clicking an at-rest square performs the same dial → commit → re-home as
  the statusbar switcher, landing on that exact (gateway, profile):
  `selectConnection(id, { profile })`. The spinner sits on the clicked
  square; the previous source stays painted until the target answers.
  Groups keep their slots whichever gateway is active, so a square never
  moves under the pointer that clicked it.
- Right-click on an at-rest square: Switch to / Color / Rename / Edit
  SOUL.md / Delete, executed on the owning gateway (renameProfile,
  getProfileSoul and updateProfileSoul accept the same scope deleteProfile
  already had); the delete confirmation names the machine. The legacy
  per-profile "Connect to a remote host…" item is hidden on multi-gateway
  setups, where the rail shows machines directly.
- Unreachable gateways keep their squares with an amber dot on the glyph;
  two registrations of one backend collapse to one group; past thirteen
  squares across the fleet the strip condenses into a menu sectioned by
  gateway. Roster is fetched on mount / focus / registry change only — no
  periodic fleet polling.
- Single-gateway Desktops render exactly as before: no roster fetch, same DOM.

Also fixes a boot race the e2e surfaced: initializeConnectionsRegistry()
"restored" the launch-mode source over a switch the user had already made
while boot was settling (same class as #91047). The restore now yields when
a switch is pending or already landed.

Tests: pure grouping (fleet-rail.test.ts), rail component fleet mode
(profile-rail-fleet.test.tsx), store (explicit profile pick; restore yields),
and a Playwright e2e (fleet-profile-rail.spec.ts) that boots Desktop with two
REAL backends — the local one plus a second `hermes serve` registered as a
remote URL connection — and verifies layout, a real re-home, gateway-scoped
actions, and order stability.

Docs: multi-connection-desktop.md describes the fleet rail.

Refs #89304, #92384, #91047, #94724

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 08:39:45 -07:00
Teknium 6e5413844e feat(compression): lean tail retention is the default — compaction keeps 10-25K verbatim, not 100-240K
The legacy tail budget scales as threshold×target_ratio, which was designed
around 128K windows at a 50% trigger (~13K tail). On modern big-window
models with raised thresholds it silently hoards: a 1M-window session at
threshold 0.85 keeps a 170K-token verbatim tail (255K soft ceiling) out of
EVERY compaction, so a 540K manual /compress lands at ~290K and every
subsequent turn re-ships the hoard. Nobody chooses this; it is an artifact
of the formula outside its design envelope.

Lean mode (#87326, compaction-v2) was built for exactly this and its recall
was validated in the before/after eval (evals/compaction/results/): clamped
2.5%-of-window tail (10K floor / 25K cap), continuity carried by the
upgraded summary (digests, anchor index, verbatim user messages,
session_search recovery pointers). This flips the DEFAULT to lean; explicit
'tail_mode: legacy' in config keeps the old behavior exactly.

Also fixes a latent bug the flip exposed: update_model() re-assigned the
LEGACY formula directly when recomputing budgets, silently reverting a lean
compressor to the hoard on every mid-session model switch. The recompute
now routes through the mode-aware tail_token_budget property (regression
test included).

Surfaces: context_compressor.py defaults + getattr fallbacks, agent_init
parse default, DEFAULT_CONFIG, gateway _CACHE_BUSTING_CONFIG_KEYS gains
compression.tail_mode (mode changes now evict cached gateway agents like
target_ratio changes do), user + developer docs. Tests: 3 new default
contracts, legacy tests pinned explicitly, feasibility-skip scenario pinned
to legacy (under lean its payloads correctly become compressible).

E2E counterfactual (real imports, 1M window @ 0.85):
  main default:  legacy, tail 170,000 (ceiling 255,000)
  head default:  lean,   tail  25,000 (ceiling  37,500)
  head legacy:   170,000 (opt-out intact)
  update_model to 400K: 10,000 (lean preserved across switch)
2026-08-26 07:16:04 -07:00
Teknium be85903234 feat(macos): one-switch Full Disk Access guidance in doctor and setup
The last piece of the macOS permissions campaign (#52010 follow-up): macOS
prompts per-folder (Desktop, then Downloads, then Documents, ...) as the
agent touches each one — a drip-feed of dialogs on first use. ONE Full Disk
Access grant covers all of them permanently, and with the stable signing
identities merged this week it survives every update. Nothing in Hermes
taught users that.

- hermes doctor: check_macos_full_disk_access() — prompt-free probe (the
  FDA-gated TCC db dir returns EPERM without a dialog; TCC only prompts on
  protected-CATEGORY paths), reports granted state or prints the one-switch
  setup with the Privacy_AllFiles deep link.
- hermes setup: same probe at the end of onboarding — the moment users are
  primed to do system setup — silent when already granted, indeterminate,
  or non-macOS.
- docs: desktop.md TCC section now leads with the one-switch guidance.
- 7 tests (granted / denied / indeterminate / non-macOS, both surfaces).
2026-08-26 06:26:07 -07:00
kshitijk4poor b5e7842442 docs(cron): describe widened continuable scope (origin fallback + explicit opt-in)
The feature page still said 'only the origin chat is ever touched', which
this change makes stale. Documents the three attach-eligible shapes and
that the global flag never activates explicit targets (review nit).
2026-08-26 16:06:09 +05:30
Teknium a74ffb9ee8 chore: map contributor email for projetsjsl 2026-08-26 03:21:37 -07:00