Commit Graph

20 Commits

Author SHA1 Message Date
ethernet
2364dc30c0 fix(install-e2e): prove a module-launched backend's tree without its app-owned cwd
The diagnostic from the last run named the mismatch:

  cwd=.../home/.hermes/.desktop-smoke-home  HERMES_PYTHON_SRC_ROOT=(unset)
  expected=.../home/.hermes/hermes-agent
  command=".../hermes-agent/venv/bin/python" "-m" "hermes_cli.main" "serve" ...

The backend was the installation's own venv interpreter; the check inferred the
import root from the process cwd, which the APP owns (the smoke's home), so a
correct backend read as "imports a different source tree".

A captured HERMES_PYTHON_SRC_ROOT stays authoritative (a launcher that bound a
tree still has to match). Without one, accept the launcher cd'ing into the tree
or a command that names it, and only then fail. Unreadable paths count as no
evidence instead of throwing ENOENT. Invariant test covers all three, including
the foreign interpreter that still fails.
2026-09-17 16:45:46 -04:00
ethernet
7d4eab88f1 chore(install-e2e): make the backend-origin mismatch and the composer inspectable
Both failures reached new assertions and reported nothing to inspect. The
source-origin check now names the cwd, HERMES_PYTHON_SRC_ROOT and the command it
saw beside the expected root, and the smoke writes desktop-backend-<phase>.log
BEFORE that assertion instead of after it, so the backend's identity is on disk
when the check fails.
2026-09-17 16:19:54 -04:00
ethernet
9606ba3bd6 fix(install-e2e): make the desktop smoke's composer locator hit-test the editor
waitForChatReady targeted a bare `[data-slot="composer-root"] textarea`, and the
composer renders an aria-hidden, sr-only <textarea> as assistant-ui's binding
primitive beside the real contentEditable editor. That primitive is "editable",
so it passed toBeEditable and then no trial click could ever hit it -- 225
retries ending in "element is outside of the viewport", which is what every
+desktop leg reported once the app stopped booting behind its onboarding
overlay (see the run with model.base_url in config.yaml: the app now resolves
http://127.0.0.1:<port>/v1 and renders the chat UI).

Require the visible editor, keep a textarea fallback only for a real input
(not aria-hidden, not sr-only), and let the failure explain itself: it reports
the composer-root/contenteditable counts and the composer's outerHTML instead of
a bare Playwright timeout.
2026-09-17 16:08:51 -04:00
ethernet
5265ebed78 fix(install-e2e): put the mock endpoint in config.yaml where the desktop reads it
The smoke writer fed the endpoint to `model.provider: custom` through .env
OPENAI_BASE_URL, which runtime_provider's bare-`custom` trust path never reads
on v2026.8.31 -- that tree's own comment: "OPENAI_BASE_URL env var is no longer
consulted -- config.yaml is the single source of truth for endpoint URLs". So
the app's readiness check (setup.runtime_check) resolved `custom` to no endpoint
and booted behind its onboarding overlay ("No usable credentials found for
custom. setup.status reports configured credentials, but runtime resolution
still failed"), which is why every installer-script+desktop leg failed at its
OLD checkpoint and why the desktop smoke's trial click never landed.

Reproduced against the real ladder before changing anything: with the writer's
config, resolve_runtime_provider(requested='custom') raises
"provider 'custom' resolved without credentials (no endpoint or API key
configured)"; adding
model.base_url makes it resolve to the mock URL (api_key no-key-required, the
loopback bypass). A custom_providers entry alone is not enough, so the endpoint
now goes where both vintages read it. The named/custom:<name> entry and the .env
pair stay for the consumers that use them.
2026-09-17 15:46:38 -04:00
ethernet
f58d34cf5e fix(install-e2e): pin the mock provider's endpoint in config.yaml
The desktop smoke configured a provider as `model.provider: custom` plus
OPENAI_BASE_URL/OPENAI_API_KEY in .env, with deliberately no provider entry in
config.yaml. Every `installer-script+desktop` leg then died at its OLD
checkpoint: the v2026.8.31 app boots behind the onboarding overlay ("No usable
credentials found for custom. setup.status reports configured credentials, but
runtime resolution still failed"), so the smoke's trial click on the composer
never landed (225 retries against the covering div).

That vintage resolves a bare `custom` only from config.yaml
`custom_providers:` (name + base_url + key_env, with a bare-"custom" fallback to
the first valid entry); the env pair stays for the trees that resolve the
endpoint from the environment. Write both, replacing the entry this writer owns
rather than stacking duplicates.
2026-09-17 14:36:43 -04:00
ethernet
b4a294fff9 Merge origin/main; keep PM as plugin dependency owner
Reconcile plugin declarations and validation through PM's atomic generation publication; preserve external runtimes, target markers, and conflict refusal. Keep one source-update completion owner and port upstream lifecycle changes to the PM desktop/runtime paths.
2026-09-17 13:52:05 -04:00
teknium1
f879fead28 test(desktop): live Bot Chat spec for message_agent friendly-name targets
Mock inference gains a one-shot `E2E_CALL(<tool>)[<json>]` script so a spec can make
the REAL tool run from a real Bot Chat; the new spec creates a sender bot, seeds a
`writer` profile titled "Scribe", sends a DM to "Scribe" and asserts the rendered
tool result says "Message dispatched to @writer" (base: "No teammate named
'Scribe'"), plus the sender's state.db tool row.
2026-09-16 22:25:33 -07:00
teknium1
b466a87bd6 fix(bot-mode): group rooms wake on @all, ignore prose stop words, report dead turns, approve on click
Three group-room turn-loop defects, each live-reproduced in the real
Electron app against a real gateway with mock inference:

- Hold classifier (group-rounds.ts): a stop/halt/pause token holds a member
  only within two words of an @mention, so "@impl go, das ist halt ein Test"
  is delivered instead of setting a sticky hold (#103893); addressing the
  whole room (@all/@everyone) without a stop word releases every hold, so a
  Stopped room wakes on "@all <task>" without the literal "resume" (#97740).
- Retained failure (group-turns.ts): the gateway keeps a failed turn under
  session.resume.inflight as {status:'error'}; the room read that as live
  work and slid the deadline to the 20-minute cap while the member looked
  busy. groupSessionBusy/retainedGroupTurnError classify it as finished;
  the foreground poll throws it into the failed-turn path (activity +
  roster badge) and the harvester consumes the stranded marker (#92760,
  diagnosis from #95103 by @hrnbld).
- Approval card (group-chat-parts.tsx): approval choices submit on click;
  the clip-prone footer Respond button is gone for approvals (#91706).
  Room message code blocks wrap instead of overflowing (#91857 by
  @piskooooo).

Tests: invariant vitest cases in group-rounds/group-turns/group-chat-parts
(all red on base); three Electron specs under apps/desktop/e2e using two
new mock-inference triggers (gated rm -rf for a real approval prompt, a
non-retryable 401 for a retained failure). Docs: bot-mode.md § Groups.

Co-authored-by: hrnbld <hrnbld@users.noreply.github.com>
Co-authored-by: piskooooo <piskooooo@users.noreply.github.com>
2026-09-16 21:55:50 -07:00
teknium1
89f277020c fix(desktop): bot-workspace tiles drop the whole ambient composer selection; live Electron proof
Gate reasoning_effort and fast behind the same switch as model/provider in
desktopSessionCreateParams (includeComposerSelection): a Bot-workspace tile
targets a different profile without switching the window's composer, so all
four fields of an unrelated session's pick would otherwise ride into its
session.create. Omitting them lets the bot profile's configured defaults apply.

The vitest added for this PR left the hoisted requestGatewayForAgent mock
with a recorded call (restoreAllMocks only restores spies), which failed the
next test's not-called assertion in CI ("keeps an unlisted named local
legacy-profile tile owned by its bare profile"). The test now resets that mock
and the composer atoms it set, and asserts the two new omitted fields.

New Playwright spec bot-tile-ignores-ambient-composer-model.spec.ts drives the
real app: open a bot's canonical chat, pick a second model from the composer
menu (the mock provider now lists extraModels; receivedModels records the
model of every completion request), Ctrl+T a side chat, send a turn, and
assert the inference request carried the profile default. Fails on main's
index.ts (request carried the ambient pick), passes on head.
2026-09-16 16:44:13 -07:00
ethernet
74ca008260 fix(e2e): no named 'mock' provider anywhere
The second writer (desktop e2e fixtures' dead-backend setup) hand-rolled
the same mock-named provider block, and the first still emitted
providers.mock. Both now go through the one writer and express an external
endpoint the portable way: provider 'custom' + OPENAI_BASE_URL +
OPENAI_API_KEY. grep for a mock provider in the harness now returns
nothing.

The desktop fixture keeps its dead endpoint (port 1) -- only the spelling
changed.
2026-09-16 19:32:46 -04:00
ethernet
4ae3ca7036 fix(install-e2e): configure the mock as a plain OpenAI-compatible endpoint
No tree should need to know a provider called 'mock'. resolve_provider()
accepts only openrouter/custom/PROVIDER_REGISTRY at every vintage -- the
same three-way gate at v2026.3.12 and at HEAD -- so declaring an
out-of-tree provider named 'mock' made older refs die with
"Unknown provider 'mock'" the moment they got past the not-configured
guard.

Point them at the generic route instead: model.provider 'custom' plus
OPENAI_BASE_URL + OPENAI_API_KEY in .env. That pair is how any external
OpenAI-compatible server is reached (vLLM, llama.cpp) and is accepted by
every ref, so the install is genuinely chat-capable on any starting tag
without any mock-specific concept in the product.

The named providers.mock block is left in place for one more run:
removing it is a separate step once CI confirms 'custom' resolves on the
desktop legs, which cannot be validated from here.
2026-09-16 19:09:33 -04:00
ethernet
0ecfcc2881 fix(ci): predict the desktop smoke's Hermes home from the baked bundle env
Commit desktop bundles can bake environment defaults/clears (--bundle-unset
HERMES_DESKTOP_USER_DATA_DIR, HERMES_HOME=null) that stomp the smoke driver's
--home/--user-data pin, so every native smoke threw "Desktop did not honor the
isolated home and userData directories".

Instead of pinning, the driver replays the bundle env over its launch env
through the same resolver the app runs, seeds the predicted home (bailing
rather than wiping when it is not empty), and verifies the app landed there
via a new hermesHome report on the version bridge. The --user-data equality
check now applies only when the artifact bakes no env.

- Extract the pure path resolver into electron/data-paths.mjs (data-paths.ts
  is now a typed re-export) so Node's type-stripped driver can import it.
- Add applyBundleEnvironment/validateBundleEnvironment as the pure twin of the
  bundle banner, pinned by a lockstep test.
- Record bundleEnv in the install stamp and add readBundledBundleEnv.
- Report hermesHome from hermes:version and assert it equals the predicted home.
2026-09-15 20:32:31 -04:00
teknium1
3040c87ac1 fix(bot-mode): do not retry failures from already queued sends
Keep failed-member exclusion across the room queue, rather than resetting
it per pending thread. A new user action after failure still permits a new
attempt. Share the drain activity epoch so a skipped queued thread cannot
hide the preceding member failure.

Proven red in real Electron: hold transport refusal, enqueue same-thread
and cross-thread sends, then release; old head submits three times, fixed
head once. Strengthen follow-up evidence with distinct provider replies,
exact public log order/count, and per-input inference counts.
2026-09-14 17:35:04 -07:00
teknium1
52972d15f3 test(desktop): live e2e for a teammate's @hermes handoff driving the primary bot
Real-Electron Playwright spec for #100406: a two-member room (primary
profile + code-farmer). The user addresses only @code-farmer; its
scripted reply @mentions hermes; the assertion is a `default`-authored
"B" entry in the persisted room log. On origin/main the room settles
after Code Farmer's line and the spec fails at that assertion; with the
mention-alias fix it passes.

The mock inference server gains a per-speaker script for group rooms:
`E2E_SAY(<handle>)[<line>]` tokens in the user's send answer the member
whose turn prompt opens with `You are @<handle>`; unscripted members
reply "(pass)". `{at}` stands for `@` so the script itself never
mentions anyone and round one only drives the member the user tagged.
2026-09-14 17:04:27 -07:00
ethernet
748d119abf fix(ci): seed every resolvable hermes home in the bundle smoke driver
Commit-build desktop bundles can bake a HERMES_HOME clear (the
environment-defaults banner writes '' so a stale registry or ambient
value cannot redirect a real install). The bundle smoke driver passed
its own HERMES_HOME via the launch env, so the banner stomped it and
the app resolved <userData>/hermes-home instead — the mock provider
config seeded into --home was never read, the backend reported the
profile unconfigured, and the first-run setup overlay intercepted
every composer click until the 120s timeout (the macOS smoke logs).

Seed the mock provider config and .env into every home the app can
resolve under the driver's env (explicit --home plus the
<userData>/hermes-home fallback), and capture backend logs from both.

Also create the sandboxed AppData/XDG directories before launch:
Electron resolves shell folders before app 'ready', and Windows
SHGetFolderPath fails when the roaming dir named by the sandboxed
USERPROFILE/APPDATA does not exist, crashing applyDesktopIdentity at
startup ("Failed to get 'appData' path", the Windows x64 smoke logs).

windows-11-arm runner images ship Chocolatey but no winget, so the
screen-recording action's ffmpeg install failed before the Windows
arm64 chat steps could run at all; fall back to choco there.

Validation: bundle-env banner reproduced locally against a
banner-baked electron main (base driver timed out on the setup
overlay intercepting the composer; fixed driver seeds
<userData>/hermes-home, verified on disk). New vitest invariant
proven red on base, green with the fix; tests-js suite otherwise
unchanged (one pre-existing setup-pm-cache failure on base too).
2026-09-14 14:20:19 -04:00
ethernet
bf75bc2516 feat(ci): require desktop chat after bundle and install builds
Share the real composer, provider-witness and completed-reply check across
post-build bundle smoke and desktop-bearing install/update checkpoints.
Keep native automatic-relaunch proof separate from post-update chat.

Download receipt-bound artifacts without release credentials and install
DMG, ZIP, MSIX and universal MSIXBUNDLE on each native architecture.
Split Windows assembly from feed publication; publish tested bytes only.
Bind candidate smoke results into the manifest used by stable promotion.

Verify historical/source provenance without assuming a version IPC commit,
strip CI identity from source build children, and use the actual Electron
PID rather than Playwright's Windows launcher wrapper.

Validation: real Linux Electron chat and sequential OLD/NEW source smoke
with preserved history; 145 targeted Python tests and 14 JS tests passed;
TypeScript, shell/PowerShell parsing and workflow checks passed.
Native macOS/Windows deployment and historical upgrades need Actions proof.
2026-09-13 19:57:38 -04:00
teknium1
b25c2ef0ba test(dev-mock): raise the dev:mock provider context window to the 64K floor
Same site class as the e2e fixtures: tests-js/scripts/mock-server.ts writes
the config for `npm run dev:mock`. Entry-level providers.<name>.context_length
is honoured since #98387, so a 4096 window now fails agent init below
MINIMUM_CONTEXT_LENGTH exactly as it did for the Playwright fixtures.
2026-09-12 08:30:27 -07:00
hermes-seaeye[bot]
e9bb6e86fb fmt(js): npm run fix on merge (#106039)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-08 21:23:02 +00:00
yoniebans
d19038e76b Merge upstream main (b81383ec21) into the install-e2e suite branch
Conflicts, three, resolved:
- scripts/desktop-update.ps1: upstream's side taken whole. Upstream moved
  the hand-off to scripts/desktop-update/windows.ps1 (this file is now a
  one-line compat forwarder) and the new implementation already drains
  both pipes asynchronously with bounded abandonment, which supersedes
  this branch's stderr-drain fix for the same deadlock.
- apps/desktop/e2e/fixtures.ts: kept upstream's resolveElectronBinary
  import alongside this branch's consolidated mock-server path.
- tests-js/scripts/mock-server.ts: kept upstream's task-panel trigger
  addition inside the consolidated file; rewired the five upstream specs
  still importing './mock-server' to the consolidated path (export sets
  verified identical) and dropped the superseded apps/desktop/e2e copy.
2026-09-01 19:33:13 +02:00
ethernet
fd02bd8621 refactor(desktop-e2e): one mock inference server in tests-js/scripts
The dev:mock script duplicated the e2e mock server. The copy had only
the plain chat reply; every scripted path lived only in the e2e version.
A single mock server now lives in tests-js/scripts/mock-server.ts.
The e2e suite imports it as a library. Running the file directly
starts the server, writes a mock config, and launches the desktop app.
The dev:mock script now runs that file.
The e2e tsconfig lists tests-js/scripts in its include, because the
composite project rule requires every imported file to be listed.
2026-08-12 10:40:57 -04:00