Commit Graph

34374 Commits

Author SHA1 Message Date
ethernet
5cae2e679b Document explicit generation inputs and recorded repair replay 2026-09-12 19:06:05 -04:00
ethernet
b1cad3aa24 fix(pm): preserve cross-target stages and recorded build inputs
Cross-target Node verification attempts to execute foreign bytes before
and after publication. Check the native target before smoke probes, while
retaining file and architecture checks for every target.

Repair must retain a plugin's build directory when it contains the declared
PEP 517 backend. Use the same copy exclusions as the initial snapshot.

The foreign-ELF execution trap and offline real-uv replay test fail before
the fixes and pass after them. Native smoke probes and bionic no-execution
checks also pass. The focused PM run reports nine unrelated failures,
all reproduced at the starting commit. No full suite or native Windows
validation was run.
2026-09-12 19:04:37 -04:00
ethernet
4e198ec6f8 Share desktop shutdown completion and SQLite snapshot ownership 2026-09-12 19:04:07 -04:00
ethernet
02f9a241d3 Let PM own Git environments and Matrix dependency preparation
Delete consumer-side Git discovery caches and layout reconstruction. Share the selected environment while preserving PM acquisition policy, system Git fallback and downstream config isolation. Route Matrix binding through its existing dependency operation and explicit setup through sync_venv.

Real Git regression tests cover absent ambient PATH, changed selection, unsupported target and failed acquisition. Independent final review: 100 focused tests pass, including malicious Git config and Matrix setup contracts. Native Windows execution was not run.
2026-09-12 19:03:39 -04:00
ethernet
bb57c51795 Remove unused desktop update counter 2026-09-12 19:03:26 -04:00
ethernet
cfebdfd466 Own passive source update checks in Python
Use the exact target root and profile for branch, release, and cache decisions. Keep the desktop as a transport and handoff adapter. Remove the competing TypeScript checker and switch the banner, dashboard, and updater count consumers.

Preserve fork origins, unknown counts, publication checks, old-probe recovery, and official SSH branch healing through public HTTPS. Keep the historical unstamped-root policy unchanged.
2026-09-12 19:03:26 -04:00
ethernet
03d3c46697 fix(boot): keep shared launcher defaults profile-independent
Named-profile boot republishes shared source launchers. Derive their default home from the dependency root so a fresh default launch cannot inherit that profile. Explicit HERMES_HOME overrides remain unchanged.

The real-launcher regression failed before the fix. Focused tests: 109 passed, 37 skipped. Native Windows execution was not available. Two installer-stage failures also reproduce with the original writer.
2026-09-12 19:03:05 -04:00
ethernet
5c72dc0c6d fix(build): protect symlinked desktop source inputs
A source child can resolve outside the checkout. The output guard must
protect its canonical path before the builder checks prepared inputs.
Exclude generated dist/build trees so in-tree products can still rebuild.
Keep explicit prepared inputs protected even inside generated trees.

The public buildDesktop regression test first failed with a missing-icon
error instead of an overlap error. All 7 desktop-builder tests now pass,
including real cold/warm builds under apps/desktop/build/products.
Node syntax checks and git diff --check pass. No full suite or native
packaging ran.
2026-09-12 19:01:21 -04:00
ethernet
1686e54d4f refactor(boot): converge runtime checks and launcher maintenance 2026-09-12 19:00:52 -04:00
ethernet
690316c589 Give release Python sole ownership of App Installer descriptors 2026-09-12 19:00:39 -04:00
ethernet
7417158acd Let the shared output guard classify desktop products 2026-09-12 19:00:39 -04:00
ethernet
ea7299062c Remove unreachable parallel Store publisher 2026-09-12 19:00:39 -04:00
ethernet
7f82a86978 Make dependency workspace preparation fresh-generation only
Require the caller-selected source, output, seed and prepared environment. Delete reusable-root defaults, changed detection, seed precedence, replacement cleanup and fallback engine creation. Preserve frozen recorded-workspace replay; refuse existing outputs and missing explicit seeds.

Migrate lifetime tests onto fresh snapshots and real uv builds. Remove obsolete managed-uv default-lifetime tests, retaining explicit-engine routing coverage. Combined focused acceptance: 221 passed, 2 skipped; known stale node-sidecar and bionic PATH assertions excluded, along with unavailable Python 3.11 bootstrap.
2026-09-12 19:00:34 -04:00
ethernet
da076f3fa4 Unify pinned tool installation and cross-target staging
Use one acquisition, assembly, verification, replacement, rollback and cleanup path. Keep host facts and cross-target markers as concrete recording differences, including interrupted-entry recovery and verified Python copies.

