Commit Graph

125 Commits

Author SHA1 Message Date
ethernet
4188301624 fix(build): propagate icon and queued-open failures
Icon generation reported errors but returned success. Aggregate target
write and verification failures into exit 1, while processing later
targets. Keep rendering dependencies in the isolated build group.

A synchronous open error consumed a slot. Returning that slot alone
still let a deferred throw suppress an earlier callback and stall the
queue. Schedule queue draining outside completed callbacks and retain
the original exception.

Verified real clean-source generation and structural checks for all
35 targets, plus a real write failure through the Node/uv runner.
Native Windows child tests cover immediate and deferred open errors.
POSIX descriptor-limit cases are explicit skips here, not passes.
No signed package or native macOS acceptance is claimed.
2026-09-09 18:38:26 -04:00
ethernet
60a84773f5 fix appinstaller update 2026-09-09 10:54:10 -04:00
ethernet
f6db54ddf2 fix(release): report handles on failed dmg detach
DMG failures lose their useful state when dmgbuild performs forced
cleanup. Capture open handles immediately after a failed native detach,
before the supplier retries or cleans up the staging image.

Use the resolved dmgbuild toolset and its paired Python interpreter.
Report scoped lsof results, including process IDs, descriptors and paths.
Keep detach results, arguments, signing and retry policy unchanged.

Verification: three JS tests and two portable Python tests pass.
The macOS held-file test is added but skipped on this Windows host.
Real builder download/interception and Node-to-Python wiring pass.
ESLint, Ruff, syntax and new-file formatting checks pass.
2026-09-08 22:53:41 -04:00
ethernet
7d2b3b767d merge: integrate upstream/main into ethie/pm-clean
Merge upstream b1f003e186 while preserving PM runtime ownership and
Python 3.14 worker startup, Windows signing, and macOS wait recovery.

Keep retired runtime modules deleted. Port upstream updater preflight
checks into the checkout strategy and preserve live build logging.
Carry checkpoint filename handling and process recovery into the current
module layout. Regenerate locks and adapt incoming platform test markers.

Focused Python and JavaScript tests, desktop and root-test typechecks,
conflict-path lint checks, lock validation, and retired-import checks pass.
The full test suite and packaged release builds were not run.
2026-09-08 19:17:39 -04:00
ethernet
15eb3be676 fix(notarize): resume timed-out waits without resubmitting
An Apple status request can time out while notarization continues. Keep submit --wait as the normal path. Resume the same submission with notarytool wait only after the observed HTTP timeout.

Bound retries by one shared deadline and increase the existing macOS job and build-step limits. Unknown submission IDs and permanent failures still fail the build.

Focused notarization and macOS packaging tests pass, with lint, syntax, and workflow checks. Live Apple notarization remains unverified.
2026-09-08 16:25:42 -04:00
ethernet
54d6771ae9 fix(release): provision payload signing tools on cold caches
The afterPack payload signer runs before the Azure manager downloads its dlib. An empty cache therefore stopped the Windows build before signing.

Use the shared tool provisioner with the actual packager config. Keep the SDK, ATS dlib, and .NET runtime paired and remove the cache walkers.

Verified the cold-cache regression, native SDK execution, signature-cache contracts, and focused signing tests. ESLint passes. Full signed x64 release verification remains pending.
2026-09-08 16:04:58 -04:00
ethernet
d36562ac9f fix(release): provision Windows bundle tools on cache misses 2026-09-08 14:32:30 -04:00
ethernet
525ea2ad55 fix(build): honor the provisioned Python in desktop hooks 2026-09-08 13:07:04 -04:00
ethernet
c8aa5608c2 Merge pull request #101420 from ethernet8023/ethie/desktop-update-tests
test(install): cross-OS install/update E2E matrix (windows, macos, linux)
2026-09-08 10:30:44 -04:00
ethernet
67e5572ed1 fix(signing): share the verified runtime resolver for MSIX bundles
The envelope signer selected a cached .NET ZIP as DOTNET_ROOT. Its local
cache walker included archive and state files beside the extracted runtime.
The payload signer already filters these entries correctly.

Reuse the payload signer's runtime and dlib resolvers. Remove both duplicate
cache walkers and cover archive/state siblings in the shared resolver tests.

The actual bundle script failed before this change and passed afterward
with the same published packages and real Azure signing. The 4.9 GB bundle
passed native signature verification. All 34 focused tests pass. Remote
publication still needs a release run containing this fix.
2026-09-08 04:20:20 -04:00
ethernet
276174db7e fix(signing): verify embedded signatures behind Windows catalogs
PowerShell selects the Microsoft catalog signature for some payload DLLs.
That hides the valid Nous signature and rejects the signed payload cache.

For catalog matches, verify the primary embedded signature with signtool.
Require a trusted timestamp and the expected embedded publisher. Keep the
existing content binding and reject catalog-only trust.

