Commit graph

19525 commits

Author SHA1 Message Date
hermes-seaeye[bot]
fed48cf5b0
fmt(js): npm run fix on merge (#74677)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-30 07:36:23 +00:00
brooklyn!
866c9adae3
Merge pull request #74651 from NousResearch/bb/composer-paste-directives
Paste directives into the composer
2026-07-30 02:25:09 -05:00
brooklyn!
a2a74d7acf
Merge pull request #74623 from NousResearch/bb/open-project
feat(desktop): ⌘O open folder as project, projects in ⌘K
2026-07-30 02:12:22 -05:00
Juan Martitegui
0bf471dd68 fix(gateway): preserve voice_only semantics for text input 2026-07-30 00:11:33 -07:00
brooklyn!
05f5df6bdb
Merge pull request #74640 from NousResearch/bb/undo-close-tab-focus
fix(desktop): focus the tab restored by undo-close (⌘⇧T)
2026-07-30 02:06:29 -05:00
brooklyn!
1e2d55072c
Merge pull request #74642 from NousResearch/bb/sidebar-icon-clip
fix(desktop): sidebar labels truncate instead of pushing header icons out
2026-07-30 02:04:54 -05:00
Brooklyn Nicholson
422ecfe1da feat(desktop): hydrate commands when repainting inert composer text
Two repaint sites hand the editor text that is finished rather than
mid-keystroke: the main composer's programmatic draft writes (restore,
insert, history recall) and the inline edit composer opening a sent
message. Both now render with `trailingCommitted`, so a command ending
that text chips instead of reading as a half-typed token — the edit
composer in particular showed plain text for a message the transcript
had just rendered with a pill.

Regression tests cover the paste path: a command ending the paste, one
named mid-prose beside a ref, a path left alone, a paste landing against
a word, and one landing after an existing chip.
2026-07-30 02:00:22 -05:00
Brooklyn Nicholson
ccca952b92 feat(desktop): chip pasted directives in the composer
`appendComposerContents` — the one builder every paste goes through —
only ever chipped `@kind:value` refs. Slash commands had a single
leading-token special case in `renderComposerContents`, which paste
doesn't call, so a pasted `/clean` landed as dead text while the same
text typed by hand became a pill.

Both directive kinds now hydrate from one ordered span walk, with `@`
refs winning a tie so a slash inside a quoted ref value stays part of
that value. Paste additionally scans as inert text: a command ending the
paste is complete rather than half-typed, and the insertion point's own
token boundary decides the leading token, so `foo` + `/clean` stays
`foo/clean`.

`textBeforeCaret`'s chip-atomic serialization moves to rich-editor as
`serializeTextBefore` — the paste path needs the same "a chip edge is a
token boundary" reading that trigger detection does.
2026-07-30 02:00:22 -05:00
Brooklyn Nicholson
c5a68213fa feat(desktop): recognize slash commands in text the composer didn't watch typed
The composer chips a `/command` when it's picked or accepted from the
popover. Text that arrives whole — a paste, a restored draft, an undo
step — never passes through that path, so nothing recognizes the
commands in it.

Extract that recognition into a scanner that answers on the same terms
the typed path uses: no-arg commands only, no paths, built-ins as
invocations while skills may also be named mid-prose, and a trailing
token still-typed unless the caller says the text is inert.
2026-07-30 02:00:22 -05:00
brooklyn!
fa8b959b92
Merge pull request #74630 from NousResearch/bb/update-restart-race
fix(update): GUI update self-deadlocks against its own lock — every retry fails with "Hermes is still running"
2026-07-30 01:43:38 -05:00
Brooklyn Nicholson
3209236ae3 fix(desktop): fade the ⌘-held project label
The 'New session in <project>' preview is a note about what Enter will do,
not the row's name — so it takes text-muted-foreground/80, the same muted
tone the palette's detail notes already use.
2026-07-30 01:20:08 -05:00
Brooklyn Nicholson
958ab818b8 fix(desktop): sidebar labels truncate instead of pushing header icons out
Flex containers around section/lane labels kept their default min-width:auto,
so a long project title refused to shrink at narrow sidebar widths and shoved
the trailing action icons (caret, +, kebab, branch) past the edge. Give every
header label min-w-0 so its truncate can engage, pin shrink-0 on the caret at
the primitive level and on SidebarSectionMeta, and clip LaneLabel's pinned
tail inside the label. Icons now stay visible at any width.
2026-07-30 01:19:31 -05:00
konsisumer
8e1debd5ed docs: purge stale xdist/_enforce_test_timeout test-runner references repo-wide
The test runner moved to per-file subprocess isolation via
scripts/run_tests_parallel.py (hermetic `env -i`, worker count auto-scaled
from CPU count, FLAKY-retry policy) — no pytest-xdist, no SIGALRM per-test
timeout fixture. Docs still described the old runner in many places:

