Commit Graph

30 Commits

Author SHA1 Message Date
ethernet
9214174e07 fix(ci): lint legs after the main merge
- check_no_tmp_literals: resolve scratch via tempfile/os.tmpdir; the termux
  container mount point is one marked variable per script
- ruff TID251: desktop E2E fixtures may reach PM internals like tests do;
  the pm.runtime_stage ban message no longer names a module that never existed
- auth_codex: build the capped httpx stream subclass on first use so importing
  hermes_cli.auth_codex no longer forces httpx (the lazy proxy in auth_constants
  was defeated by a module-scope base class; broke lanes without httpx)
- desktop-smoke: launchApp is a parameter; the bundle-env test substitutes a
  refusing launcher instead of letting Playwright spawn a dying binary
  (3 unhandled rejections failed the tests-js lane)
2026-09-19 23:25:18 -04:00
ethernet
48f619f4e4 fix(install-e2e): accept the app's own report where the backend environment is unreadable
assertBackendOrigin demands evidence of the tree a module-launched backend
imports from. On Windows there is none to read: the venv launcher hands the
interpreter over as a system python, so argv never names the tree, and the
platform exposes neither the process's cwd nor its environment. Every desktop
leg there died with

    Source backend listener imports a different source tree
      (cwd=(unreadable), HERMES_PYTHON_SRC_ROOT=(unset),
       executable=C:\hostedtoolcache\...\python.exe, ...)

while the backend was in fact the installation's own: its app log says
`[backend] `serve` supported for Hermes at <root> (venv: <root>/venv)` and it
came up and served.

The two facts that matter are already established before the assertion runs:
localBackendProcess only returns a listener that is a descendant of the app
process the driver launched, and the driver asserts the root that app reported
resolving against options.root. So pass that reported root through as evidence
and accept it -- but only when the platform supplied no process evidence at all
(no cwd, no PYTHONPATH, no VIRTUAL_ENV), which keeps the platforms that can read
one exactly as strict as they are today.

Verified: tsc -p tests-js/tsconfig.json --noEmit clean; the new invariant test
covers the Windows shape, its control row (nothing readable and no report is
still a different tree), a report of some other tree, and the case that matters
most -- readable evidence naming another tree is NOT rescued by the app's report.
The only failing test in that file is the pre-existing headless Electron launch.
2026-09-18 12:13:04 -04:00
ethernet
4abc223f90 fix(install-e2e): prove the desktop backend's tree from the environment
The backend-origin assertion accepts a command that names the installation
root, which is the LINUX shape. On macOS `<root>/venv/bin/python` is a symlink
to the framework binary and the app resolves it before spawning, so argv names
that binary and never the root; on Windows the install's venv copy is bypassed
for the toolcache interpreter. Twelve macos legs and their windows twins died
with "Source backend listener imports a different source tree" while the
backend was the installation's own: its log reads
`[backend] serve supported for Hermes at <root> (venv: <root>/venv)`, and it
came up and served on a real port.

The app binds that backend to the tree by ENVIRONMENT -- main.ts puts
`[ACTIVE_HERMES_ROOT, process.env.PYTHONPATH]` on the backend's PYTHONPATH and
VIRTUAL_ENV names its venv, which is how `import hermes_cli` resolves from the
installation. macOS reads a process environment through `ps eww`, so the
assertion now also accepts an installation-root entry in PYTHONPATH, or a
VIRTUAL_ENV whose parent is the root. Windows exposes no equivalent reader, so
a Windows backend stays exactly as strict as it was.

Verified: `vitest run tests-js/scripts/desktop-smoke.test.ts` passes including
the new test (macOS shape accepted; control row with no environment evidence
still a different tree; evidence naming another tree still rejected), and
`tsc -p tests-js/tsconfig.json --noEmit` is clean.
2026-09-18 04:25:06 -04:00
ethernet
626b3a80a0 fix(install-e2e): clear the restored composer draft before a chat checkpoint types
The update window's checkpoint submitted the ROOT checkpoint's prompt: the
backend turn for the window's fresh session carried the earlier checkpoint's
exact text, so the witness could never match the prompt the smoke typed. The
app persists its composer draft across launches, so the window boots with the
previous checkpoint's text already in the composer.

Select-all + Delete it and refuse to type until the composer reads empty, so
the checkpoint proves its own input rather than inheriting a draft.
2026-09-18 01:17:52 -04:00
ethernet
d66bd10769 test(install-e2e): prove the desktop composer took the prompt before trusting Enter
The draft-clear poll passes vacuously: the composer reads '' when the editor
never accepted the keystrokes, so a window whose chat is inert looked exactly
like a window that sent and cleared. Assert the echoed prompt first, so the
checkpoint fails fast and says which half broke.