The native regression rejects corrupt certificates and missing timestamps.
All 36 focused tests pass. Real Azure signing and duplicate restoration
pass with signer calls forbidden on the warm cache path. Full release
acceptance remains pending.
2026-09-08 01:31:02 -04:00
ethernet
e7af654b31 fix(signing): bound binary scanning during macOS packaging
The osx-sign dependency accepts protobuf-like file lengths beyond its
512-byte sample and keeps allocating after EOF. That crashes the native
macOS signing walk before codesign can finish.

Pin the scanner used by osx-sign to the fixed version already in the
build closure. Keep signature checks enabled. A real signing-walk test
reproduces the allocation failure before the override and passes after it.
2026-09-08 00:45:29 -04:00
ethernet
712734436e fix(pm): make bootstrap and bundle ownership explicit
Finish bootstrap uv before PM replaces its store entry. Keep failure
receipts stdlib-only and align the cryptography requirement and override
with the locked version.

Let bundle builders declare launch paths and update ownership. Remove
payload discovery, Store probing, and the unused develop command.
Derive Nix Python from the PM lock and share its provenance stamp.

Document setup, activation, optional dependencies, and distribution
ownership. Targeted Windows tests, relocated runtime launches, Electron
bundling, and bilingual docs builds pass. Native Nix and signed-package
acceptance remain CI gates.
2026-09-08 00:24:51 -04:00
ethernet
8c2e88aa4d fix(release): bind stable package versions and manifest origins
The Windows package version ignored an explicit stable tag.
Use the tag for sideload version derivation and reject candidate metadata
that does not match. Keep the separate Store version policy unchanged.

Restrict baseline manifests to the configured release origin and reject
cross-origin manifest responses. Cover the checks with real loopback HTTPS
and extend the version and empty-gate regression tests.

Document retries that reuse the original artifacts. Remove an unreachable
manifest check and an unused test import.

Targeted Python and JavaScript tests, Ruff, ESLint and the shared MSIX
module typecheck passed. Full signed native release acceptance was not run.
2026-09-07 14:59:29 -04:00
ethernet
6edee205bd perf(signing): reuse verified payload signatures by input hash
The Windows release signed 1,106 payload binaries for each of the bundled
and Store variants. Both passes repeated remote signing and timestamping.

Cache signed payload bytes by exact input SHA256 and signing policy.
Paths and release versions do not affect entry identity. Verify content
binding, publisher and timestamp before restoring a hit. Sign duplicate
inputs once and publish cache entries only after successful verification.
Keep product EXEs and package envelopes on the fresh signing path.

Persist the cache across release runs and test its native verification
in the Windows release lane. Targeted signing tests: 35 passed.
Real Azure signing of three mixed binaries took 9.6s cold and 1.8s warm.
Warm probes restored identical signed bytes with no signtool calls.
Full release performance and cache transfer overhead remain unverified.
2026-09-07 10:33:23 -04:00
ethernet
0de874c423 fix(notarize): retry ticket lookup after confirmed acceptance
Both Darwin release jobs failed with stapler error 65 and a missing
CloudKit record. The hook discarded the submission result, so the logs
could not distinguish rejection from delayed ticket delivery.

Require Accepted status and retrieve Apple's diagnostic log on failure.
Retry only the missing-ticket signature after acceptance. Share the path
between API-key and keychain-profile builds without resubmitting.

Targeted notarization and macOS packaging tests, lint and syntax checks
passed. Apple acceptance still requires a native signed build.
2026-09-07 10:04:35 -04:00
Teknium
80e585fad3 test: verify desktop task tails remain reachable in Chromium 2026-09-07 04:56:05 -07:00
yoniebans
32ed2061bb Merge upstream main (fc8d15d779): freshen before PR push 2026-09-07 10:15:01 +02:00
ethernet
925bcc0d22 test(install-e2e): wire native bundled update routes and receipts 2026-09-07 01:53:29 -04:00
ethernet
d6cd079966 fix(msix): reserve Store revision and correct App Installer descriptors 2026-09-07 01:39:40 -04:00
ethernet
8643f93384 fix(bundle): preserve staging guards and verify real uv sync 2026-09-06 22:38:58 -04:00
ethernet
1a09c42414 refactor(bundle): share Python payload assembly across desktop and Termux 2026-09-06 22:21:06 -04:00
ethernet
37650f810c merge: integrate desktop update acceptance tests
Preserve the PM runtime-repair module boundary. Port the incoming stderr-streaming fix without restoring the deleted managed_uv downloader. Targeted runtime/progress tests and root JS checks passed; desktop typechecks passed.
2026-09-06 21:59:45 -04:00
ethernet
da236308fd feat(desktop): wire macOS bundle updates and guarded feed publication
Route packaged macOS bundles and Light through the updater strategy.
Use electron-updater 6.8.9 and wait for native signature acceptance before
backend teardown. Keep checkout and Store ownership separate.