- AGENTS.md: "-n auto xdist workers, in-tree subprocess-isolation plugin"
  clause replaced with the current per-file-subprocess description; the
  `::test_x` single-test example now shows file + -k (runner is
  file-granular).
- CONTRIBUTING.md: "hermetic env, 4 xdist workers" comment corrected;
  `tests/conftest.py::_enforce_test_timeout` reference redirected to the
  win32 timeout-method shim in `tests/conftest.py::pytest_configure`.
- skills/autonomous-ai-agents/hermes-agent/references/contributor-guide.md
  and windows-quirks.md: same corrections (the bundled skill mirrors the
  contributor docs); Windows workaround no longer installs pytest-xdist
  or passes -n 0.
- website/docs + zh-Hans i18n mirrors: same fixes in adding-providers.md
  and the bundled-skill doc pages.
- skills/software-development/python-debugpy/SKILL.md (+ zh-Hans mirror):
  "-p no:xdist"/"-n 0" pdb advice rewritten for the captured per-file
  subprocess runner.
- skills/creative/comfyui/tests/README.md: parent-repo "-n auto by
  default" rationale updated to past tense.

Combined salvage of PR #38295 (konsisumer), PR #51354 (TutkuEroglu,
redirected to the current conftest truth and the relocated
references/contributor-guide.md), and PR #54956 (waroffchange).

Co-authored-by: TutkuEroglu <rrandqua@gmail.com>
Co-authored-by: waroffchange <116298975+waroffchange@users.noreply.github.com>
2026-07-29 23:16:18 -07:00
Jeff Watts
53f7d137ed fix(windows): native Windows correctness for CLI, gateway status, banner, and WSL browser paths
Salvaged from #57016 by @lEWFkRAD:
- cli.py: handle file:///C:/... drive-letter URIs on nt (strip the
  leading slash urlparse leaves); join Termux example paths with literal
  forward slashes so hints stay POSIX on Windows.
- gateway/status.py + hermes_cli/gateway.py: normalize backslashes to
  forward slashes before the HERMES_HOME substring match so separator
  style cannot defeat profile ownership detection.
- hermes_cli/banner.py: cprint degrades to plain print when
  prompt_toolkit has no console (NoConsoleScreenBufferError on
  redirected/absent Windows stdout).
- hermes_cli/browser_connect.py: posixpath.join for WSL /mnt/c/... bases
  (os.path.join would emit backslashes on nt).
- Test hardening: symlink skip-guards, USERPROFILE alongside HOME for
  ntpath.expanduser, SIGKILL absence skipif fixed via monkeypatch,
  drive-letter URI / separator-normalization / banner-fallback coverage.

