191 Commits
Author SHA1 Message Date
Maciej Lisiewski d62c60787f fix: pnpm review follow-ups in nix, evals, the hang repro and an e2e test
- nix dev shell: pnpm-workspace.yaml (overrides, catalog, allowBuilds)
  joins the package.json stamp, so a settings-only change relocks.
- desktop-pane-fixture: resolve vitest/config from apps/desktop; pnpm
  links it only there.
- short-session hang repro: name the lockfile in the target metadata, so
  an npm-era target fails as a lockfile mismatch rather than a bare hash.
- test_mirrors: the pnpm mirror test prefetches from the registry and
  needs allow_network, like its siblings.
2026-11-06 04:08:46 +01:00
Maciej Lisiewski 7062229090 chore(js): switch the workspaces from npm to pnpm
pnpm 12.15.0 (native executable) installs every JS workspace from one
pnpm-lock.yaml, imported from package-lock.json with the same resolved
versions. website/, the WhatsApp bridge and the photon sidecar stay separate
npm projects.

Configuration moves from .npmrc and the root manifest into
pnpm-workspace.yaml: the 14-day minimumReleaseAge and its excludes,
overrides, allowBuilds (the old allowScripts set, with strictDepBuilds
failing any unlisted build script), engineStrict, sideEffectsCache: false
(in-place rebuilds must not write into the store) and
verifyDepsBeforeRun: false (a `pnpm run` never installs).

The pnpm version is pinned in pm/lock.json and in root devEngines.
pmOnFail: ignore keeps that pin out of pnpm-lock.yaml: a leading pin
document makes osv-scanner read zero packages. scripts/build/node-deps.mjs
enforces the exact version instead, and every lock reader refuses a
multi-document lock. A root preinstall refuses `npm install`; devEngines
onFail is warn so npx (MCP servers, the tsc linter) keeps working inside
the checkout.

User-machine installs run --frozen-lockfile --trust-lockfile (release age is
checked when locking, in CI, not per user). node-deps removes npm-era
node_modules trees before the first pnpm install. Nix builds the store with
fetchPnpmDeps and a hash file that CI checks.
2026-11-06 04:08:24 +01:00
ethernet c90b27e8e3 feat(nix): pass the token as HERMES_DASHBOARD_SESSION_TOKEN=file:<path>
Follows the backend's file: prefix: one credential variable, and the
unit environment carries a path rather than a new secret-shaped name.
2026-10-28 12:22:11 -04:00
ethernet 7c8d7b9cf3 feat(nix): the session token rides the unit environment as a path
backend.sessionTokenFile used to be rendered into a launcher that read
the file, exported the token and execed hermes -- because hermes took
the token only as an env var. That launcher is gone: hermes reads
HERMES_DASHBOARD_SESSION_TOKEN_FILE itself, so the module now sets the
path in the unit environment (systemd Environment=, launchd
EnvironmentVariables) and builds the plain hermes argv whenever no wait
is configured. Nothing renders the path through a shell any more, which
also retires the escapeShellArg-versus-${XDG_RUNTIME_DIR} bug class for
the backend -- and the same expansion fix lands in the desktop wrapper,
which still reads the token itself. The option insists on an absolute
path; a secret manager's placeholder would pass through unexpanded.

Depends on the HERMES_DASHBOARD_SESSION_TOKEN_FILE backend support;
against an older backend the service would mint a random token and the
desktop would fail to attach with no nix-side error.
2026-10-28 12:22:11 -04:00
unsupportedpastels 3f2b1722b1 feat(nix): ship the Webapp in the Nix package
The Nix package had no Webapp bundle, and `hermes webapp` could not build
one in the read-only store. nix/webapp.nix builds it in the npm workspace
the hook prepares (no native rebuild, no Electron), exposed as
`.#webapp`. The agent assembler takes it as a `webapp` frontend: the
wrapper sets HERMES_WEBAPP_DIST to it, as HERMES_WEB_DIST is set for the
dashboard.