Share Darwin feed paths between packaging, runtime and publication.
Validate both native feeds, verify streamed artifact hashes, prevent
same-tag artifact replacement, and conditionally update the channel
pointer. Protect live feed references during canary retention.

Use one notarization owner. Require publishing credentials and validate
the stapled app. Keep Windows, Linux and Termux jobs unchanged.

Verified with updater/feed unit and transport tests, release-helper tests,
desktop typechecks, the desktop JS build, and workflow lint. No E2E,
native macOS install, release dispatch or public publication was run.
2026-09-06 21:27:58 -04:00
ethernet
0ea7a75f41 fix(desktop): open registered App Installer descriptors without disabled protocol
Microsoft disables ms-appinstaller by default. Download a bounded local descriptor before teardown and open its file association. Read the registered source URI from Windows when no feed override is configured. Remove the invalid default URL and share checker parsing.

Verified real loopback download, size/error boundaries, apply ordering, 35 Electron tests, Electron typecheck, and 8 Python projection tests. Packaged update acceptance remains separate.
2026-09-06 13:49:29 -04:00
ethernet
ce49cdbc59 merge: reconcile upstream main with pm audit closeout
Merge upstream 5e645791ac.

Retain the PM feature-flag owner and add upstream connection options.
Use the deny-only window-open policy while trusted external links keep
the existing IPC path. Keep both session-import and external-link copy.
Preserve captured timeout output when adding terminal yield handoff.
Quickstart tests patch the explicit upstream model-assignment owner.
Migrate incoming legacy OS markers to the branch's platforms gate.

Desktop renderer and Electron typechecks passed. Targeted Electron tests
passed (42 tests), Python conflict checks passed (26 tests, 3 skips),
and the plugin-compat import checker passed. CI owns the broad merge gate.
2026-09-06 13:17:07 -04:00
ethernet
3c08d16ba7 fix(pm): close runtime publication and updater audit gaps
Dependency publication now recovers interrupted config/facts changes before
activation and leases live generations during collection. Receipts retain
update correlation and failed steps across nested command boundaries.
Doctor and desktop surfaces report those failures through shared owners.

Move checkout updates out of the desktop facade. Stage a detached Windows
relaunch waiter before shutdown, with bounded handshake and process-birth
checks. Keep packaged lifecycle tests isolated from the installed app.

Native verification exposed two production races: cron maintenance imported
the interactive CLI and rewrote TERMINAL_CWD, and install-ID reads collided
with first publication. Use the existing owners and locks. Plugin checks
now run at startup and each due-gated housekeeping tick, not after 60 ticks.

Share updater-test mutation boundaries and remove collection-root fixtures.
Separate cold MCP startup from command latency and give the real HTTP drip
test enough time to reach body handling.

Root npm check passed, including packaging. The fixed-tree Windows Python
run reported 44557 passed, one failed, and 1404 skipped, plus one retry-only
HTTP test. Those final failures now pass in a 35-test bounded batch. A real
isolated gateway wrote startup and periodic plugin-check receipts.

Full final-tree CI, bundled Sandbox deployment, and actual App Installer
relaunch remain unverified. docs/pm-audit-status.md records these limits.
2026-09-06 11:45:41 -04:00
Axl Ibiza, MBA
cacf1218d7 fix(desktop-update): acknowledge Windows progress completion
A delayed browser could miss the 900ms terminal event and spin forever after the updater exited. Retain terminal delivery until the page acknowledges it, bound unavailable-client teardown and failed requests, and preserve a truthful final display.

Fixes #103747. Builds on OutThisLife and Teknium detached handoff work in #83634 and the #75895 quiet-window design. Continues Axl Ibiza Windows update investigation (#60233, #94107, #100763), including source/review contributions carried by merged #93353 and #85170. Existing #102373, #103140, #95719, #97299 and #103632 retain their separate scopes.
2026-09-06 07:19:21 -07:00
ethernet
92686159d1 fix(pm): integrate audited runtime and lifecycle repairs
Prepare dependency generations before selecting them. Keep shipped tool
bytes separate from writable additions, and store facts beside their entries.
Validate proposed plugin sets before config publication. Restore the previous
config if the facts write fails.

Consolidate duplicate updater, backup, setup, and voice helpers. Repair
launcher selection, dependency consumers, download ownership, update feeds,
and native Windows process and file handling.

Verification: 206 changed/prior-failing Python files reported 4630 passed,
one failed, and 330 skipped. Fix the remaining Hindsight fixture boundary.
The final targeted rerun reported 234 passed and two skipped. The store
review regression batch reported 83 passed and one skipped. Desktop
TypeScript checks, 56 selected Electron tests, 24 release tests, and the
removed-import/compatibility guards passed.