On failure, also write the renderer's own evidence (`desktop-chat-<phase>-renderer.log`:
timeline-free console lines plus thread/composer DOM state) — from the mock's
side a swallowed send and a send that was never made are identical.
2026-09-18 01:17:52 -04:00
ethernet
999a923e80 fix(install-e2e): log what the mock parsed, not just the request line
The desktop chat smoke's witness poll timed out on the app-update leg while
`POST /v1/chat/completions` was logged and answered — a request arriving and a
request being *recorded* look identical in mock.log. Log the recorded prompt
text on the two recognized body shapes, and when neither matches log the parsed
lastUserMessage plus the raw body (truncated). One run then names the shape
mismatch instead of another round of theory.

Extracting the shape check also pins that an unrecognized body records nothing,
rather than a half-parsed witness, and describeForLog tolerates an absent
lastUserMessage (`JSON.stringify(undefined)` is not a string).
2026-09-18 01:17:52 -04:00
ethernet
9dd3a21fa1 fix(install-e2e): log every request the mock receives
The driver collects this server's stdout as mock.log, so a request that never
arrived and a request to an endpoint the mock does not serve were both invisible:
"0 requests" was read as evidence the desktop app never sent anything, when the
server was in fact incapable of recording a request at all.

One line per request (method + path), plus an explicit
"NOT IMPLEMENTED <method> <path> → 404" on the fallthrough. The mock serves only
/v1/models, /v1/chat/completions and the witness endpoint, so an app that POSTs
/v1/responses now says so by name instead of failing silently.

Verified against the real server: GET /v1/models → 200, POST /v1/chat/completions
→ 200, POST /v1/responses → "NOT IMPLEMENTED POST /v1/responses → 404".
2026-09-18 01:17:52 -04:00
ethernet
7ffadfa546 fix(install-e2e): give the submitted draft the same window the other post-send checks get
installer-script+desktop -> hermes-desktop-app-update failed on
"The submitted draft must clear before the idle control proves completion"
(15s predicate), while the failure screenshot shows a healthy, configured app --
Gateway ready, Mock Model selected, and the exchange COMPLETED
("Hello from the mock inference server! The full boot chain is working.") with an
empty composer by capture time.

So the submit was accepted and QUEUED, not rejected: v2026.8.31's composer routes
a submit to its queue while a turn is in flight (its own comment: "busy submit
routes text to the queue instead of a steer"), so the draft legitimately stays
visible past 15s and clears once the queue drains. That is why this leg differs
from its sibling, whose app the capture step builds from HEAD.

The draft-clear poll now uses 90s like the mock-receipt and reply polls beside it;
those still enforce delivery, so a genuinely stuck composer fails there instead.
2026-09-17 22:02:38 -04:00
ethernet
0117a28b44 fix(install-e2e): writeEnvFile rewrites keys in place instead of moving them
desktop->desktop failed with "0 deleted, 1 modified" and MODIFIED .env but no
`variables ...` line; the new line/order summary said "no key differs --
comments/order/blanks only", which is reordering. The writer filtered its managed
keys out and re-appended them at the end, so every call moved a journey's own .env
entries while keeping the same keys and values: a byte-level change with nothing
for a value-level diff to name.

Managed keys are now rewritten where they already stand, with only missing ones
appended, and the prior file's trailing blank lines are dropped BEFORE appending
(trimEnd() runs after them and cannot reach a blank line they follow -- the first
version of this fix left one, caught by asserting the writer's exact bytes).

Verified: writeEnvFile leaves
  OPENAI_BASE_URL=<url>/v1, "# keep me", OTHER_TEST_VALUE=kept, MOCK_API_KEY=..., OPENAI_API_KEY=...
in that order; tests-js/scripts/desktop-smoke.test.ts is 12 passed | 3 skipped.
2026-09-17 20:16:07 -04:00
ethernet
690b8d5b34 fix(install-e2e): writeEnvFile must not delete a provider endpoint it isn't replacing
The run configures the mock provider through the CLI path, which writes
OPENAI_BASE_URL/OPENAI_API_KEY into .env. tests/install/e2e-assets/desktop-smoke.ts
then calls writeEnvFile(home) with NO url -- and that call filtered the portable
pair out of the file while writing only MOCK_API_KEY. So the user-state snapshot
recorded .env without the endpoint, the upgrade's own post-update .env sync added
it back, and the leg failed as "the upgrade changed the user's own state ... 0
deleted, 1 modified" with the pair reported as ADDITIONS (before: 26403 bytes with
MOCK_API_KEY; after: 26473 bytes with the pair).

Only keys this call actually replaces are dropped now: without a mockUrl the
MOCK_API_KEY line is still rewritten and the portable pair is left untouched.
Proven red on base by the new case (writeEnvFile(home) left
'\nMOCK_API_KEY=e2e-mock-key\n', endpoint gone) and green with the fix.
tests-js/scripts/desktop-smoke.test.ts: 11 passed | 3 skipped.
2026-09-17 19:49:01 -04:00
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