Commit Graph

182 Commits

Author SHA1 Message Date
ethernet
35ea96cd33 fix(desktop): import rmSync in stage-native-deps
prepareDesktopNativeDependencies called a bare `rmSync` that the file never
imported (every sibling here uses `fs.rmSync`), so `npm run build` in
apps/desktop died with "ReferenceError: rmSync is not defined" at
stage-native-deps.mjs:696. That fails EVERY desktop build, which is why the
install e2e's products stage reported "[hermes] app products or command
publication failed" and install.sh exited 1 at HEAD.

Proof: the covering native case in tests-js/desktop-builder.test.mjs is red on
base with exactly that ReferenceError and green with the import.
2026-09-17 16:58:26 -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
4146fcbd7a test(desktop): drop the source-text cpSync/rmSync guard
Root AGENTS.md bans tests that read source files and regex-match call sites;
the behavioral accented-tree restage test stays and is the invariant.
2026-09-17 00:26:32 -07:00
teknium1
afb9243f9c fix(desktop): widen libuv-only staging to get-windows and the rollback .bak wipe
The salvaged commits drop the cpSync/rmSync imports, but stageGetWindowsInto
landed on main after the PR was opened and still called both, so the module
threw ReferenceError on every platform (7 vitest failures on the cherry-picked
head). Route its six sites through copyFileSync/removeDirSync so get-windows
staging survives the same non-ASCII profile paths as node-pty.

preserveRollbackBackup had the same rmSync no-op as cleanStaleAppOutDir: a
surviving .bak makes renameSync fail, so the previous working build gets wiped
instead of kept as rollback material. Same existsSync -> removeDirSync fallback.

Earlier fixes for the same crash: #60480 (@liuhao1024), #61832 (@danilofalcao),
#76211, #103458, #109273, #111590.
Co-authored-by: liuhao1024 <liuhao1024@users.noreply.github.com>
Co-authored-by: danilofalcao <danilofalcao@users.noreply.github.com>
2026-09-17 00:26:32 -07:00
Kósa
8317fc8f4e fix(desktop): harden before-pack cleanup against the same non-ASCII rmSync no-op
Review follow-ups to the previous commit:

- before-pack.mjs: cleanStaleAppOutDir kept the retrying rmSync (its
  EBUSY resilience is worth keeping) but now verifies the tree is gone
  and falls back to the libuv-backed removeDirSync when the native
  rmSync silently no-ops on a non-ASCII path — previously it logged
  'removed stale unpacked dir' while the stale tree survived.
- removeDirSync: export it for reuse; handle a plain file or symlink at
  the path (rm -rf semantics) instead of throwing ENOTDIR, and tolerate
  broken symlinks via lstat.
- Add a source-level guard test: repo CI runs on Linux where the native
  implementations happen to work, so the accented staging test cannot
  catch a cpSync/rmSync reintroduction there.