hermes_cli/webapp.py serves a HERMES_WEBAPP_DIST bundle as shipped: no
freshness check, no build lock, never rebuilt or marked stale, and
`--force-build` says it cannot rebuild it.
2026-10-27 09:42:22 -04:00
Teknium e892ed0594 fix(nix): keep function-form overrides that append to prev.extraDependencyGroups working
Moving the shipped groups to defaultDependencyGroups dropped
extraDependencyGroups from the base callPackage arguments, so an
override (prev: { extraDependencyGroups = prev.extraDependencyGroups ++ [...]; })
that evaluated on main threw 'attribute extraDependencyGroups missing'.
The base package now passes an explicit empty list, and the
extras-extend-package check appends without 'or [ ]' so the shape stays
covered.
2026-10-24 18:02:46 -07:00
Teknium 61f3668c6a fix(nix): cover module extras on a custom package that has its own
Greptile: "Custom extras lack coverage". The extras-extend-package cases
all started from the default package, whose groups now live in
defaultDependencyGroups, so reverting effectivePackage to the old
replacing override still passed every case. Add a custom `package`
built with extraDependencyGroups + extraPythonPackages, set the module's
extras on top, and assert the venv and the package equal one built with
both lists. Eval-only.
2026-10-24 17:56:33 -07:00
Teknium 44b57c8af1 docs(nix): extraDependencyGroups adds to the package's shipped groups
Module and .override extras now append to the selected package's
defaultDependencyGroups. Say so in nix-setup.md (en + zh-Hans) and the
option description, point smaller builds at .#minimal or
defaultDependencyGroups = [ ], and replace examples that named groups the
default already ships ("messaging", "voice") or that no longer exist
("honcho").
2026-10-24 17:09:39 -07:00
Teknium 57c9e3efd8 fix(nix): extras checks use a group the default package does not ship
extras-extend-package named "langfuse", which left pyproject.toml when
Langfuse moved to the plugin catalog; uv2nix throws on an unknown group,
so the check aborted at eval. The desktop check's "exa" override is now
shipped by `full`, so its effective package equalled the default and it
no longer exercised a customised package. Both use "google-meet", a real
extra outside full's defaultDependencyGroups; the header example drops
"honcho" (also moved to the catalog) for the same group.
2026-10-24 17:09:39 -07:00
Teknium 1de96a025f Merge origin/main into fix/nix-extrapythonpkgs-extend
# Conflicts:
#	nix/hermes-agent.nix
2026-10-24 17:08:28 -07:00
ethernet 75f8bd95fe fix(nix): ffprobe on darwin and native dev commands in devVenv
pm's macOS ffmpeg (martin-riedl) is a single binary with no ffprobe; moving
runtimeDeps to it dropped the ffprobe nixpkgs' ffmpeg used to provide, so
transcription could not measure duration and skipped splitting long
recordings. Darwin's runtime PATH links ffprobe alone from ffmpeg-headless,
keeping the pinned ffmpeg first.