Exercise both routes with real archive servers, pause/resume, multi-archive progress, post-publication failure and killed publishers. Keep native Windows directory-hold coverage gated to its host.
2026-09-12 19:00:34 -04:00
ethernet
c9ffa52757 fix(pm): stream verbose uv build-backend logs
Enable verbose uv output whenever PM streams a command so long native dependency builds expose package activity and backend stdout/stderr before failure.

Verify both sync and requirements installs with real offline uv builds that wait for their output to reach the parent. Both cases fail on the base and pass with this change.
2026-09-12 15:36:37 -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
ethernet
81b4c132b5 fix(build): normalize npm config names in dependency receipts 2026-09-12 14:06:35 -04:00
ethernet
21bf81a1ee fix: hide install stamp from git 2026-09-12 13:50:16 -04:00
ethernet
26c4e8b160 refactor(update): use PM and shared source builders
PM owns Python dependency generations. Shared frontend builders own Node
preparation and compilation. Route source updates and launchers through
these owners instead of separate repair ladders.

Remove obsolete live-venv holder gates and soft build-failure plumbing.
Preserve source validation, staged publication, fleet outcomes, and
historical relaunch hooks.

Verification: 956 tests passed in the combined focused run, with 43 skips.
After the final ZIP exit fix, 583 focused tests passed. Shared JavaScript
builder tests, Ruff, and diff checks passed. Native Windows/macOS and full
packaged-app builds were not run.
2026-09-12 13:45:08 -04:00
ethernet
cb17e9ba72 fix(build): allow more time for native wheel builds
Give native payload dependency builds two hours without changing the normal install timeout. Allow three hours for the standalone PM bundle job so setup, cache saves, and smoke tests fit around the build.

Verified seven focused tests, Ruff, and actionlint. Full CI builds were not rerun.
2026-09-12 13:20:02 -04:00
ethernet
2a0b69858d fix(build): reuse the shared PM cache during payload staging
Resolve the default cache before isolating HOME so payload builds use the directory that CI restores and saves. Remove the standalone bundle workflow dependency on an unset cache variable.

Verified offline wheel reuse in isolated children, 20 focused tests, Ruff, and actionlint. Full native release builds and the separate source-build timeout remain unverified.
2026-09-12 13:11:36 -04:00
ethernet
6380a4e0ab fix(build): reuse cached desktop npm dependencies 2026-09-12 11:53:49 -04:00
ethernet
d81c0a3fcc fix(termux): validate updater refusal by artifact identity
Commit builds have no update channel, even when installed through dpkg. Read the installed stamp before checking the refusal message and require exit code 2 for both artifact kinds.

Verified real CLI refusal paths, negative controls, and related tests: 15 passed. Ruff and type checks passed. Full deb validation was not rerun.
2026-09-12 11:07:42 -04:00
ethernet
3af8084671 fix(pm): support junction checks during legacy Python bootstrap
Python 3.11 fails while PM records the replacement interpreter's tree digest. The bootstrap cannot require Path.is_junction before it installs the managed Python.

Use Windows reparse metadata for junction detection in the store and data cleanup. Retain junction target protection without requiring the newer pathlib API.

Verified the exact failure on real Python 3.11 before the fix. Cold provisioning and source-update relaunch pass afterward. Targeted tests: 16 passed, one Windows-only test skipped on Linux. Ruff passed. The full suite and native Windows test were not run.
2026-09-12 11:01:12 -04:00
ethernet
3d5eff442b merge: concurrent input archiving and test guard fix 2026-09-12 10:38:23 -04:00
ethernet
4360d8df59 merge: preserve earlier pm-clean tip and its integration fixes 2026-09-12 10:38:23 -04:00
ethernet
38dfe6df50 merge: current main into consolidated PM and onboarding 2026-09-12 10:37:00 -04:00
ethernet
cade33cf7a perf(ci): archive all unique pinned inputs concurrently 2026-09-12 10:32:06 -04:00
ethernet
92bb5369ac fix(tests): preserve home guard tracking across descriptor reuse 2026-09-12 10:32:06 -04:00
Teknium
53c57871d6 feat(plugin-catalog): add snyk — Snyk MCP server + snyk-security-scan skill
Snyk lands as a standalone Agent Plugins v1 package
(NousResearch/hermes-plugin-snyk, pinned 2a41a07f) instead of an
optional-mcps entry: the package carries the pinned `npx -y snyk@1.1306.0
mcp` stdio launch with CLI analytics disabled AND the workflow skill that
tells the agent when to use which scanner, so a single `hermes plugins
install snyk` gives both the tools and the playbook. Third-party product
integrations ship outside the core tree per the contribution rubric.