- Link the upstream Node issues in the banner (nodejs/node#61878, fixed
  in v24.15.0; nodejs/node#56049, fixed in v24.13.1) and stop
  attributing the native rewrite to Node 24 — only the observation was
  on v24.11.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-17 00:26:32 -07:00
Kósa
2d88b9b4df fix(desktop): stage node-pty without fs.cpSync/fs.rmSync (non-ASCII Windows paths)
Node 24's native rewrite of fs.cpSync/fs.rmSync mishandles non-ASCII
Windows paths (observed on v24.11.1 with an accented Windows user name,
i.e. the default %LOCALAPPDATA%\hermes install home):

- recursive cpSync fails with EIO "Access is denied" (errno 5) or
  hard-crashes the process (0xC0000409),
- cpSync over an existing file fails with a bogus errno-0
  'unlink' / "The operation completed successfully" error,
- rmSync({recursive, force}) silently deletes nothing, so the
  half-staged dist/node_modules/node-pty tree poisons every retry.

In practice this bricked the desktop build (and thus install/update)
for every Windows user whose profile path contains accented characters:
stage-native-deps.mjs died copying the conpty prebuild, and each rerun
failed earlier on the stale tree the silent rmSync left behind.

Replace all cpSync/rmSync uses with libuv-backed primitives that handle
these paths correctly on the same node.exe: copyFileSync for single
files, a manual mkdirSync+readdirSync+copyFileSync walk for directory
copies, and a manual unlinkSync/rmdirSync walk (verified with existsSync
afterwards, failing loudly instead of silently) for the dest cleanup.

Add a regression test that stages a fake node-pty tree - including a
nested conpty/ payload - into an accented src/dest path twice, so the
restage exercises the delete-then-recopy path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-17 00:26:32 -07:00
teknium1
3dcf0d49ad fix(install): let Rolldown name the missing binding; repair on every OS
The first cut derived the package from `binding-${platform}-${arch}` with an
exact-suffix match, which never matches Windows (`-msvc`) or Linux
(`-gnu`/`-musl`) names, so the repair only ever worked on macOS and
install.sh had to gate it there. Rolldown's own loader already resolves
platform, arch and libc and prints the exact `@rolldown/binding-*` it wanted
in its error chain; parse that instead and drop the gate. Also spawn npm
through a shell on Windows (Node refuses to spawn npm.cmd directly) and trim
the tests to the two invariants (no-op when it loads; installs exactly what
the loader asked for, then re-probes).
2026-09-17 00:25:57 -07:00
Gille
f67cad172e test(desktop): register Rolldown repair tests with Vitest 2026-09-17 00:25:57 -07:00
Gille
ae9f42accf fix(install): repair missing Rolldown bindings 2026-09-17 00:25:57 -07:00
teknium1
f501ebcf1e fix(desktop): size the rcedit retry to the observed lock window and retry any failure
The field report saw the commit still failing 5 s after a first attempt,
so 100/300 ms could exhaust before the scanner released the exe. Use
500 ms / 1 s / 2 s (four attempts, 3.5 s worst case) and retry every
rcedit failure instead of one error-message shape: a held handle can also
surface as a load/open error, and a permanent failure only costs 3.5 s
before after-pack.mjs swallows it.
2026-09-16 17:46:56 -07:00
KoNit-K
8ca920b52a fix(desktop): retry transient rcedit commit failures
Windows desktop rebuilds intermittently hit rcedit's `Fatal error: Unable
to commit changes` while a real-time file scanner holds the freshly packed
Hermes.exe; `stampExeIdentity` called rcedit exactly once, so the stamp
failed on the first lock. Retry the commit with bounded backoff before
surfacing the original error.

Trimmed from #112552: the after-pack.mjs injection seam and its test —
the hook's swallow-and-warn is pre-existing behaviour on main and needs no
change (the reporter's "Could not install the rebuilt desktop app" comes
from hermes_cli/main_desktop.py::_swap_staged_desktop_app's directory
rename, not from the stamp).
2026-09-16 17:46:56 -07:00
brooklyn!
76a4d3b62a feat(desktop): detect the dual-Command gesture on macOS 2026-09-16 19:13:25 -05:00
brooklyn!
691228447d perf(desktop): observe live workspace frame pacing without synthetic sessions 2026-09-16 06:20:40 -05: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
ethie
3bbb10c250 fix(desktop): sign Mach-O members inside payload wheel zips before notarization
Apple's notary extracts every zip in the submitted archive and validates
each Mach-O; the notarization failure (err 4000) traced to uv-cache wheel
zips whose native members were never signed — electron-osx-sign reaches
only the extracted archive buckets, not zip members.

signWheelZipMembers (afterPack, darwin) walks the payload's uv-cache
wheel zips, signs .so/.dylib members with the same Developer ID identity
and keychain the chromium signer resolves, repacks, then re-extracts and
runs codesign --verify --strict on every signed member — the same
predicate the notary applies — failing the build loudly if a host's zip
semantics ever drop the embedded signature.

Proven on a real macOS 26 host: codesign may write signatures to extended
attributes for ad-hoc/generic binaries, and plain zip repack silently
drops xattrs; the verify gate converts that host behavior from a silent
notary rejection into a same-run build failure.
2026-09-14 22:22:29 -04:00
ethernet
936fb81005 fix(desktop): cap MSIX copilot extension Id at the 39-char manifest limit
Channel identities splice a 16-hex token into artifactNamePascal
(HermesChannel<token>), so Id="${artifactNamePascal}CopilotKeyProvider"
reached 47 chars and makeappx rejects the manifest with an opaque
maxLength 0x80080204. The Id only discriminates within the package;
DisplayName already carries the variant name, so pin the literal.
2026-09-14 20:30:57 -04:00
ethernet
5b30ae9991 refactor(desktop): delete cross-package retirement migration machinery (round 3 slice 3) 2026-09-14 12:59:17 -04:00
ethernet
31a26c5896 feat(release): shared assembly, scoped publication, harness fold (rounds 2 cont.)
Checkpoint remainder: the unstaged half of the reduction work.

- channel_artifacts.py becomes the single native manifest/feed writer;
  release_artifacts.py and channel release paths delegate to it
- r2_scope fork isolation binds namespace before credential access;
  disposable runs refuse unscoped requests
- canary bootstrap verifies canary's own promoted outputs
- retained-link inventory + empty-directory preservation in strict
  migration snapshots; connection-collision resolution and URL-credential
  rejection in retirement connection adoption
- harness controller test relocated to tests/scripts/ (canonical pytest
  name); channel-retirement install-e2e jobs wired
- S/T receiver candidates build with stable update ownership
- test fixture updates across release/source-channel suites
2026-09-14 10:26:48 -04:00
ethernet
b37acb8389 feat(release): dynamic R2-owned channels and preview retirement (rounds 1-2)
Checkpoint before round-3 reduction (two-tier retirement derived from
product identity). Includes:

- R2 channel protocol (release_channels.py, channel-protocol.ts): records,
  builds, manifests, retired channels with pinned destinationHead and
  receiverProtocol; fail-closed readers in both languages
- One shared native manifest/feed writer (scripts/bundles/channel_artifacts.py)
- Scoped/disposable R2 publication, fork isolation before credential
  access, canary bootstrap verification of its own promoted outputs
- Channel source CLI: typed SourceTarget, source-channel resolution,
  retirement downgrade refusal
- Desktop channel resolver/strategy, install-stamp/build-stamp receiver
  ownership (stable-owned S/T candidates), single-flight updater operation
- Cross-package retirement machinery (receiver/host/preservation/
  compatibility/connections/dialog/discovery, backup_migration strict
  snapshots with retained-link inventory, empty-dir preservation,
  connection-collision resolution, URL-credential rejection)
- Native install harness (tests/install/channel-retirement-*) and
  install-e2e retirement jobs
- checkout-source.test.ts transport shim now covers build_opener().open
  (was silently hitting the real network in CI)

Removed secondary certification protocol (channel_qualification.py) per
approved round-2 plan. All focused suites green at checkpoint; native
cross-package journeys unverified (to be deleted in round 3).
2026-09-14 10:26:36 -04:00
ethernet
a3ff6bb986 fix(build): sign nested Chromium with the discovered certificate hash 2026-09-13 20:32:50 -04:00
ethernet
303beaf6fc fix: keep lock files binary and fixture shell paths portable 2026-09-13 18:07:46 -04:00
ethernet
45b5f467d0 fix(desktop): exit native admission probe after ConPTY shell completes 2026-09-13 18:03:57 -04:00
ethernet
f8bdc96033 Merge branch 'ethie/pm-test-audit' into ethie/pm-clean
# Conflicts:
#	tests/scripts/test_bundle_native.py
#	tests/scripts/test_pm_runtime_bundle.py
2026-09-13 18:03:57 -04:00
ethernet
41be32c3c0 test: consolidate desktop contracts around real consumers 2026-09-13 15:10:31 -04:00
ethernet
2efa4ff94f refactor(desktop): prepare dependencies before saving build caches
Dependency acquisition during packaging left native wheels and packager
inputs outside the pre-build cache save. Compose PM and existing providers
into a preparation phase, then require builds to consume admitted inputs.

Share native preparation with PM Bundle. Keep path-bound environments and
signing outputs separate from reusable caches. Use read-only cache tokens
for commit builds and preserve the one-command local build path.

Verify pinned tools through PM, probe PTYs under the prepared Electron,
and supply dmgbuild through a build-only PM package. Resolve bundled tool
stores from their payload manifest so relocation preserves discovery.

Validation: focused Python and JS tests, checkJs, Ruff, Windows checks,
anti-slop, cache relocation, and network-denied Linux AppImage builds.
Relocated runtime smoke passed with NixOS host libraries supplied.
Native Windows/macOS signing and live GitHub cache behavior remain untested.
2026-09-13 14:28:31 -04:00
ethernet
21ddd6a943 fix(desktop): bound complete signing probes without patching fs 2026-09-13 13:05:54 -04:00
ethernet
6e0dd267a2 test(desktop): measure real signing traversal descriptor pressure 2026-09-13 12:52:35 -04:00
ethernet
7d73a180ae Merge branch 'ethie/uv-build-logs' into ethie/pm-clean 2026-09-13 11:18:46 -04:00
ethernet
9a42d60f25 fix(build): handle large release uploads and trim packaging waste 2026-09-13 11:04:03 -04:00
ethernet
690316c589 Give release Python sole ownership of App Installer descriptors 2026-09-12 19:00:39 -04:00
ethernet
3d0b6f9a0c refactor(pm): remove redundant work and test layers
Keep desktop rollback in staged publication instead of restoring backups
inside a candidate directory that will be discarded.

Use the npm tag endpoint, let the downloader own archive verification,
and ensure CI toolchain roots without repeating dependency verification.
Remove an unused resolver input and replace overlapping tests with
fault-proven lifecycle coverage.

Verified with the scoped Python gate and before-pack tests. Native
Windows/macOS update execution and the full suite were not run.
2026-09-12 18:34:01 -04:00
ethernet
5e4a2a3d24 refactor(pm): remove legacy dependency and launch managers
Competing installers and checkout-local venv assumptions bypassed PM
selection, install consent, and generation lifetimes. Route consumers
through PM and installation-bound launchers. Refresh source launchers
before obsolete Python entries can be collected.

Remove Node, browser, and CUA acquisition engines, obsolete venv-holder
handling, detached sync, and unused PM APIs. Keep historical updater
exports inert and preserve external tool ownership and native integration.

Share product freshness and prepared inputs across builders. Align plugin
admission, Docker provisioning, setup instructions, and behavioral tests.

Verified targeted Python and JavaScript tests, desktop and web typechecks,
scoped lint, real product builds, and the Docker frontend smoke test.
The missed post-setup test cleanup is included and verified.

Native Windows/macOS execution, full Rust compilation, and the complete
repository suite remain unverified. Historical compatibility requirements
were preserved and extended, not fully rescanned.
2026-09-12 14:57:38 -04:00
brooklyn!
4f1966edac feat(desktop): connector cards that wait for the sign-in, and a guided first launch that holds together (#108292)
* feat(desktop): give Button a loading prop that swaps label for spinner without layout shift

The label stays in the box, invisible, and the spinner is absolutely
centred over it, so a Connect or Approve button keeps its width while it
works instead of collapsing to a spinner. The approval bar had the same
thrash and moves onto it.

* refactor(desktop): one consent card for connectors and MCP setup

McpSetupTool rendered its own copy of the connector card's markup. It now
renders ConnectorCard for the pending question and ConnectorSummary once
settled, and the card gains what MCP needed: keyboard accelerators, a
source line, a question heading. The card also gets an avatar variant
(40px mark in the left gutter, text and buttons on one column) and a
collapseWhenSettled switch so a connector can stay a full card with a
green Connected pill in the action slot while MCP keeps its one-line
summary. Brand marks for Gmail, Calendar, Drive, Discord, Telegram and
Spotify; Slack via Tabler because simple-icons dropped the mark.

* feat(desktop): connector card drives the agent through manage_connections wait

The offer used to end in a Continue in chat button, and the agent, seeing
an unconnected status, would improvise around the app. Now the card does
what the TUI does. Clicking Connect opens the browser and sends one hidden
line telling the agent to park in manage_connections action=wait for that
slug and to never call connect again (a second link cancels the one being
signed into). Not now sends its own line. A hidden request that lands
while the turn is busy steers it, or queues if the turn just ended.

Which call owns the live card changes too: consecutive calls naming the
same apps are one exchange (connect, the wait, the status that follows),
and the first of the last exchange is the card, so the agent's wait no
longer demotes the card mid-authorization and mints a fresh one below it.
A targeted ask renders one or two bare cards; only a real catalog gets the
header, search and refresh.

* feat(desktop): onboarding connects apps in chat and keeps tasks finishable without them

The welcome chat knew connectors only as preferences to pick and wire up
later, so asked to connect Gmail it invented a Settings page that does not
exist. Both scripts now carry one rule set: status once, one batched
connect for every app named, the card is the ask so write a line and end
the turn, never route around a declined app with another client or
credential. The build handoff checks real connection status instead of
asserting none are connected, and the first task must be finishable, not
free of, the apps they picked. The connectors card explains what
connecting means and reports the count on its Continue button.

* fix(tools): resolve the Nous identity for share_auth profiles in the connector gate

A profile created with share_auth has no auth.json of its own and signs
in through the root store. Every other credential reader falls back to
the global root; the connector gate read HERMES_HOME/auth.json directly,
saw nothing, and stripped manage_connections from the profile's tool
list, so the welcome chat's agent truthfully reported the tool missing.
The gate now goes through get_provider_auth_state.

* fix(agent): name a provider retry backoff on the live status line

The retry status is buffered and replays only when every retry fails, so
during a 60s backoff after a 5xx the user saw a bare spinner. Right after
a connector sign-in landed this read as the agent going silent. The
backoff now also rewrites the live wait notice, which the desktop already
renders in the thread status row; it is transient and clears on recovery.

* test(desktop): connector rehearsal launcher and flagged connector spec

connector-rehearsal.mjs starts the real desktop and backend under a fresh
HERMES_HOME with no copied credentials, a fixed Vite port and CDP on 9344,
so the onboarding connector flow can be driven end to end by hand or from
outside. The Playwright spec covers the flagged connector step.

* fix(desktop): send the agent back into wait when the user keeps waiting after a timeout

The card's Keep waiting re-entered the poll but the agent's own wait had
timed out too and nothing told it to go back in, so it would start
talking mid-authorization. keepWaiting now fires onWaiting like connect
does. Tests also pin that an expired or revoked grant asks the gateway
for reconnect, not connect.

* style(desktop): blank lines in connector-flow test per lint

* feat(desktop): HERMES_SKIP_INTRO=1 / --skip-intro skips the first-run film

The intro is a one-time reveal, so anyone rehearsing the guided chat behind
it sits through it on every fresh HERMES_HOME. The flag rides the existing
launch-flags path (main → preload → renderer) next to guestOnboarding and
only gates isIntroRevealEnabled; the backend never sees it. The rehearsal
launcher sets it.

* fix(desktop): onboarding card Continue stays Done after the transcript rebuilds

The card kept its Done flag in component state. The hidden submit and the
turn-end hydrate both rebuild the message list, so the card remounted with
the flag false and Continue came back live, letting a step be answered
twice. The committed steps now live with the other onboarding answers,
keyed by step, and the first-build chip pick rides the same store.
remember_onboarding projects by key, so the new field never reaches USER.md.

* fix(desktop): no provider picker or free-tier chip over the guided first launch

Two sign-in surfaces leaked into the guide. A credential probe on the
setup profile (a free-tier token mid refresh, a session before its runtime
settled) hit requestDesktopOnboarding and dropped the provider picker over
the chat the user was in; and the statusbar free-tier chip sat there
offering a second sign-in the whole time. Both now yield while the gate
phase is cinematic, guided or handoff. The free tier is the provider for
those phases, and the guide offers sign-in on its own ready screen.

* fix(desktop): onboarding connector picks are real catalog slugs

The picker offered Spotify, GitHub and Stripe, none of which the deployed
connector catalog carries, and spelled Calendar and Drive with hyphens the
gateway does not use. A pick the build chat could not honour ended as
"Spotify isn't in the connector list" after the user had been told to
expect it. The list is now twelve slugs from the live status catalog,
spelled as the gateway spells them; GitHub is out (the terminal has git
and gh), chat channels stay on Messaging. Marks for the new entries; the
Google marks answer both spellings. The build runbook offers the picked
connections in its first turn rather than after the work is underway.

* fix(desktop): the free-tier ready screen never interrupts the guided chat

A readiness round fires when the layout pick assembles the window, and it
raised the free-tier ready screen over the conversation: the user was
dropped into the main app, dismissed it, and came back to a card they had
already answered. The guide is the introduction. The ready screen now
yields while the gate is cinematic, guided or handoff, and the notice is
acked the moment the guided chat takes the screen, not only when the film
does, so a skipped film no longer leaves it pending.

* feat(desktop): tour options that lead to building, and a fork that follows the tour

"Just the basics" and "Show me around" read as a click-through with no
exit; "I'll figure it out" read as declining help. Now Quick tour, Show me
everything, and Skip, let's build something. The script also folds the
fork into the same turn as the tour, so when the user closes the overlay
the next ask is already waiting instead of a transcript that ends on the
tour call.

* feat(desktop): the onboarding connector picker reads the live catalog

A hardcoded list, however carefully copied from today's catalog, is the
next drift. The picker now asks connectors.list through the same
session-owned RPC the connector cards use and offers exactly what the
gateway carries: a curated lead order puts the everyday apps first, chat
channels stay on Messaging, everything else is reachable by search. The
picks are gateway slugs, handed straight to manage_connections. No
catalog (toolset off, gateway unreachable) ends the step honestly with
Skip instead of inventing apps.

* test(desktop): the guided first launch never forces a sign-in

The acceptance criterion the guided onboarding was built to, as a test:
while the gate is cinematic, guided or handoff, the provider picker does
not open and a credential warning is dropped rather than deferred to the
next send. Outside the guide the picker opens as before. Red against the
tree before the guards landed (6 of 9).

* fix(desktop): a relaunch mid-guide resumes the guide, in the guide's shape

Closing the app during the guided first launch and reopening it booted the
normal shell around the persisted solo layout: the connecting splash, the
stock composer and model picker, a small window whose sidebars would not
open, while the gate still read guided. The gate now queues a kickoff for
the guided phase too (the kickoff adopts the existing guide chat by title),
takes the solo shape before the gateway opens rather than after, and the
connecting overlay yields to the guide's own opening. A typed reply in the
composer now closes an ask card and the first-build chips the same way a
click does; the layout card's Continue comes back Done.

* style(desktop): one answeredAfter helper for the ask card and first-build chips

* fix(desktop): the guide takes its shape on the tick the film ends, not after the window shows

Between the film and the greeting the full-size shell painted for a beat:
finishIntroReveal showed the main window, then the kickoff shrank it once
the setup profile answered. The listener on the intro's hidden edge now
takes the guide's shape (solo layout + small centred window) synchronously,
so the window is already the guide when it is shown. One takeGuideShape
owns the pair; kickoff and the boot gate call it idempotently.

* style(desktop): the 'nothing connects yet' line reads first on the connectors card
2026-09-12 10:00:06 +05:30
ethernet
cc01ae9d44 merge: connector UI e2e v2 into bundled onboarding (rebuilt)
# Conflicts:
#	apps/desktop/electron/main.ts
#	apps/desktop/src/app/settings/local-models-settings.test.tsx
#	hermes_wisdom/agent_led/share_flow.py
#	plugins/memory/hindsight/__init__.py
#	scripts/lib/wisdom-demo-env.sh
#	scripts/release.py
#	tests/skills/test_collective_wisdom_install_skill.py
#	tools/checkpoint_manager.py
2026-09-11 21:10:19 -04:00
ethernet
b66044f9ec test(desktop): verify macOS side-by-side identities natively
Run the production AppInfo and macOS bundle transformations against
isolated copies of Electron. Check bundle and helper identities, decode
the generated ICNS files with native tools, and launch four fixture apps.

On iris, stable, canary, and two commit identities held distinct userData
paths and locks. A duplicate canary was rejected. Canary could restart
while the other apps remained alive. A no-op identity mutation failed the
native userData assertion. The restored source passed.

This is a fixture test, not a signed release build or Hermes backend E2E.
The script writes only to temporary directories and retains its receipts.
2026-09-11 20:23:27 -04:00
ethernet
7c68f6c33e fix(desktop): generate artwork for native bundle test
The native bundle test fails on a clean checkout because its PNG inputs
come from the separate icon build. Generate valid temporary PNGs instead.

Verified on Promise with Windows ARM64, Node 26.7.0 and Vitest 4.1.10:
commit-bundles.windows.test.mjs reports one passed test and no skips.
The test uses real MSIX packaging tools and preserves its output checks.
No app installation, registration, signing or full suite was run.
2026-09-11 20:18:59 -04:00
ethernet
c4e2d937f7 fix(desktop): isolate canary and commit package identities
Canary and commit builds must not replace stable or share its desktop
state. Package names alone are insufficient because Electron reads the
product name before main initializes its paths. Pin nonstable userData
before the first lookup, and keep the packaged identity independent of
runtime build variables.

Keep release artifact filenames unchanged. Qualify payload CLI names,
route each nonstable MSIX alias to its own entrypoint, and copy the
immutable desktop provenance into the embedded Python checkout. Only
stable releases can use the official Store identity.

Targeted validation: 75 JavaScript tests passed, 2 platform skips;
15 Python tests passed with file retries disabled. Native Windows SDK
manifest proof is tracked separately. Full app install, signing and macOS
launch validation are not claimed.
2026-09-11 19:59:38 -04:00
ethernet
617880db21 test(msix): verify ACP aliases target their own launchers 2026-09-11 19:55:24 -04:00
ethernet
efbb69d0a3 test(desktop): verify distinct GUI and CLI manifest targets 2026-09-11 19:53:45 -04:00
ethernet
66b5cf155d test(desktop): verify flavor manifests with native Windows SDK 2026-09-11 19:50:21 -04:00
brooklyn!
aa13cbe9e0 feat(desktop): HERMES_SKIP_INTRO=1 / --skip-intro skips the first-run film
The intro is a one-time reveal, so anyone rehearsing the guided chat behind
it sits through it on every fresh HERMES_HOME. The flag rides the existing
launch-flags path (main → preload → renderer) next to guestOnboarding and
only gates isIntroRevealEnabled; the backend never sees it. The rehearsal
launcher sets it.
2026-09-11 15:57:55 -07:00
brooklyn!
7227e5e4f3 test(desktop): connector rehearsal launcher and flagged connector spec
connector-rehearsal.mjs starts the real desktop and backend under a fresh
HERMES_HOME with no copied credentials, a fixed Vite port and CDP on 9344,
so the onboarding connector flow can be driven end to end by hand or from
outside. The Playwright spec covers the flagged connector step.
2026-09-11 15:57:55 -07:00
ethernet
c184f04838 Use prepared Python for bootstrap and desktop build helpers 2026-09-11 17:59:55 -04:00
ethernet
86efc1f945 fix(release): allow explicit environment clears in commit bundles
An inherited HERMES_HOME can defeat a test bundle's data-directory suffix.
Older Windows installers also persisted that variable in the user registry.
This gives the app fresh UI state while its backend reads existing sessions.

Add --bundle-unset NAME, encoded as null in the existing bundle environment
object. Apply each clear as an explicit empty value before module startup.
Do not restore an explicitly empty HERMES_HOME from the Windows registry.
Ordinary defaults still preserve runtime overrides.

Verified the release parser, builder handoff, compiled startup ordering,
registry opt-out, and child environment with focused regression tests.
A native Windows probe passed with an inherited home. No MSIX was rebuilt.
2026-09-11 15:59:08 -04:00
ethernet
2b1312d4ca Merge branch 'ethie/pm-binary' into ethie/pm-clean 2026-09-11 15:13:11 -04:00
ethernet
162c5b92d5 feat(desktop): name commit and canary builds in the product display name
Commit builds now show 'Hermes Agent <sha7>' (e.g. Hermes Agent abc1234)
and canary builds 'Hermes Canary' / 'Hermes Light Canary' / 'Hermes Agent
Canary' as the OS-visible product name, so side-by-side installs and
per-commit artifacts are readable at a glance.

Display-only by design: appId, appNamePascal, and msixAppIdWithOrg are
unchanged, so a canary MSIX still updates in place over stable and
userData / single-instance sharing with the stable install is unaffected.
bundle-electron-main.mjs derives the commit from the install stamp
(source='commit-build') so the baked runtime identity matches the
packaging identity.
2026-09-11 14:58:11 -04:00
emozilla
4e865d42d1 fix(desktop): restore flat glass surfaces without text bleed 2026-09-11 13:41:05 -05:00
ethernet
7d326adf9d feat(release): bake explicit environment defaults into commit bundles 2026-09-11 14:25:12 -04:00
ethernet
1bf588234c refactor(build): share product recipes across distributions
Build TUI, web, desktop UI and runnable agent products from explicit
prepared inputs. Keep dependency preparation separate from distribution
packaging, with PM and native builds sharing uv environment construction.

Docker copies compiled frontend products instead of build dependencies.
Nix retains uv2nix environments and consumes shared assembly through store
references. Native desktop and Termux use the same launcher and frontend
contracts. Preserve the independent PM runtime and source imports from
arbitrary working directories.

Keep failed frontend builds from replacing the previous product, reject
source/output overlap, and bound dependency-process output draining.
Include hermes_wisdom in the Nix wheel: real CLI smoke tests exposed its
missing package declaration on the base revision too.

Verified focused Python and JavaScript suites, Docker build/runtime checks,
Nix desktop and CLI/ACP checks, standalone TUI and packaged Electron PTY,
and real full-Chromium interaction. Native signed installers, Android device
installation and the full repository suite remain CI verification.
2026-09-11 13:16:55 -04:00
ethernet
90482e16b9 Merge branch 'ethie/pm-clean' of github.com:NousResearch/hermes-agent into ethie/pm-clean 2026-09-10 16:09:14 -04:00