This is an integration checkpoint, not full audit acceptance. The complete
Python suite has not run on this fixed tree. Crash-atomic plugin publication,
generation cleanup, receipt correlation, and packaged lifecycle acceptance
remain open in docs/pm-audit-status.md.
2026-09-05 22:36:48 -04:00
ethernet
642579db60 Merge remote-tracking branch 'upstream/main' into ethie/pm-clean
# Conflicts:
#	.github/actions/detect-changes/action.yml
#	.github/workflows/ci.yaml
#	.github/workflows/tests-os.yml
#	agent/prompt_builder.py
#	agent/ssl_verify.py
#	agent/subdirectory_hints.py
#	apps/desktop/electron/main.ts
#	apps/desktop/electron/preload.ts
#	apps/desktop/src/app/settings/about-settings.tsx
#	apps/desktop/src/global.d.ts
#	apps/desktop/src/i18n/ar.ts
#	apps/desktop/src/store/updates.ts
#	cron/suggestions.py
#	gateway/channel_directory.py
#	hermes_cli/config.py
#	hermes_cli/doctor.py
#	hermes_cli/linux_desktop_entry.py
#	hermes_cli/main.py
#	hermes_cli/web_routers/profiles.py
#	hermes_constants.py
#	plugins/platforms/photon/adapter.py
#	scripts/ci/classify_changes.py
#	scripts/install.ps1
#	tests/agent/test_relay_runtime_plugins.py
#	tests/ci/test_classify_changes.py
#	tests/hermes_cli/test_gui_command.py
#	tests/hermes_cli/test_linux_desktop_entry.py
#	tests/hermes_cli/test_update_fleet_restart_pending.py
#	tests/state/test_fts_runtime_rebuild.py
#	tests/tools/test_lazy_deps.py
#	tests/tools/test_macos_protected_search.py
#	tools/browser_tool.py
#	tools/file_operations.py
#	tools/lazy_deps.py
#	tools/mcp_tool.py
#	tools/working_diff.py
#	uv.lock
2026-09-04 03:18:16 -04:00
ethernet
5069aedb75 feat(desktop): MSIX registers HermesGateway — desktop6:Service manifest extension
Task 2 of the gateway-as-MSIX-service plan (settled: user-context,
1903 floor, config-only demand-start):