Supersedes the optional-mcps manifest from #73860 (same pin, same
telemetry posture, tool-pruning rationale moved into the skill).
2026-09-12 06:46:17 -07:00
hermes-seaeye[bot]
3e09e5a15f fmt(js): npm run fix on merge (#109094)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-09-12 13:20:20 +00:00
Teknium
b0c383cdf7 fix(desktop): served profiles show running and route lifecycle to the multiplexer from a pooled local backend
Electron sends a local sub-profile's REST to its pooled `hermes --profile X serve` without
?profile=; inside that process the unscoped branches never reached the multiplexer rung, so a
profile served by the default multiplexer read as 'Messaging gateway stopped' on the system and
messaging pages, start/stop spawned a child that exited 78 while the UI reported success, and
restart ran `gateway restart` under X's HOME (same exit 78). Remote-backend topology was already
correct because its requests carry ?profile=.

Unscoped liveness/status/messaging now take the multiplexer rung for the process's own home;
lifecycle verbs resolve the own profile, refuse start/stop with 409 and restart the multiplexer via
-p default; Electron routes POST /api/gateway/{restart,start,stop} through the primary with
?profile= so the action lives on the backend the status poll asks and outside the pooled
backend's shutdown SIGTERM.
2026-09-12 06:13:44 -07:00
Teknium
be2f7e9c36 feat: curator prunes unused skills at 30 days (was 90), stale at 14
A skill nobody has loaded in a month is prompt weight, not knowledge, and
archival is recoverable (`hermes curator restore`). Defaults move
stale 30→14 / archive 90→30; config v44 rewrites only the OLD defaults so
an explicitly customized window is preserved. `hermes curator prune`
now defaults --days to curator.archive_after_days instead of a
hardcoded 90 so the manual and automatic paths agree.
2026-09-12 05:56:15 -07:00
Teknium
f364c19775 feat(plugins): touchdesigner ships as a catalog plugin (MCP server + skill), optional skill retired
The twozero TouchDesigner integration now lives in one installable unit:
plugin-catalog/touchdesigner.yaml points at NousResearch/hermes-plugin-touchdesigner
(portable Agent Plugins v1: mcp.json registers the twozero Streamable HTTP hub, skills/
carries touchdesigner-mcp). `hermes plugins install touchdesigner` + `hermes plugins
enable td` replaces the optional skill whose setup.sh hand-wrote an mcp_servers block.

The manifest name is `td` because Hermes names portable MCP tools
mcp__agent_plugin_<name>_<hash>__<server>__<tool>; twozero's longest tool under a
`touchdesigner` namespace is 71 chars, past the 64-char provider function-name cap.

optional-skills/creative/touchdesigner-mcp and its generated docs (bundled, optional,
zh-Hans) are removed; catalog tables, sidebar and kanban-video-orchestrator references
are updated to point at the plugin. Supersedes #68607 (MCP-catalog-only approach).
2026-09-12 05:15:22 -07:00
Teknium
876e444e4e fix(cli): open the session store through the state.db registry, not a bare SessionDB()
The classic CLI froze for ~0.7-2s between the banner and the first prompt. py-spy +
strace on real PTY startups showed the main/REPL threads inside
refuse_deleted_wal_generation -> _iter_proc_fd_targets: a second full SessionDB open.

_init_session_store built a bare SessionDB(); a moment later the goal/loop/heartbeat
managers acquired the same state.db through hermes_state_registry from the REPL thread,
which is a different handle, so the whole open ran again — including the /proc-wide
deleted-WAL sidecar scan (~4.4k readlinks). Each readlink drops and re-takes the GIL
while the startup threads (plugin discovery, MCP, skill sync, banner git) are busy, so an
11ms scan stretched to 1.3s per pass, and the second pass landed exactly where the
prompt should have appeared.

Route the CLI's handle (init + the two re-open sites) through the registry so every
in-process consumer shares one writer. One scan per startup; live A/B on the same box,
interleaved x6: banner->prompt gap 0.37s mean -> 0.15s mean (plain), 1.77s -> 0.39s
under strace. The registry release path replaces close(), so /quit, /snapshot restore
and /handoff keep their semantics.
2026-09-12 05:13:50 -07:00
teknium1
e8016a18c1 fix(cli): report the auto-detected context length when a custom provider is saved without one
Leaving the context-length prompt blank in the custom-endpoint wizard said
"will auto-detect" and then went silent, so users could not tell whether their
endpoint runs on a detected window or the runtime's default fallback (which
shapes compression and prompt-cache behaviour). After the save prompt, run the
same resolver the runtime uses (with the endpoint's URL and key) and print
either "auto-detected N tokens" or "not detected — using the default N tokens".
Feedback only: the probe result is not persisted, and a failing probe never
blocks the save.

Fixes #2513. Approach from PR #2522 (@ygd58) and PR #85499 (@Luna161), both
written against the pre-decomposition wizard module.

Co-authored-by: Luna161 <268031236+Luna161@users.noreply.github.com>
2026-09-12 05:10:51 -07:00
teknium1
08bb58bd4a fix(skills): never call a rate-limited fetch a stale index entry; drop per-search caveat
A throttled GitHub fetch also yields index-metadata-without-bundle, so the new
stale-entry verdict would tell users a skill "no longer exists upstream" when
it does. Check the adapters' rate-limit flag first and keep the existing
rate-limit hint for that case (the keep_open review concern on #3261).

The per-search "results may be stale" note is dropped: it fires on every
skills.sh search whether or not anything is stale, and the install-time error
now names the condition precisely where it happens.
2026-09-12 05:10:31 -07:00
nikkoxgonzales
257a704d18 fix(skills): name stale index entries instead of generic fetch failure
_resolve_source_meta_and_bundle already distinguishes index-hit-without-
files from unknown identifiers, but do_install printed the same generic
'Could not fetch' for both, sending users off to re-check spellings for
what is actually a stale skills.sh entry. Split the message, and add a
staleness caveat to do_search results from skills.sh.

Fixes #3259. Supersedes #3261 (stale since July — re-applied onto the
current _print_fetch_failure helper).
2026-09-12 05:10:31 -07:00
nikkoxgonzales
75ade17617 fix(telegram): normalize unicode dashes in bot menu descriptions
BotFather rejects setMyCommands descriptions containing em/en dashes
(U+2012-U+2015, U+2212). Fold them to ASCII hyphen at the two Telegram
sinks (telegram_bot_commands, telegram_menu_commands) so core, plugin,
and skill entries are all covered.

Fixes #2925.
2026-09-12 05:10:11 -07:00
Teknium
1b1f5c4abe test(desktop-update): drive linux_gate through --self-test-gate; cover both symlink spellings
Replaces the source-extraction harness (regex-lifting the function body and
asserting 'readlink -m' is present in the text) with the script's own
--self-test-gate entry point, and adds the canonical-root + symlinked-target
spelling from the #108867 report. Two invariant tests, linux_only.
2026-09-12 05:09:59 -07:00
MatthewHines
31bf8e504c fix(update): address review — neutral gate comment, pin empty-target skew case
- Rewrite the linux_gate comment to describe the environment fact
  (symlinked /home, kernel-canonicalised /proc/<pid>/exe) without
  local-patch markers or the unfiled-issue reference.
- Add test_empty_relaunch_target_falls_to_skew pinning the [ -n ... ]
  guard behavior so it cannot be simplified away.
- Add the missing trailing newline to the test file.
2026-09-12 05:09:59 -07:00
MatthewHines
d3b090a397 fix(update): canonicalise relaunch-target paths before the linux skew gate
The linux relaunch gate compares the running desktop's exe path
(relaunch target, read from /proc/<pid>/exe — kernel-canonicalised)
against the checkout's unpacked-app prefix with a raw case-pattern.
On hosts where /home is a symlink to /var/home (e.g. Fedora), the two
sides spell the same tree differently (/home/... vs /var/home/...) and
the gate false-positives "skew", telling the user to reinstall the
desktop app after every successful self-update.

Canonicalise both sides with readlink -m (which resolves existing
leading components without requiring the full path to exist, unlike
-f) before the prefix compare. No-op when both sides already agree.
2026-09-12 05:09:59 -07:00
ms-alan
0aa08a835e fix(toolsets): use typing.Any instead of builtin any in get_distribution return type
`Optional[Dict[str, any]]` annotated the value type with the builtin `any()`
function rather than `typing.Any`; static checkers reject it and the intent is
`Any`. Add `Any` to the typing import and fix the annotation (#2139).

Re-authored to the PR author's GitHub noreply identity: the original commit
carried an empty author email (misconfigured local git, not malice).
Salvaged from PR #20812.
2026-09-12 05:09:50 -07:00
Teknium
39ab25a6ac test(mem0): drop the inside-the-cap no-op case (salvage bar: 2 invariant tests) 2026-09-12 05:09:43 -07:00
John Paul Soliva
aca4dc86a3 fix(memory/mem0): trim a synced message at the last sentence boundary, not the first separator kind
_truncate_for_sync documents "the last sentence boundary within max_len", but it
looped over separator KINDS and returned on the first kind that qualified. An early
"。" therefore outranked a "." 240 characters later, and in pure ASCII "." outranked
a later "!" or "?" purely because it comes first in the tuple.

With the 450-char default, "a"*200 + "。" + "b"*240 + "." + "c"*100 kept 201 of the
442 characters available: 241 characters the embedder would have accepted were
discarded, so any fact in the second half of the turn never reached extraction. The
add() call succeeds, so unlike #106235 nothing is logged — the turn is simply
remembered from its first sentence. Raising sync_max_chars widens the gap rather
than closing it.

Take the max over every separator instead, from a named tuple so the set is not
buried in the loop. ".\n" is dropped: its index can never exceed the bare "." it
starts with, so under a max it is unreachable. The first-third guard and the hard-cut
fallback for unsegmented input are unchanged.

Fixes #108868
2026-09-12 05:09:43 -07:00
Teknium
e705dde6e4 chore(tests): pass encoding=utf-8 in test_skills_guard file writes (windows-footgun ratchet) 2026-09-12 05:09:26 -07:00
Teknium
596bd8fec6 fix(skills_guard): dns_exfil no longer fires on the English noun "host" in prose
`(dig|nslookup|host)\s+[^\n]*\$` matched any line where the word "host"
was followed, anywhere later, by a `$` -- "Set the host value and run
`${SKILL_DIR}/scripts/check.py`" was a CRITICAL DNS-exfiltration finding
that blocked a one-file community skill from installing (#108873).

DNS exfiltration puts the data in the queried NAME, so the pattern now
requires the interpolation in the first positional argument (after
optional -flags with values, +opts and @server). Real `host $SECRET.x`,
`dig @1.2.3.4 +short $TOKEN.x`, `nslookup -type=txt "$KEY".x` and
`host -t txt ${API_KEY}.x` still flag; the llama.cpp `--host ... $PORT`
exemption is preserved.
2026-09-12 05:09:26 -07:00
Teknium
bed4abd106 fix(lsp): look up nested single-root clients and version docs before didChange send
Two LSP freshness bugs reported by @tobific (#108882, #108881):

- `_current_diags_async()` keyed the client lookup by the enclosing
  workspace root while `_get_or_spawn()` stores single-root servers under
  `srv.resolve_root(...)` (a nested package.json project). The lookup
  returned [] for a live client with diagnostics, so the delta baseline was
  refreshed from nothing. Use the same resolved-root key.

- `open_or_change()` published `_DocState.version` only after awaiting the
  didChange write. A versionless publishDiagnostics read during that await
  was credited with the OLD version and judged stale once the send resumed.
  Bump the version before the send; a failed send (swallowed by
  `_send_notification`) leaves a version nothing satisfies, i.e. "no
  verdict", which is the existing contract.

The mock server gains a push-only `versionless` script so the race is
reproducible without a real language server.
2026-09-12 05:09:11 -07:00
Teknium
a9cfcf70c1 fix(desktop): forward optional composer handlers through the latest-actions adapter
latestChatActions rebuilds the ChatView handler bag field by field, so an
optional handler added to ChatActions but not to the adapter is silently
dropped before it reaches ChatView. Live CDP probe on a built Desktop: the
wiring controller had onAttachPastedText, ChatView received undefined, and
a 4,500-char paste stayed inline. onAttachPrCommentUrl and onSteerHidden
(already on main) were dropped the same way on the main chat surface; the
session-tile path passes them directly and was unaffected.

Forward all three via latestOptional and pin the class with one invariant
test: every handler present on the actions bag is present on the adapted
bag (red on the previous adapter).
2026-09-12 05:09:01 -07:00
Teknium
ab7f03049e refactor(desktop): large-paste writer in its own electron module, 3k threshold
Move writeComposerPaste out of electron/main.ts into composer-paste.ts
(placement gate: no new behaviour appended to the facade). Lower the
conversion threshold from 10k to 3k characters so a pasted stack trace or
log excerpt already becomes a chip. Trim the policy tests to two
invariants (strict threshold boundary; chip size label is byte-based) and
drop vendor references from code comments.
2026-09-12 05:09:01 -07:00