devVenv copied only Python-shebang scripts, dropping the dev group's native
ruff and ty. Link every other command as-is, skipping the activate scripts
and devPython's interpreter links. dev-venv-self-contained runs both.
2026-10-23 17:20:39 -04:00
ethernet 5a5aa9a3a8 fix(nix): patch patchelf so pm's Linux binaries survive autoPatchelf
nixpkgs' patchelf 0.19.2 corrupts executables whose headers it must move
when --set-interpreter runs before --set-rpath, which is autoPatchelf's
order: the new read-only LOAD overlaps the RW one, .dynamic lands read-only
and ld.so faults (NixOS/patchelf#674). The pinned node segfaulted on start,
so every npm build (hermes-tui first) died with exit 139 after the flake
update moved patchelf off 0.15.

Apply upstream's fix (NixOS/patchelf#672, merged, not yet released) to the
patchelf the pm derivations use. Linux only; darwin derivations unchanged.
2026-10-23 17:20:39 -04:00
ethernet 0329638c2b refactor(nix): stdenv.is* -> stdenv.hostPlatform.is*
The updated nixpkgs deprecates the stdenv.is* aliases and warns on every
evaluation.
2026-10-23 17:20:39 -04:00
ethernet 0663b92ce9 fix(nix): the dev shell evaluates and builds on darwin
cage, ghostty, grim and libglvnd, and the sandbox's Electron LD_LIBRARY_PATH,
pulled alsa-lib, which refuses to evaluate on darwin. They are Linux-only now.

pm-packages ran autoPatchelf over darwin artifacts and crashed on the Mach-O
ffmpeg; ELF patching is Linux-only. It also drops Nix's env-vars dump from
$out, and npm-pinned takes the node lib.nix passes instead of defaulting.
2026-10-23 17:20:39 -04:00
ethernet be82958af5 fix(nix): the dev shell's HERMES_PYTHON carries its own dependencies
The shell pointed HERMES_PYTHON at the bare pm-pinned interpreter and reached
the uv2nix site-packages only through PYTHONPATH. Every consumer that replaces
PYTHONPATH (Ink's gateway bootstrap, image attachments, run_tests.sh's env -i)
got an interpreter with no dependencies, so 'hermes --tui' in the shell failed
with 'No module named ruamel'.

devVenv re-homes uv2nix's editable venv onto the pinned interpreter: a real
pyvenv.cfg whose home is the lock's python, devPython's site-packages, and
console scripts re-shebanged onto it. HERMES_PYTHON, VIRTUAL_ENV, python,
pytest and pm are now one interpreter and the shell sets no PYTHONPATH.

The shipped package and the shell share one tool list: runtimeDeps takes
ripgrep and ffmpeg from pm/lock.json too. Reverts the run_tests.sh PYTHONPATH
passthrough, which leaked a caller's PYTHONPATH into every HERMES_PYTHON run.
The dev-venv-self-contained check runs the venv under env -i.
2026-10-23 17:20:39 -04:00
ethernet a2cdc450d9 refactor(nix): dev shell takes hermesNpmLib.nodejs instead of a second node+npm pair
hermesNpmLib.nodejs already puts the pinned npm over the pinned node; the
separate npmPinned + pmPackages.node pair relied on PATH order to get the same.
2026-10-22 17:25:38 -04:00
ethernet e2adc90f82 feat(nix): dev shell runs the pm-pinned tool set
The dev shell took uv, node, ripgrep and ffmpeg from nixpkgs, so it ran
different versions than pm/lock.json ships (uv 0.11.32 vs 0.13.5, node
26.5.0 vs 26.12.1, ffmpeg 8.1.2 vs 9.0.1). Compose its tool set from
nix/pm-packages.nix instead, and wrap the pinned npm onto the pinned node
so `npm` and `node` are one generation.

pm-packages.nix also shipped every artifact nested under its archive's
versioned directory ($out/node-v26.12.1-linux-x64/bin/node), so no consumer
could reach them through PATH; flatten that and link root-level executables
into $out/bin.

git and openssh stay nixpkgs: the lock carries no Linux artifact for either.
2026-10-22 17:25:38 -04:00
ethernet da95152cde feat(nix): add pm to devshell 2026-10-22 17:25:38 -04:00
Teknium 7a0e1e2464 fix(tui): one launch path for Ink's Python helpers; name a bootstrap that cannot start
Settles TUI-23 and TUI-24 on one launch path: the helpers ship with the TUI
product (dist/scripts, salvaged from #139423) AND boot the install's own
dependency generation (salvaged from #138898). Combined as cherry-picked they
broke each other: a packaged helper's parents[2] is the product dir, not the
Python root, so activate_dependencies looked up the wrong install. The client
now passes the root it already puts on PYTHONPATH as HERMES_PYTHON_SRC_ROOT and
both helpers use it.

A helper interpreter that still cannot load Hermes (no committed generation,
missing module) prints the named verdict `gateway unavailable:
bootstrap_dependencies`; gatewayClient reports it once and stops scheduling
reconnects instead of retrying every 30 s forever. nix/checks.nix asserts the
helpers ship in the Nix TUI output.

Refs #134889 (TUI-23, TUI-24)
Refs #139574
Refs #139208
Fixes #138878
2026-10-16 01:54:58 -07:00
clover. d2cb13b2af Merge branch 'main' into fix/nix-extrapythonpkgs-extend 2026-10-12 17:15:40 -04:00
Teknium ca38632fc0 Merge origin/main (#17: 454 commits to e6757f2029) into feat/unified-gateway-runtime
12 conflicted files, resolved in three lanes:
- gateway/run.py: take the branch split; main's tirith removal and spool-replay
  ordering ported into run_runtime_init.py / run_bootstrap.py.
- config migrations: main keeps v50 (tirith); the branch's bot-chat timeout removal
  becomes v51 (_config_version 51, template + test updated).
- hermes_state_usage.py: main's voice_chat task route threaded through the branch's
  owner transaction; worker.persist usage allowlist accepts task.
- tui_gateway/server.py: branch side; main's edits applied where the code now lives
  (agent_factory, session_registry). Desktop sessions get the catalog toolset on the
  authority path; daemon-run finite CLI turns are not watched turns; lazy session info
  reports nous/welcome on the free tier.
- Desktop/Ink: one branch-child spinner fix (main's route-token check); main's
  uninstall i18n keys kept, pool keys stay removed; voice_turn forwarded and accepted
  by the authority submit (surface_v1.voice_turn).
- chat_ws / PTY keepalive test: main's stricter test kept.
2026-10-07 06:48:09 -07:00
Teknium 1e500ebce2 Merge origin/main (#16: 4,465 commits to 9dcab1e440) into feat/unified-gateway-runtime
99 conflicted files resolved in nine area lanes (Desktop composer, Desktop renderer/shared
client, Electron main, Ink, tui_gateway, gateway, state, CLI/config, cron/tools/CI). The
unified runtime's architecture wins where it deliberately replaced a mechanism (pool,
fallback lanes, claim poller, renderer queue); every orthogonal main change is kept, ported
into the <stem>_<topic> sibling that now owns relocated code.

Notable ports and repairs in this merge:
- tui_gateway/server.py relocated regions: atomic _attach_built_agent (#49852), slash ranking
  registry names, minted-key helpers methods_session.py calls, request_overrides, service tier.
- gateway/run_turn*.py: i18n accessors for the removed reply constants, first-contact
  internal/DM rule, rotated-compression persist, fast-mode tiers, prompt-pin rehydration.
- webhook: route_from_chat_id decodes main's opaque webhook:v2 chat ids (old ids still read).
- ws.py: main's per-connection dispatch queue with authority-first dispatch inside it.
- state: schema event for runtime revision renumbered to 31; worker mirrors updated for
  main's message uid/merge fields, on_contended lease arg, compression columns.
- config: branch's bot_chat_delivery_timeout removal renumbered v47 -> v50.
- Electron: primary-profile pin, redirect header scoping, remote fetchJsonForBackend, SSH
  teardown on profile delete, held-update dial gate restored (lost in earlier merges).
- Desktop: canonical clarify.request mapped to main's questions[] card; replay truncation vs
  snapshot_required; periodic secondary prune tick restored; DELETE already_absent restored.
- cron/tools: diagnostic category on cron failure notices restored; owner_holds_delivery
  ported onto the authority; admitted receipts can no longer be cancelled locally.
2026-10-06 14:24:24 -07:00
Teknium 911da42e9e refactor(security): remove the bundled tirith scanner from core
tirith ran `tirith check` on every terminal command, default-on with fail-open, as a
pinned PM binary auto-downloaded at startup. Measured against the shipped 0.4.2 binary
it caught 14/16 attack commands but flagged 12/24 everyday agent commands
(`echo $((6*7))`, `"$VAR/x.sh"`, `gh api | python3`, LAN curls, `.dev` hosts,
`pip install` via analysis_incomplete), and in cron/-q deny mode each of those is a
hard block. It only ever saw terminal strings (execute_code, writes and the browser
bypass it), fail-open meant no protection wherever the binary could not install
(Windows, Termux, x64 musl), and it produced a steady issue/PR stream (fail_open
semantics four times, install loops filling disks, a SIGSEGV hang, session-approval
scoping, third-party promo text on approval cards).

Removed: tools/tirith_security.py, the tirith branches of the command guard (the
is_tirith tuple, session-only "always" downgrade, import-error/fail-open paths,
unattended tirith pass), the CLI and gateway startup installers, the PM package and
lock pins, the nix runtime dep, security.tirith_* defaults, the CLI warning string
and three tirith tips (gaps refilled from the tail so translations survive), plus
the gateway's "no automated assessor" warning that only existed to point at tirith.

Config v50 drops security.tirith_* and enables nothing in its place. Hermes ships
no tirith plugin; if tirith upstream publishes one, it goes through normal catalog
admission under their name.
2026-10-06 14:22:13 -07:00
ethernet 59956e6b07 fix(nix): remove ELECTRON_OZONE_PLATFORM_HINT, clean up args
this hasn't worked since electron 38.
2026-10-06 14:11:29 -04:00
ethernet d795726f78 fix(nix): pick up wayland in the desktop app 2026-10-03 17:31:04 -04:00
ethernet 699f42fecf fix(nix): include assets/dmg-volume.png in the icon generator fileset 2026-10-02 11:06:08 -04:00
teknium1 7e53b3ef82 chore(memory): remove bundled honcho provider; install from the plugin catalog 2026-10-02 04:39:28 -07:00
teknium1 0954ad4b9b fix(desktop): verify the renderer bundle once per build, not twice
npm runs the `postbuild` hook after every `npm run build`, and the desktop
compiler (scripts/build/desktop.mjs) already runs checkDistBuilt on the
scratch product before it publishes dist/. Every update and every pack
therefore re-verified the bytes it had just verified; with the per-chunk
syntax check that was a second ~960-process pass (#123216).

Drop the hook; nix's checkPhase calls the guard directly.
2026-10-01 15:28:55 -07:00
cl0vrfi3ld db752fb8c4 fix(nix): keep shipped dependency groups out of extraDependencyGroups
The default and messaging packages carried their groups in
extraDependencyGroups, so the documented overlay path
`pkgs.hermes-agent.override { extraDependencyGroups = [ ... ]; }`
replaced them and rebuilt the venv as minimal. Give the package its own
defaultDependencyGroups argument, set it from packages.nix, and keep
extraDependencyGroups purely additive (deduplicated, devPython too).

Replace module-extras-extend-package with extras-extend-package, which
covers the overlay and module paths and asserts positive effects as well
as preserved ones, so an implementation that ignores the extras fails.
2026-09-30 22:20:26 -04:00
cl0vrfi3ld 4d1cf2d0de fix(nix): module extraPythonPackages/extraDependencyGroups extend the package's own
effectivePackage overrode the package with a plain attrset, replacing its
extraDependencyGroups. The default package is minimal plus the provider
groups (anthropic, messaging, ...), so setting extraPythonPackages alone
rebuilt the venv as minimal and dropped them. Use the function form of
override and append instead; add a module-level check that the default
venv is unchanged.
2026-09-30 20:49:03 -04:00
Siddharth Balyan 298df0820d fix(nix): fresh declarative installs no longer warn their config predates version 12 (#126022)
* fix(nix): stamp _config_version into the declarative config.yaml

The NixOS and Home Manager modules rendered config.yaml without a
_config_version key. Hermes reads a missing key as version 0, so every
boot of a fresh declarative install ran the post-update migrate step,
which logged "This config predates version 12 ... can no longer be
auto-migrated". Hermes cannot fix this itself: in managed mode
save_config() refuses to write config.yaml, so migrate_config() can
never persist the stamp.

The module now builds the generated config with the package's own
interpreter and adds DEFAULT_CONFIG["_config_version"], so the stamp
always matches the package it ships with. An explicit
settings._config_version still wins. A user-supplied configFile is
installed unchanged.

* test(nix): a generated config.yaml is current to the packaged Hermes

Merges the module's generated config into an empty HERMES_HOME and asks the packaged check_config_version() whether it is up to date. Fails on the parent commit (v0 vs v46).
2026-09-28 11:12:16 +05:30
teknium1 6f2aeb528f Merge origin/main (#10: 382 commits to 8f897d2d23) into feat/unified-gateway-runtime
Conflicts:
- hermes_state_sessions/maintenance: main's shared write-guard filter
  (_guarded_ids, delegate-child cascade guard) runs first, then the branch's
  mutation retirement (retire_sessions / retire_prunable).
- hermes_state_messages/compression: the branch's _append_messages_in_transaction
  split keeps main's serialize/decode repair hooks and runs under main's
  _execute_transcript_write (caller row state restored on rollback).
- web_routers/sessions delete: the gateway-executed canonical delete stays
  (busy sessions already answer 409 session_busy); docs keep that contract.
- web_server lifespan lives in web_server_app: main's orphan-reap startup
  grace (min_age_s, #122533) ported there.
- Desktop uninstall teardown: main's quiet 'quit' intent; its pool-PID seeding
  is dropped because attach-model pool entries own no child process.
- Desktop lifecycle/slash imports: union.
2026-09-27 12:42:14 -07:00
Octopustank a6686cc396 fix(desktop): carry the window app id in the Linux launcher entry
Packaged windows claim `com.nousresearch.hermes` as their Wayland app id
(electron-builder bakes product-identity.cjs's `appId` into
extraMetadata.desktopName; Electron hands that string to the compositor
verbatim), while the entry was written as `hermes.desktop` with
`StartupWMClass=Hermes` — so GNOME matched neither StartupWMClass nor a
`<app_id>.desktop` file name and every launch fell back to the placeholder
icon, with the raw app id in the tooltip.

- write `<app_id>.desktop` with `StartupWMClass=<app_id>` (Name= stays "Hermes")
- retire a leftover `hermes.desktop` once the new entry is on disk, and only
  when the file still names this app and launcher management is enabled;
  foreign files at that path are left alone
- nix/desktop.nix derives the entry file name from the module instead of
  hardcoding it
- tests: the installed entry carries the app id, legacy retirement, foreign-file
  preservation, opt-out preservation, plus a node probe asserting
  APP_ID == product-identity.cjs appId

Known consequence: an existing taskbar pin points at the old entry id and has to
be re-added once.
2026-09-27 07:46:31 -05:00
teknium1 493bb4397d fix(nix): port main's pinned-backend Desktop check to the attach model
Main's desktop-backend check waits for a spawned `hermes serve` HERMES_BACKEND_READY sentinel;
Desktop now attaches to `hermes gateway ensure` and the daemon serves the dashboard. Desktop
logs the backend it chose and its attach readiness, and the check asserts those: the pinned
CLI wins over the mutable install and the gateway comes up.
2026-09-27 03:49:28 -07:00
Hermes Agentandak2k d99bb06862 fix(nix/desktop): compile node-pty against electron.headers
The desktop derivation compiled node-pty against a hand-pinned fetchurl
of Electron's node headers, whose URL is templated off electron.version
but whose sha256 is hardcoded. Every nixpkgs electron bump swaps the
tarball out from under that pinned hash, failing the fixed-output
derivation at build time — and only there, never in the networked
Docker/npm ci paths.

Use nixpkgs' own electron.headers derivation instead. It is
version-locked to the electron package, so it tracks every bump
automatically with no hand-pinned hash to go stale, needs no network,
and is already the --nodedir layout (so the manual fetch + tar
extraction both go away). Same pattern as signal-desktop,
github-desktop, session-desktop, rstudio in nixpkgs.

Fixes #61443

Co-authored-by: ak2k <19240940+ak2k@users.noreply.github.com>
2026-09-25 17:09:43 -05:00
ethernet 3aa215fdc9 build: remove leftover references to the dropped hindsight extra
The extra removal left CI, the Docker image and the nix package still
requesting `hindsight`. Once the extra is gone, `--extra hindsight` and
extraDependencyGroups = [ "hindsight" ] ask for something that no longer
exists. Drop them the same way 73c598e319 originally did: remove it from
the CI extras lists, the Docker sealed-venv build and the nix default
groups, and point the nix examples/check at honcho. Also remove the
stray blank line left in the exclude-newer table.
2026-09-24 22:15:15 -04:00
ethernet ccc9d422ed fix(tts): install kittentts on python 3.14, opt-in only
KittenTTS 0.8.1 requires misaki[en]>=0.9.4. PyPI's misaki 0.9.4 caps
Python below 3.13. NousResearch/misaki f03fd2be73 is upstream main with
the cap raised to <3.15 and no code change, so the extra declares
misaki[en] at that commit and uv resolves KittenTTS's requirement to it.

misaki[en] also lists spacy-curated-transformers. The newest release
that fits spacy 3.8, 0.3.1, caps Python below 3.14, so PM's `uv pip
check` rejected every 3.14 venv that carried it. Nothing imports it:
only transformer spaCy models use it, and KittenTTS never builds a
misaki G2P. A never-true override marker removes it from the lock.
torch, transformers, spaCy and num2words stay: misaki.en imports them
at module level and KittenTTS imports misaki.en.

Native bundles sync with --all-extras, so un-gating the extra would put
KittenTTS, torch and spaCy in every payload. [tool.hermes] opt-in-extras
names the extras that only an explicit selection installs. PM adds
--no-extra for each of them to an all-extras sync, so bundles and their
recorded feature list leave kittentts out, and setup still installs it
through sync_venv when the user picks it.

The extra is gated on platform instead of Python: onnxruntime has no
Intel macOS wheel and torch has no Windows ARM64 wheel. Nix gets the
hatchling build backend the misaki git source needs.

Verified on macOS arm64 with CPython 3.14.7 and uv 0.12.3:
uv sync --frozen --extra kittentts from this lock, then uv pip check
reports all 126 packages compatible. _generate_kittentts writes a
3.87 s 24 kHz WAV (RMS 3661), and no spaCy model is downloaded. A dry-run
all-extras sync with --no-extra kittentts selects none of kittentts,
misaki, torch, transformers or spaCy. The new build test fails without
the environment change and passes with it.
2026-09-24 16:17:35 -04:00
ethernet eedbddbeb1 Revert "fix(tts): install kittentts on python 3.14 via the misaki fork"
This reverts commit 694fe220a4.

misaki[en] also requires spacy-curated-transformers. With spacy 3.8
(thinc 8.3) only its 0.3.x line resolves, and 0.3.1 declares
requires-python <3.14. uv's lock ignores that upper bound, but PM's
dependency validation rejects it, so the win32-x64 bundle failed with
"spacy-curated-transformers requires Python >=3.9, <3.14". Restore the
<3.13 gate until misaki's English stack installs cleanly on 3.14.
2026-09-24 15:19:06 -04:00
ethernet 694fe220a4 fix(tts): install kittentts on python 3.14 via the misaki fork
KittenTTS 0.8.1 requires misaki[en]>=0.9.4, and PyPI's misaki 0.9.4
declares requires-python <3.13, so the extra was gated off the managed
3.14 interpreter. NousResearch/misaki f03fd2be73 is upstream main
(0.9.4 + "Enable Python 3.13") with the cap raised to <3.15 and no code
change. The extra now declares misaki[en] at that commit directly, so
uv resolves KittenTTS's transitive requirement to the fork. A
[tool.uv.sources] entry would not reach a transitive dependency, and
an override would bypass pip installs of the extra.

The extra is gated on platform instead of Python: onnxruntime has no
Intel macOS wheel and torch (pulled by misaki[en]) has no Windows ARM64
wheel, the same gate piper already carries.

Nix: uv.lock records no build backend for the git source, so
buildSystemOverrides supplies hatchling for misaki. Without it the
opt-in build fails with "No module named 'hatchling'". The default
package derivation is unchanged; kittentts is in no Nix bundle.

Verified on macOS arm64 with CPython 3.14.7: uv sync --frozen --extra
kittentts from this lock installs misaki from the fork commit, and
tools.tts_tool_local._generate_kittentts writes a 3.87 s 24 kHz WAV.
2026-09-24 14:47:06 -04:00
ethernet dcd2bca06a fix(desktop): render icons with core runtime dependencies 2026-09-24 01:30:15 -04:00
ethernet dcd678d6fd fix(release): close publication custody gaps 2026-09-22 02:19:38 -04:00
ethernet e34214118f nix: unpack only the archive artifacts of a pm pin
pm/lock.json lists provenance sidecars (checksums.txt, .sig/.pem/.asc)
next to the tirith and iron-proxy archives; pm verifies them at install
time. nix/pm-packages.nix fed every artifact to srcs, and stdenv's
unpackPhase has no unpacker for a .txt, so `nix build .#pm-tirith`
failed with "do not know how to unpack source archive". `nix flake
check` only evaluated the pm derivations, which is why CI stayed green.

srcs now keeps the suffixes pm/store.py::extract unpacks; the
fixed-output hash already pins the archive. A new check builds the two
sidecar-bearing pins so the regression cannot hide behind evaluation.
2026-09-21 18:44:09 -04:00
ethernet a41aabff6c fix: platform legs — docker arm64 bootstrap, win32-arm64 native builds, nix desktop-backend cleanup
- pm.environment owns _RESOLVER_MARKERS: the streaming uv runner imported
  pm.workspace, whose tomllib import fails on the 3.10 system python that
  bootstraps the Docker arm64 image (No module named 'tomllib'). Invariant test
  proves runtime staging needs neither tomllib nor pm.workspace.
- run_tests.sh forwards the MSVC/SDK/Rust/OpenSSL toolchain variables through
  its env -i scrub so PM tests that compile ruamel-yaml-clib on Windows arm64
  find cl.exe (previously 'Visual C++ 14.0 or greater is required').
- nix desktop-backend check: the spawned backend outlives cage's process group
  and kept writing under the temp HERMES_HOME during rmtree; stop every
  process bound to the throwaway HOME before cleanup.
- windows: test_launcher_runtime_selection imports runtime_state from
  hermes_cli (moved in bbec973514); the ' spaced ' suffix row loses its
  trailing space on win32 (the filesystem strips it).
- macOS: test_sealed_worker_command copies the interpreter into the payload
  (the escape guard resolves symlinks) and links the host lib tree.
2026-09-20 10:42:30 -04:00
ethernet c3caa07a39 fix(nix): the renderer derivation ships the packaging helper product-identity.cjs requires
apps/desktop/product-identity.cjs now resolves the channel request through
scripts/msix-shared.mjs (and its content-types table); nix flake check failed
with Cannot find module '../../scripts/msix-shared.mjs'.
2026-09-19 02:28:20 -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 ea5757a8fe fix(nix): container mode does not linger the host service user; document the cron/linger dependency
The scope-dispatching cron worker runs inside the container there, so a
host user manager would start for nothing (as PR #110641 by @liuhao1024
also gated it). nix-setup.md gains the note operators need when they
declare the user themselves.
2026-09-15 04:03:25 -07:00
Zaar Hai ec30eefec9 fix(nix): linger the service uid so cron can create its worker scope
The NixOS module produces the one topology the restart-safe cron worker
cannot run in: a system service (`User=hermes`, `isSystemUser = true`)
whose uid has no systemd user manager. `run_one_job` hands every fire to
`_launch_external_cron_worker` → `restart_safe_gateway_child_argv`, which
requires a transient `systemd-run --user --scope` and fails closed when it
cannot get one:

  Restart-safe cron worker dispatch failed: cannot create restart-safe
  systemd scope for gateway child: systemd-run --user --scope is
  unavailable (usually no reachable user D-Bus session at
  /run/user/995/bus). On a system-level service install, run
  `sudo loginctl enable-linger <gateway-user>` and restart the gateway.

So on a stock `services.hermes-agent.enable = true` host, no cron job runs
at all — every fire is recorded as a failed execution before an agent
starts, and the occurrence is consumed, so a weekly job does not retry for
another week. The error names the remedy, but nothing in the module applies
it, and the terminal-worker path with the same cgroup-isolation goal
degrades gracefully instead (`process_registry.py`: "worker shares the
gateway cgroup"), so nothing else on the host looks wrong.

Three parts, because linger alone leaves a startup race:

- `linger = lib.mkDefault true` on the created user: the declarative
  `loginctl enable-linger`, which gives the uid a user manager and with it
  /run/user/<uid>/bus. mkDefault, so an operator can still refuse it.
- after/wants linger-users.service, the unit that runs enable-linger.
- a bounded preStart wait for the socket. logind starts user@<uid>.service
  asynchronously, and `run_gateway` resolves XDG_RUNTIME_DIR /
  DBUS_SESSION_BUS_ADDRESS exactly once at startup (802f0f9), so a bus that
  appears after ExecStart is one the process never sees for its lifetime.
  Non-fatal after 10s: a gateway without cron beats no gateway.

All three are gated on `lingerEnabled`, read back off `config` rather than
assumed: with `createUser = false` the operator owns the user, nothing here
knows whether they lingered it, and the unit must not block on a bus that
may never arrive.

Test: nix/checks.nix `cron-worker-user-scope` relates the unit's `User=` to
that user's `linger`, requires the ordering and the wait when it lingers,
and requires neither when the module does not own the user. Red on the
parent commit for the first three arms.

Needs nixpkgs >= 25.05 for `users.manageLingering`.
2026-09-15 04:03:25 -07:00
ethernet dd189aad1a fix(desktop): prefer pinned backend over mutable install
The Nix desktop selected a legacy user venv before its explicit backend command. That runtime passed the version probe but failed agent initialization with a missing tools.kanban_toolset_context module.

Resolve the deployment command before the managed-install fallback. Add a headless packaged-desktop check with a competing legacy launcher; it fails on the old artifact and passes inside the Nix sandbox after the fix.
2026-09-12 21:18:23 -04:00
ethernet a0d425d60f fixup: nix builds 2026-09-12 20:20:46 -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