- before-build.mjs serviceExtensions(): the desktop6:Service fragment
  — Executable = the payload launcher hermes.exe (the same distlib
  PE serving the AppExecutionAliases; gateway run --service is the
  SCM frontend — NO shim binary), Name=HermesGateway,
  StartupType=demand (config-only posture: arrives stopped;
  `hermes gateway service on` flips to automatic-at-logon),
  StartAccount omitted (desktop6 default = installing user's context;
  localSystem explicitly rejected). light + store variants render
  service-less (store policy is the plan's open risk item). Rides
  the existing generated build/msix-extensions.xml via
  customExtensionsPath — same mechanism as the aliases + copilot-key
  fragments.
- gen-msix-manifest.mjs: minVersion 10.0.17763 → 10.0.18362 (Win10
  1903, the desktop6:Service requirement; settled support-matrix
  bump — 1809 is a 2018 OS; rides the release notes).
- Verified: the REAL rendered manifest carries the service block
  (gen-msix-manifest bundled x64 → desktop6:Service
  Name=HermesGateway Executable=...hermes.exe); the staged fragment
  regenerates with it (caught + fixed a serviceFragment→
  serviceExtensions name bug live via the real beforeBuild import).
  A flat-dir makeappx probe fails identically WITH and WITHOUT the
  fragment (the probe harness lacks signing identity) — the honest
  full validation is the win32 CI lane's real signed pack.
- tests: 4 serviceExtensions contract tests (category/name/exe/
  demand-start/one-block/xmlns-root/light-store-empty/name-prop)
  in cli-launchers.test.mjs — 15/15 pass; full scripts/ suite: my
  files green (1 pre-existing darwin-staging failure is the sibling
  agent's uncommitted territory).
2026-09-03 21:31:01 -04:00
ethernet
7707d9c32c fix(desktop): batch-sign must skip the shipped uv-cache tree
The pm bundle deliberately ships uv-cache/ (warm venv rebuilds) and the
arch audit already exempts it — but batchSignAppTree enumerated and
signed every .exe/.dll under it. That wastes hundreds of Azure sign
round-trips on inert sdist/archive artifacts and FAILS when the cache
holds files that were removed between pm bundle staging and the afterPack
walk ("SignTool Error: File not found" — reproduced on a local win32-arm64
build).

Skip agent-payload/uv-cache/ in the batchSignAppTree enumeration (same
exemption class as the audit), pinned by a test asserting a uv-cache exe
is excluded while the rest of the tree is still signed.

Verified: 90 batch-sign tests pass.
2026-09-03 11:59:33 -04:00
ethernet
124b8a1bac fix(desktop): exempt the shipped uv-cache from the bundle arch audit
A local win32-arm64 build exposed it: audit-bundle-arch fails because
the payload deliberately ships uv-cache/ (pm bundle copies it for warm
rebuilds of the mutable venv) and that cache holds sdists/archives uv
built for ANY arch — x64/ia32 PEs on an arm64 payload. They are inert
cache bytes, never loaded at runtime, the same class as the fetch-*
prune, but the audit treated them as wrong-arch binaries.

Exempt agent-payload/uv-cache/ with a comment tying it to the pm bundle
behavior, and pin it with a test (exempt inside the cache, still audited
outside).

Verified: local arm64 build + audit green (2019 native binaries, all
arm64, 615 exempt stubs); 160 audit tests pass.
2026-09-03 11:00:45 -04:00
yoniebans
558d76c401 Merge upstream main (afc3d9d34c): refresh before review
One conflict: upstream 6e7c7c7da9 replaced bot-mode-closed-chat-stays-closed.spec.ts with bot-mode-row-click-mirrors-registry.spec.ts while our side had rewired its mock-server import. Kept upstream's replacement and rewired the three new specs importing ./mock-server to the consolidated tests-js copy (symbols verified present).
2026-09-02 17:53:49 +02:00
Teknium
fdb2e10a8e fix(desktop): refuse the build when ANY declared non-optional dep is missing
Widen the salvaged guard from a hand-maintained four-package floor to the
class it stands for: every `dependencies` + `devDependencies` entry in the
desktop workspace manifest. Live probe on this box: a tree holding vite,
katex, electron and electron-builder but missing `@rolldown/plugin-babel`
still passed the floor-only guard, and `vite build` died loading
`vite.config.ts` after `prebuild` had already run. The floor stays as an
unconditional fallback for an unreadable manifest; optionalDependencies
are skipped because npm legitimately omits them (get-windows).

Five new vitest cases (12 total); the two class tests fail when the
manifest union is removed. Refs #86443.
2026-09-02 00:07:58 -07:00
kshitijk4poor
ae0418599d chore: export BUILD_CRITICAL_PACKAGES for the test, drop dead default export
Follow-up to the salvaged #87980: the test kept its own copy of the
build-critical package list (drift hazard) and the module's default
export had no consumer.
2026-09-02 00:07:58 -07:00
Jack Lau
15e74fa625 fix(desktop): guard the whole build-critical dep set, before clean
Refs #86443

assert-root-install.mjs exists to turn an incomplete root install into one
actionable line instead of a failure deep inside the build. It only ever
checked that vite resolved, so an install covering part of the workspace
graph passed the guard and died later on something else. That is the shape
reported in #86443: the updater's npm install brought in 521 of the 769
packages a full install gives, root node_modules had vite but not katex, and
the build failed on an unresolved katex/dist/katex.min.css with nothing
pointing at the install as the cause. apps/desktop/src/styles.css imports
that stylesheet, so katex is as load-bearing for the renderer bundle as vite
is, and electron / electron-builder are the same for packaging.

Check all four and name every missing one, so a partial install is reported
once and completely rather than one package per build attempt.

Resolution walks node_modules upward the way Node's own lookup does, rather
than going through require.resolve: a package whose exports map does not
expose ./package.json is not resolvable by path even when correctly
installed, and that must not read as missing. It also keeps a dependency
that landed in the app workspace instead of the hoisted root passing.

The guard now runs from prebuild, ahead of npm run clean, so a tree that
cannot build is rejected before the build deletes its own outputs. On this
checkout clean removes build/electron-types and the tsbuildinfo files, not
release/, so this ordering is not by itself what saves a packaged app; it is
the narrow correctness point that a doomed build should not destroy anything
first. build keeps its own call for anyone invoking the build steps directly,
and the check is pure filesystem lookups, so running it twice costs nothing.

The check is extracted as a pure checkRootInstall() returning {ok, error},
matching assert-dist-built.mjs, so it is unit testable without spawning a
process.
2026-09-02 00:07:58 -07:00
ethernet
a9793b3ea6 refactor(release): rename the nightly release channel to canary
The fast-moving desktop prerelease channel is now "canary" everywhere:
the tag shape (vX.Y.Z-canary.<ts>), the electron-updater/R2 feed dirs
(canary.yml / releases/<os>/canary/), the update-channel consts and CLI
choices, the MSIX build-number derivation, the App Installer channel
paths, and the Windows Store flight var (MS_STORE_CANARY_FLIGHT_ID).

Also renames the scheduled workflow to canary-release.yml and the
release test file to test_release_canary.py, and flips the CLI flags
(--canary / --prune-canaries / prune-canaries subcommand).

Unrelated "nightly" mentions are untouched: Brave's own browser channel
(browser_connect), cron scheduling prose (README, i18n, cron/browser/
kanban docs, zh-Hans), upstream skill docs (comfyui/unsloth/torchtitan),
evals fixtures, Node's node-nightly prereleases, and cron job names in
gateway tests.

Note: MS_STORE_NIGHTLY_FLIGHT_ID was renamed to MS_STORE_CANARY_FLIGHT_ID
in the workflow — the matching repo/org variable on GitHub must be
renamed in repo settings for the Store flight ring to keep working.
2026-09-01 22:15:17 -04:00
ethernet
c35a6d65c9 merge: upstream/main into ethie/pm-clean
Bring in 597 upstream commits while preserving the branch's intentional
divergence (pm store, MSIX desktop, Termux removal).

Resolution notes:
- local_runtime/local-models cluster (30 both-added files): took upstream's
  evolved version — our side was a stale feat-merge snapshot with zero
  post-merge commits, and our non-conflicted importers were verified against
  upstream's exports.
- install scripts (install.ps1/install.sh/setup-hermes.sh): kept our staged
  pm-store bootstrappers (upstream still ships the old monolithic installer).
- Deleted-by-us files (node-bootstrap.sh, install_ps1 tests, termux.md):
  kept deleted — the pm store replaced that machinery.
- runtime_repair.py: kept ours (pm-based), restored upstream's managed_uv.py
  which surviving upstream files still import.
- Core files (run_agent, update_cmd, main.py, hermes_constants, estop,
  aux_client, browser_tool, desktop entry, config_defaults): per-file merges
  combining both sides' features (upstream fleet-wide estop, PID-identity
  daemon kill, editable-install guard; our utf-8-sig sweep, stable channel,
  PYTHONPATH desktop entry).
- Desktop/i18n: took upstream's flag-gated local-models components; merged
  both sides' i18n keys; merged run-electron-builder.mjs and notifications
  tests keeping both sides' tests.
- pyproject.toml: upstream's expanded exclude-newer list + our
  google-cloud-pubsub entry; uv.lock regenerated from the merged pyproject.
2026-09-01 20:22:04 -04:00
ethernet
380d0c5a8d fix(desktop): route App Installer sideloads to the app-installer updater
process.windowsStore is true for any MSIX package — App Installer
sideloads included — not just Microsoft Store deployments. The updater
mechanism resolver keyed on it, so out-of-store installs resolved to
external and showed the static 'bundled install' line instead of the
app-installer check/apply/relaunch flow.

The store-vs-sideload distinction is a build-time fact. Bake it into
the install stamp (HERMES_DESKTOP_VARIANT=store vs bundled) and have
isWindowsStore() trust the stamp when present, falling back to the
Electron flag only for dev runs / legacy stamps.

- write-build-stamp.mjs: buildStampPayload() emits payload/store/
  distribution/updateMechanism/tag for staged desktop builds; dev and
  legacy builds keep the old 5-field shape
- install-stamp.ts: InstallStamp gains store?: boolean
- main.ts: isWindowsStore() reads INSTALL_STAMP.store; loadInstallStamp
  mirrors the field from disk
- tests: buildStampPayload variant matrix (bundled/store/light/legacy)
2026-09-01 15:34:35 -04: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
9ed9cc8350 fix(desktop): ship chromium in the payload — drop stripFetchCache
stripFetchCache did two things: dropped fetch-<sha> download-cache dirs and
dropped the chromium-* store entries. Both were wrong for the current flow:

- fetch-* is now pruned by pm gc during the bundle (the payload and CI
  cache already ship only live store entries), so the function was belt
  and suspenders.
- chromium-* was DROPPED while signNestedChromium (the autosign pass
  right after it) was trying to SIGN it — the strip ran first, so the
  signing found nothing. Chrome's Mach-O stayed unsigned, Apple's notary
  rejected the app, and the shipped mac bundle had no browser at all.

Rip stripFetchCache out entirely: the payload keeps chromium, and the
darwin autosign pass now has the chromium trees to sign (--deep over the
.app, per-file over loose Mach-O) so notary accepts them. The Windows
payload's chromium binaries are covered by the batch Authenticode pass.
2026-09-01 12:26:35 -04:00
ethernet
deb620c652 fix(desktop): parallelize payload signing and split the timestamp pass
The payload batch signs ~64k PEs serially: each chunk is a fresh
signtool child that cold-initializes the .NET dlib, auths against Azure,
and does a separate RFC3161 round-trip per file. Both Azure and the
timestamp server are per-file network waits, so the whole tree was one
long serial chain.

Two changes:
- sign chunks concurrently, capped at DEFAULT_CONCURRENCY (4) via a
  small worker pool — N children multiply throughput ~Nx.
- split signing from timestamping: the sign pass is Azure-only (no
  /tr,/td), then a second pure-RFC3161 timestamp pass (no /dlib,/dmdf)
  retries per chunk. A flaky timestamp server can no longer hold signed
  files hostage or force a full re-sign; the retry beats a rebuild.

The product exe and .msix/.msixbundle keep their existing per-file
signing paths untouched. afterPack now awaits the batch.
2026-09-01 11:24:27 -04:00
ethernet
029099b249 fix msix bundle signging with better timestamp sig 2026-09-01 10:32:12 -04:00
ethernet
47f4ab3a17 feat(desktop): bundle, publish, and update the desktop app as MSIX
Wire the desktop app onto the pm store for real distribution:
- MSIX bundle: electron-builder config, appx assets, manifest, copilot
  key + deep-link routing, App Installer + Windows Store variant
  (sign only the msix; inner binaries covered by the package block map)
- Rust CLI shim (apps/desktop/shim) — bundled builds run from the store
  python + shim, never the venv; payload symlinks relativized so the
  relocatable venv survives relocation
- Cloudflare R2 release pipeline: publish binaries + update feeds,
  nightly channels/tags, stamp-first version resolution
- Update system: gate, uninstall steward, boot bootstrap, release
  channels, update receipts
- install.ps1 reduced to a 361-line stage-protocol bootstrapper (heavy
  deps are pm's job); darwin updater + update-channel mirror ripped
- doctor: main's re-landed TCC anchor kept, termux branches removed

Rebuilt from ethie/pm onto the pm-store stack. 22 hot files hand-merged;
uv.lock + package-lock.json keep main's newer dep tree; test_engines
reads the pm/lock.json pin; lazy_deps.py deleted (all 222 importers
migrated to pm in the foundation commit).
2026-08-31 18:00:48 -04:00
ethernet
3d12e86ef1 feat(pm): unified package manager — pm store foundation
Introduce the pm store: a unified, hash-verified package store that
replaces lazy_deps and the old installer's ad-hoc tool downloads.
Store tools are provisioned on PATH (ffmpeg, node/npm via pinned uv),
with a resumable 8-way downloader, verify() returning failure reasons,
and adopt() made EPERM-safe. chromium ships in the payload for every
target. The 3600-line install.sh is replaced by a staged bootstrapper
(heavy deps are pm's job after this); setup-hermes.sh, Dockerfile and
nix pin tables are rewired onto the store. Old install-script tests,
lazy_deps/managed_uv/build_info, and the ps1/bash installer test
batteries are removed with the machinery they tested.

Rebuilt from ethie/pm onto upstream/main (ac6c8028e0) after the
utf-8-sig sweep. 16 hot files (main also churned them) hand-merged:
platform adapters, main.py, electron/main.ts, tui_gateway/server.py,
cua_backend, installer-tests workflow, install.sh (full rewrite),
setup-hermes.sh, plugins doc.
2026-08-31 18:00:48 -04:00
Finn763
38b93e0abe fix(desktop): keep @tanstack/react-query in one runtime chunk (#95560)
The packaged app crashed at launch with 'No QueryClient set, use
QueryClientProvider to set one': useQuery in a lazy chunk (session-list-density)
read a second @tanstack/react-query runtime whose QueryClientContext was never
populated by the entry's QueryClientProvider. The source tree was correct — the
duplication happened at build time, because react-query was the one
context-bearing runtime not pinned to a shared vendor chunk, and rolldown's
merge heuristics inline the spare copy into a lazy chunk depending on toolchain
version.

- vite.config.ts: add @tanstack/react-query to the vendor-react
  advancedChunks group + dev dedupe list, mirroring the react-router fix.
- assert-dist-built.mjs: fail the build when the 'No QueryClient set'
  invariant appears in more than one JS asset (launch-smoke guard).
- assert-dist-built.test.mjs: unit tests for the new invariant check.
- launch-packaged-app.spec.ts: e2e smoke test asserting the packaged app
  boots to real UI, not the QueryClient error boundary.
2026-08-31 10:10:35 -07:00
James Matheson
3738b88002 fix(desktop): pin --publish never in run-electron-builder.mjs (salvaged from #87937) 2026-08-31 10:07:51 -07:00
ethernet
10f99bc15e ci: run the work lanes on larger runners and merge the split jobs
Every Linux lane that does real work ran on a 4-core `ubuntu-latest`. The
Python suite and the JS checks were split into many small jobs to make that
size usable. Each split job repeated the full setup. In most of the JS jobs
the repeated setup cost more than the work.

The work lanes move to larger runners. Then the splits that existed only to
make small runners usable go away.

Python tests: 12 slices become 1 job on a 96-core runner. Slicing cost a
matrix job, a duration cache, a per-slice artifact and a merge job. 96 cores
clear the floor that the slowest single test file sets, which is about 82s. A
second slice divides work that is already at that floor, and adds a second
setup. Duration data from run 32522943054 gives the numbers behind this: 3178
files, 11645s in series.

The worker count is explicit, because `run_tests.sh` defaults to twice the
core count. A later commit sets it from a measurement on this hardware.

JS checks: 14 jobs become 1. The matrix paid about 371s of repeated setup to
spread about 612s of work. One larger runner installs one time. The three UI
shard scripts and `run-ui-shard.mjs` are therefore removed, because the
unsharded `test:ui` covers the same tests.

The unit of parallel work inside that job is a CHECK, and not a workspace.
apps/desktop is most of the payload, and its own `check` is a serial && chain.
A spread across workspaces alone therefore leaves that chain as the long pole.
A package that declares `check:*` sub-scripts gives one unit for each
sub-script. That is the same selection rule the matrix used.

The loop lives in `.github/scripts/run-workspace-checks.mjs`, so the same
sequence runs on a laptop. It runs 11 units together, buffers the output of
each one, and fails at the end with the full list. Children that share one
stdout interleave their lines and make a failure hard to read.
`npm run --ws check` stops at the first workspace that fails.

`check:test:plugins` joins the desktop `check` script. The matrix prefers
`check:*` sub-scripts over the plain `check` script, so `check:test:plugins`
ran only as its own leg. Without this change the merge drops that suite and
the job stays green.

node_modules is cached on the lockfile, and `npm ci` is skipped on an exact
hit. The `cache: npm` option of `setup-node` caches only the ~/.npm tarball
cache, which leaves the extract and the postinstalls to pay again.

The arm64 image build stays on a native arm64 runner. A build of linux/arm64
on an x64 host uses emulation.

The docker test lane caps its workers at the core count. Each of those tests
drives a container, so the docker daemon sets the limit and not the processor.

`.github/actionlint.yaml` declares the runner labels. actionlint knows the
GitHub-hosted labels only, and an undeclared label reads as an error that
hides the real findings.

The `detect` job checks out one file through a sparse checkout, and its
timeout drops to 1 minute. It reads
`scripts/ci/classify_changes.py` and nothing else.

Verification:
- actionlint reports 9 findings across all workflows. An unmodified HEAD with
  the same config reports the same 9. This change adds none.
- A wrong label still fails. actionlint reports `ubuntu-latest-32-cor` and
  `ubuntu-latest-32-arm-cores`.
- Every changed workflow parses, and `name` parses as a string.
- A replay of the `save-durations` merge step against a three-artifact layout
  returns all 3178 entries.
- An expansion of the npm script graph gives the same leaf commands for the
  parallel units and for a plain `npm run check`, in both directions. Against
  the 13-leg matrix the count is 13 to 11, and the whole difference is the
  three UI shards that collapse into one unsharded `check:test:ui`.
- `--list` reports the 11 units, and a full local run completes and reports
  the time of each unit.
- The runner labels cannot be verified here. The first real run is the test.
2026-08-22 02:25:12 -04:00
Royalaid
73dcd75afe perf(desktop): harden the spinner strip and tighten status invalidation
Review follow-ups on the compositor spinner and the invalidation scoping.

Spinner CSS:
- Clip each frame to its own box. Braille renders from a system fallback
  face (JetBrains Mono has no U+2800 block), whose metrics are not
  guaranteed to fit the 1em frame, so neighbouring ink could bleed into
  the viewport.
- Name descendants explicitly in the selection guard. The competing
  `[data-selectable-text='true'] *` rule has the same (0,1,0)
  specificity, so relying on inheritance made the winner depend on
  stylesheet order.
- Scope the compositor promotion to spinners that are actually running.
  A permanently promoted layer per parked spinner is pure memory at
  fan-out breadth, where many sit mounted and paused at once.
- Give every var() the braille default as its fallback, so a missing
  custom property degrades to a working spinner rather than an invalid
  declaration.

Spinner component: replace the bare `as CSSProperties` cast on the inline
style with an exported GlyphSpinnerVars contract, so a typo in a custom
property name is a compile error rather than a silently dead declaration.

Assistant message:
- Render the inter-agent collapse as a CHILD of the normal body instead
  of a competing root. The settled case previously returned a different
  element type than the running case, so settling unmounted the whole row
  and mounted a fresh one — discarding the DOM the scroll anchor held.
  One component, one root, children vary; the truth table is unchanged,
  including the collapsed row carrying no tapback listener.
- Collapse AssistantStatusSlot's separate subscriptions into one selector
  returning a stable string. The inputs always move together on a status
  flip, so reading them separately just multiplied the wake-ups.
- Give StreamingMarker a stable `data-slot` and assert on that rather
  than on `span.hidden`.

Repro script: count settled rows by subtracting streaming markers from
message roots instead of `:not(:has(...))`. The selector walked every
row's subtree on each evaluation, inside the very latency window the
probe measures.

Comments: drop the stale translateY(-100%) description, name both pause
triggers, replace hard-coded line-number citations with selector/symbol
ones, note that only the primary window arms the renderer-pause
attribute, and move the forensic trace numbers out of source comments
into the PR.

Delete the three tests that asserted on stylesheet TEXT. AGENTS.md bans
reading source in tests outright, and they demonstrated exactly why: a
var()-fallback edit that changed no rendered pixel broke one of them.
Replacements that exercise the CSS in a real browser follow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QwTc9XqUjhbay446VjugHZ
2026-08-21 04:23:41 -07:00