Dropped from the original PR: tests/cli/conftest.py fixture and the
AppSession _output monkeypatch — main's merged tests/cli/conftest.py
already handles that prompt_toolkit pollution.
2026-07-29 23:16:18 -07:00
Brooklyn Nicholson
ba194c1d19 feat(desktop): File > Open Folder… menu item
No accelerator (⌘O stays a rebindable renderer keybind, matching New
Window's rationale); clicking routes hermes:open-folder-requested through
the preload bridge to the same openFolderAsProject flow.
2026-07-30 01:15:01 -05:00
Brooklyn Nicholson
a11611af39 feat(desktop): projects in the command palette
⌘K gains a Projects group carrying each project's own sidebar codicon.
Selecting one is a pure scope switch; holding ⌘/⌃ previews the variant —
the label swaps to 'New session in <project>' beside a ⌘↵ chip — and
⌘-Enter runs it. A pinned row opens the native picker, and typing an
absolute path offers the same upsert inline. modLabel/comboHint live on
PaletteItem, so the next modifier-variant row gets both for free.
2026-07-30 01:15:01 -05:00
Brooklyn Nicholson
5affcd6bf4 feat(desktop): open a folder as a project with ⌘O
workspace.openFolder (default mod+o, the editor-standard open-folder chord)
runs openFolderAsProject: pick a folder, enter the project that already owns
it or create one named after the folder, scope the sidebar, and land on a
fresh session draft anchored there. A stale backend without the projects.*
RPC still gets the workspace session, with a warning.

StartWorkSessionRequest grows an openTab flag so these opens-from-nowhere
stack a tab instead of spending an occupied main, and goToProject/
resolveNewSessionCwd share one projectRootCwd resolver.
2026-07-30 01:15:01 -05:00
Brooklyn Nicholson
7556c51234 fix(desktop): focus the tab restored by undo-close (⌘⇧T)
Adoption alone is silent, so reopenLastClosedTile only restored placement
and left the tab behind the still-fronted workspace. Focus it after open.
2026-07-30 01:14:11 -05:00
brooklyn!
382282d5a0
Merge pull request #74610 from NousResearch/bb/checkbox-indeterminate
fix(desktop): stop the checkbox painting the check and dash at once
2026-07-30 01:06:49 -05:00
brooklyn!
eefcc098a7
Merge pull request #74611 from NousResearch/bb/composer-strips-outside
Move the composer strips out of the pop-out drag region
2026-07-30 01:05:45 -05:00
brooklyn!
fa183062e4
Merge pull request #74533 from NousResearch/bb/emoji-reactions
feat: iMessage-style emoji reactions on desktop — opt-in, two-way, persistent, model-aware
2026-07-30 01:02:14 -05:00
Brooklyn Nicholson
8c76fe19f8 fix(update): let the GUI updater's hermes update child pass the lock it already holds
The cross-process update lock (fe8e4d93d) made the in-progress marker
mutually exclusive across every update entrypoint — but the Tauri
updater holds that marker for its WHOLE run and then spawns
hermes update as a child stage. The child read the marker, found its
own parent's live pid, refused with exit 2, and the GUI mapped that to
"Hermes is still running. Close all Hermes windows and try the update
again." Retry spawns a fresh updater that deadlocks against itself the
same way, so every GUI-driven update dead-ends on the failure screen
with no winnable retry (observed: three consecutive self-refusals in
bootstrap-installer.log within 90 seconds).

Hand the claim off explicitly: update_child_env exports
HERMES_UPDATE_HANDOFF_PID naming the updater's own pid, and
UpdateLock.acquire treats a live holder matching that pid as the lock
we are already running under — run without claiming, and release
leaves the parent's marker untouched. The env var alone grants
nothing: the pid must also be the live marker owner, so a stale or
forged value cannot bypass the lock, and a dashboard-spawned
hermes update (no handoff env) is still refused exactly as before.
2026-07-30 00:51:33 -05:00
brooklyn!
98e43be8e0
Merge pull request #74602 from NousResearch/bb/pointer-intent
Keyboard-first pickers: give typing focus back, ignore a parked cursor
2026-07-30 00:41:45 -05:00
Brooklyn Nicholson
e17d058269 refactor(desktop): move the composer strips out of the drag region
The micro-action pills, the status stack, and the underside strip all
rendered inside the composer root. The pop-out drag region is an
`absolute inset-0` child of that root, so everything alongside it was
inside the grab area by construction — hovering a pill hatched the
composer and a press between two badges started a peel-out drag.

That was being patched at the gesture instead of the structure: a
`composer-no-drag` exclusion in gestureTargetOk, a matching guard on the
double-click toggle, and a strip that juggled z-index and pointer-events
to climb back over a region painting on top of it.

Introduce a dock column that owns the composer's position and stacks its
children in flow, bottom-anchored:

    composer-dock
    ├── micro-action strip
    ├── status stack
    ├── composer            <- drag region lives in here, and only here
    └── underside strip

The strips are siblings of the composer rather than children, so landing
in the grab area is impossible rather than excluded. gestureTargetOk and
the double-click handler go back to what they were, and the shared strip
constant drops to a bare flex row.

Two things fall out of the move:

Alignment stops needing a magic number. The strips sat in different
containing blocks — one in the stack's absolute lane resolving against
the root's padding box, one in the root's content box — and the root
carries `padding-inline: 5px` for the grab margin, which is the 5px the
pills hung left by. One parent and a shared `px-[5px]` line both strips
up with the surface directly.

The stack stops measuring itself. It published
--status-stack-measured-height only because it was out of flow and the
composer's measurement couldn't see it. As a dock child it's covered by
the dock's own height, so the var, the ResizeObserver effect, and the
detached-node cleanup it needed all go, and thread clearance reads one
number instead of summing two.
2026-07-30 00:18:29 -05:00
Brooklyn Nicholson
f790d5a6ce fix(desktop): stop the checkbox painting the check and dash at once
codicon.css styles glyphs through `.codicon[class*='codicon-']`, a
two-class selector that outranks Tailwind's single-class `hidden`. The
indicator stacks a check and a dash and hides one per state, so neither
was ever hidden and the box rendered both glyphs side by side, spilling
past its 16px bounds in every state including unchecked.

Use the important modifier on both display utilities so state, not
stylesheet order, decides which glyph shows.
2026-07-30 00:17:26 -05:00
hermes-seaeye[bot]
ba7d214b6a
fmt(js): npm run fix on merge (#74605)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-30 05:13:41 +00:00
Brooklyn Nicholson
745d1383cb fix(deps): add frimousse + emojibase to the lockfile without pruning other platforms
npm install on macOS dropped all 26 @esbuild/* cross-platform entries (443
lines) — that breaks Linux/Windows installs. Restored main's lockfile and
merged in only the three genuinely new entries.
2026-07-30 00:08:28 -05:00
Brooklyn Nicholson
5ecc35f9bb fix(tests): teach gateway-server fakes the include_row_ids kwarg
Slice 1 fell over on eight DB fakes with frozen get_messages_as_conversation
signatures — the new opt-in kwarg is part of the method's contract now, so
the fakes accept **_kwargs like the real SessionDB. Also opts the child-watch
resume projection into row ids: it feeds the same _history_to_messages as the
desktop resume, so reactions on a watched child session address rows the same
way.
2026-07-30 00:08:28 -05:00
Brooklyn Nicholson
fec1ac0a7a feat(desktop): reactions are opt-in under Settings → Appearance, off by default
One lever, every surface. The renderer toggle persists locally and mirrors
into display.message_reactions; the backend gates the agent's
react_to_message tool (check_fn) and the model-context annotation on the same
key, and the ':' composer trigger reads the store at detection time. Off
means off everywhere: no ☺ slot, no right-click picker, no :shortcode:
popover, no agent reactions, and the model hears nothing — while reactions
already persisted keep rendering so history doesn't lose data. Also fixes the
import-order lint error CI flagged in composer/index.tsx.
2026-07-30 00:08:28 -05:00
Brooklyn Nicholson
1af8839139 fix(state): make _row_id opt-in per consumer instead of universal
CI caught ACP session restore seeing an unexpected _row_id in restored
history — get_messages_as_conversation feeds more than the desktop, and
changing the default shape broke the strictest consumer. Row ids are now
include_row_ids=True, requested only by the gateway's resume/display
projections; ACP restore, export, and inspection get the transcript in its
historical shape.
2026-07-30 00:08:28 -05:00
Brooklyn Nicholson
a90ccd46b7 feat(desktop): :shortcode: emoji completions in both composers
A third trigger kind beside @ and / — same detection, same popover, same
commit path. :jo opens 😂/🤣/… fed by the bundled emojibase shortcode data
(search hits shortcodes first, then tags and labels); picking inserts the
emoji character as plain inline text, not a chip. Boundary-anchored with a
two-char minimum so localhost:8080, timestamps, and :D never trigger it.
Wired in the main composer and the edit composer's duplicated trigger loop.
2026-07-30 00:08:28 -05:00
Brooklyn Nicholson
6ec319f530 feat(desktop): two-way reaction UI — tapback pill, frimousse picker, live paint
One slot, Slack-style: on assistant rows the picker trigger and the landed
reaction are the same far-right element, so reacting never shifts layout
(empty → ☺ following the action bar's hover fade; reacted → the emoji, always
visible, always full-strength). User bubbles react via right-click and show
the badge beneath, in the checkpoint row's register.

- Clicks paint instantly from a local nanostores overlay — no round-trip in
  the loop; the RPC persists behind it and rolls back visibly on rejection
- Agent reactions land via the message.reaction event into an overlay keyed
  by DURABLE row id, so the end-of-turn resume (which regenerates renderer
  ids and rebuilds from in-memory history) can't clobber the paint
- Full picker is frimousse behind the six-emoji quick row, fed from bundled
  emojibase-data served at ./emojibase by a small vite plugin (offline, no
  CDN), with Slack-style alternating cell tints keyed off the codepoint
- Reaction picker opts out of the shared popover glass: solid surface so
  15%-alpha hover tints stay readable
- react_to_message tool blocks are suppressed in the transcript (like todo):
  the reaction appearing IS the UI; failures still render
- rowId reaches rehydrated messages from both transcript shapes (gateway
  row_id, REST numeric id)
2026-07-30 00:08:28 -05:00
Brooklyn Nicholson
7d92056c49 feat(gateway): iMessage-style message reactions — storage, RPC, agent tool, model context
Reactions live in the existing messages.display_metadata JSON column (no new
table), with iOS Tapback semantics enforced DB-side: one reaction per author
per message, re-tap retracts, different emoji replaces. The desktop catches up
to the reaction contract five platform adapters already ship.

- SessionDB: set/get_message_reaction, latest_message_row_id (role + offset +
  require_text so invisible tool-call-only rows are never targeted),
  take_unseen_reactions (announce-exactly-once), get_message_role
- message.react RPC: accepts row_id or newest_role for live messages that
  haven't learned their durable id yet
- react_to_message tool: desktop-gated (check_fn), defaults to the user's
  latest visible message, messages_back for retroactive reactions
- Model context rides run_message only (beside the speech-interrupted note):
  the persisted prompt stays clean, so no [The user reacted …] scaffolding in
  transcripts, and no cached prefix ever changes
- Resume projection forwards row_id + reactions; _row_id is stripped from
  outgoing API copies next to display_metadata
2026-07-30 00:08:28 -05:00
Brooklyn Nicholson
aaf3298d61 fix(desktop): pickers stop eating the keyboard and the pointer
Committing a model with Enter left focus on the pill (Radix restores it
to the trigger), so the next thing typed went nowhere instead of into
the message being written. And with the cursor parked over the list,
rows re-flowing under it as the query narrowed hover-stole the selection
mid-type — Enter then committed whatever the mouse happened to be over.

Both surfaces adopt the shared primitives: the model menu and every cmdk
list (palette, model dialog, session picker, searchable selects) go
pointer-inert until the mouse moves, and the model menu plus the command
palette hand typing focus back on close. The release defers a frame and
yields when something editable already claimed focus, so a palette
action that opens a dialog or navigates keeps its own focus.

Replaces the model menu's focus/blur highlight gate — pointer intent is
the real signal, so the keyboard highlight no longer flickers off when
Radix moves DOM focus onto a hovered row.
2026-07-30 00:06:52 -05:00
Brooklyn Nicholson
913882cdbb feat(desktop): keyboard-first overlay primitives
Two rules every hotkey-opened, search-driven overlay needs, in one place
so each picker doesn't reinvent them:

usePointerQuiet — a mouse that is merely PRESENT is not a mouse in use.
A list that opens under a parked cursor, or re-flows under one as its
filter narrows, fires pointerenter on whatever row slides beneath; menus
that select on hover take that as intent and steal the row you typed
toward. The pointer stays inert until it actually moves (or scrolls),
then hover works for the rest of the overlay's life.

releaseTypingFocus — dismissing the overlay ends its claim on the
keyboard. Handlers subscribe once, so the primitive stays ignorant of
what typing means on any given surface.
2026-07-30 00:06:52 -05:00
brooklyn!
71a86101ab
Merge pull request #74601 from NousResearch/bb/hover-tab-fallback
fix(desktop): ⌘1-9 dead when the pointer is off the panes
2026-07-30 00:04:59 -05:00
Brooklyn Nicholson
0ad06ae9da fix(desktop): keep ⌘1-9 working when the pointer is off the panes
Hover-first targeting (#74447) made the tab verbs read the hovered zone
else the focused one. But ⌘1-9 and ⌃Tab then took that single answer as
final: with the pointer over the sidebar, the titlebar, or any non-zone
chrome, the resolver returned null and the keys did nothing at all.

Turn the resolver into an eligibility ladder — hovered, then focused,
then the workspace's zone — where each rung must actually satisfy the
verb (a real tab strip for the number keys, a chat strip for ⌃Tab and
the ⌘W / ⌘T family) to claim the keystroke. Pointing at nothing now
falls through to focus, and a fresh window with no interaction yet falls
through to main, so the keys always land somewhere sensible.

This also fixes the narrower case the old code shared with ⌘W: hovering
a zone that is NOT a tab strip (a lone file tree) used to swallow the
key rather than handing it to the next rung.

⌘W / ⌘T already laddered to the workspace, so they keep their behavior
and simply share the one resolver again.
2026-07-29 23:58:41 -05:00
Teknium
8eb06e75b9 fix(tests): stub _ensure_vercel_sdk in vercel sandbox tests — CI has no vercel dist
The tests fake the vercel SDK entirely via sys.modules, but
_ensure_vercel_sdk checks installed DISTRIBUTION metadata through
tools.lazy_deps.ensure(): on CI (no vercel dist + lazy installs
disabled) it raised FeatureUnavailable→ImportError before the fake SDK
was ever reached — 16 failures on slice 6, green on dev boxes that
happen to have vercel==0.7.2 installed. Failure mechanism reproduced
locally by forcing _is_satisfied False; fixture patch verified to close
it while the real-dist path stays green (16/16).
2026-07-29 21:30:53 -07:00
Teknium
e7bf0ad8cd chore: keep LEGACY_AUTHOR_MAP frozen — mehmetkr-31 mapping lives in contributors/emails/
The #68873 salvage re-added a line to the frozen dict; the canonical
mapping (contributors/emails/mehmet.kar@std.yildiz.edu.tr) already
exists from the #68872 salvage.
2026-07-29 21:30:53 -07:00
Teknium
0fe6a36e6e chore: gitignore the .lazy-refresh-incomplete runtime marker
Companion to the #72002 salvage: the marker was accidentally committed
once (3a69e34702) and the guard now prevents test runs from writing it;
ignoring it prevents any future accidental re-commit.
2026-07-29 21:30:53 -07:00
mehmetkr-31
6cf4bdd165 test(gateway): fix order-dependent telegram-mock flake cluster
The file-local telegram mock in test_dm_topics.py installed unconditionally
(no __file__ guard), registered a separate string-valued telegram.constants
module, and force-popped the adapter — poisoning the session for any later
telegram test in the same process (assert 'MARKDOWN_V2' in "'MarkdownV2'").

Fix at the source:
- conftest: _FakeEnumMember(str) with PTB-faithful str()==value and
  repr()==<ChatType.X: 'x'>, satisfying both repr assertions and the
  adapter's str(chat.type) normalization; the same object is bound to
  mod.ParseMode and mod.constants.ParseMode.
- test_dm_topics.py: delete the divergent local mock installer; import the
  shared conftest one.
- release.py: mailmap entry for the author.

Verified: the 5-failure cluster repro (dm_topics + slash_confirm +
approval_buttons + model_picker + network_reconnect + telegram_format in one
process) goes 83/83 green (3x); full tests/gateway single-process run drops
10 -> 5 failed, the remainder being pre-existing discord order-dep failures
out of scope here.

Salvaged from #68873. Credit to @liuhao1024 for the earliest root-cause
diagnosis of this str-enum mock class in PR #33875, two months earlier.

Fixes the telegram-mock order-dependent flake cluster.
2026-07-29 21:30:53 -07:00
Jeeves Assistant
080bb83746 test(homeassistant): prevent unit tests from calling live instances
Two tests made real HTTP calls to homeassistant.local:8123 — on a LAN
with an actual Home Assistant instance they could turn on real lights,
and otherwise burned ~10s in network timeouts. Replace with AsyncMock at
_async_call_service and assert the exact production call signature
(domain, service, entity_id, data). 35 pass in ~0.2s.

Salvaged from PR #72634 by @jeeves-assistant.

Co-authored-by: Jeeves Assistant <jeevesassistant00@gmail.com>
2026-07-29 21:30:53 -07:00
Tony (eazye19)
9e9c220608 chore(tests): drop accidental temp guard probe file
tests/test_zz_guard_probe_tmp.py was a throwaway verification probe that
was swept into the PR #43299 salvage commit by mistake (and one of its
shell=True cases fails by design of the probe). The durable regression
coverage lives in tests/test_live_system_guard.py.
2026-07-29 21:30:53 -07:00
sunwz1115
230a2c273e test: harden yolo and kanban signal tests on macOS
Autouse fixture also resets approval_module._YOLO_MODE_FROZEN so a
HERMES_YOLO_MODE=1 host env can't poison every case (the one
startup-frozen test still patches it back explicitly). Adds the darwin
'ps -o stat=' zombie branch to _is_alive_like_dispatcher, mirroring
production hermes_cli/kanban_db.py — a no-op on Linux.

Salvaged from PR #34069 by @sunwz1115.

Co-authored-by: sunwz1115 <192549904+sunwz1115@users.noreply.github.com>
2026-07-29 21:30:53 -07:00
SmokeDev
3e7a11ca2e fix(test-runner): native Windows venv probe + glyph-safe stdio
Two Windows fixes for the canonical test runner, salvaged from #66496:

- scripts/run_tests.sh: probe the native Windows venv layout
  (Scripts/activate → Scripts/python.exe) alongside bin/activate,
  adapted to main's pytest-import-guarded loop with SKIPPED_VENVS
  reporting. Without it a python -m venv / uv venv on Git Bash/MSYS is
  never found and the runner refuses to start.
- scripts/run_tests_parallel.py: _make_stdio_glyph_safe() reconfigures
  stdout/stderr to UTF-8 (errors=replace fallback) so the ✓/✗ progress
  glyphs cannot crash a cp1252 console when the runner is invoked
  directly (run_tests.sh's PYTHONUTF8=1 only covers the wrapped path).
  No-op on UTF-8 stdio. Ships 3 OS-independent cp1252 tests plus
  encoding=utf-8 in the runner-subprocess assertions.

Dropped from the original PR: the USERPROFILE/HOMEDRIVE/HOMEPATH/
SYSTEMROOT env-forwarding hunk — main's WIN_ENV loop already forwards
a superset (#67385/#70813).
2026-07-29 21:30:53 -07:00
Jeff Watts
0860b9804f test(tui_gateway): pin goal-command config home against collection-time _hermes_home freeze
Sibling files (test_billing_rpc etc.) import tui_gateway.server at
collection time, freezing module-level _hermes_home = get_hermes_home()
(server.py:54) to the developer's real home before conftest isolation
runs. _load_cfg() then reads the REAL ~/.hermes/config.yaml — e.g. a
local MoA preset — instead of what _write_moa_config wrote.

The server fixture now monkeypatches _hermes_home to the isolated
HERMES_HOME and resets the mtime-keyed cfg cache (_cfg_cache/_cfg_mtime/
_cfg_path); monkeypatch restores originals on teardown.

Complementary to the #57066 autouse teardown, which only restores the
cfg cache to its pre-test value and never re-points _hermes_home.

Combined tests/tui_gateway + tests/test_tui_gateway_server.py:
792 passed.

Salvaged from PR #63981 by @lEWFkRAD.
2026-07-29 21:30:53 -07:00
Tony (eazye19)
7216ca19a6 fix(tests): live-system guard treats only argv[0] as the executable
Argv-list commands now scan only argv[0] against _PROCESS_KILLERS, ending
false positives like ['cat', '.../skill'] ('skill' is a real util-linux
binary name). Wrapper executables (sh/bash/env/nohup/timeout/sudo/xargs/…)
keep full-token scanning so ['bash', '-c', 'pkill …'] and
['env', …, 'pkill', …] stay blocked; string commands are unchanged.
Adds tests/test_live_system_guard.py pinning both directions.

Salvaged from PR #43299 by @eazye19.

Co-authored-by: Tony (eazye19) <support@captureclient.net>
2026-07-29 21:30:53 -07:00
Christopher-Schulze
00cd9b2b3a test(fal): pin fal_common behavioral contracts
Contract tests for tools/fal_common.py: queue-URL normalization
(trailing slash / whitespace / empty-raises), _extract_http_status
response-vs-exc precedence and non-int rejection, the
_ManagedFalSyncClient RuntimeError guards against fal_client private
API drift, and submit()'s queue-URL + POST/json/timeout wiring.

Trimmed on landing: test_non_string_coerced_to_string (pins an
implementation accident, not a contract) per the coverage-padding
policy in AGENTS.md.

Salvaged from #52166.
2026-07-29 21:30:53 -07:00
BlutAgent
b631f80b7a fix(tests): register no_isolate and ssh pytest markers
Registers the two genuinely-unregistered markers (no_isolate, ssh) in
pyproject.toml, silencing PytestUnknownMarkWarning for tests/tools/test_mcp_*
and tests/tools/test_file_sync_perf.py.

Dropped hunk: the live_system_guard_bypass line from the original PR — that
marker is already registered dynamically via pytest_configure/addinivalue_line
in tests/conftest.py, so the ini entry would be a duplicate.

Salvaged from #71403.
2026-07-29 21:30:53 -07:00
Jeff Watts
f708a85d2e test(tui_gateway): make the tui gateway suite order-independent
Cross-file leaks made tests/tui_gateway + tests/test_tui_gateway_server.py
fail when run in one process (issue #57068):

- conftest: _hermetic_environment now re-pins hermes_state.DEFAULT_DB_PATH
  to the fake home so no test ever touches the developer's real state.db
- conftest: new autouse _reset_tui_gateway_server_state snapshots/restores
  _methods, cfg cache, db handle and _real_stdout, and tears down leftover
  sessions via the production _close_session_by_id(..., end_reason=
  "test_cleanup") boundary
- per-file fixtures scope patch.dict(sys.modules) to the import only
- drop reload()-based teardowns that duplicated atexit hooks

Conflict resolution vs main: kept main's mod._live_transports.clear() in
the test_protocol.py server fixture alongside the snapshot/restore logic.

Combined run now 792 passed / 0 failed.

Salvaged from PR #57066 by @lEWFkRAD. Fixes #57068.
2026-07-29 21:30:53 -07:00