Commit Graph

47208 Commits

Author SHA1 Message Date
teknium1
85e11aaa00 fix(profiles): carry install records and skip install staging when cloning plugins
The light clone now copies plugins/ through the profile copier: bytecode caches,
sockets and the installer's in-flight .install-*/.update-* staging dirs stay
behind, while .install-metadata.json (revision + catalog provenance) travels, so
a catalog-installed memory provider runs at the source's revision in the clone
instead of resolving nowhere. The CLI names the plugins it carried.

Co-authored-by: Eri Barrett <eri@plasticlabs.ai>
2026-10-02 01:29:41 -07:00
CortonKwok
810c31c4c7 test(profiles): add regression test for plugins/ clone 2026-10-02 01:29:41 -07:00
CortonKwok
983bc7528c fix(profiles): clone plugins/ when cloning a profile
When creating a profile with --clone or --clone-from, the source
profile's plugins/ directory is now copied to the new profile
alongside config, skills, and memory files.

Symlinks in the source plugins/ are preserved as symlinks
(symlinks=True). The destination is not overwritten if it already
exists (dirs_exist_ok=True), matching the skills clone pattern.

Fixes plugin hooks not firing on non-default profiles (#50937, #65593)
by ensuring plugins are present in the new profile's plugins/
directory.
2026-10-02 01:29:41 -07:00
teknium1
999506c79f feat(plugin-catalog): list retaindb, byterover and holographic memory providers (Nous-maintained) 2026-10-02 01:29:22 -07:00
kshitijk4poor
b7200efef0 docs(models): get_codex_model_ids documents its fallback return 2026-10-02 13:34:54 +05:30
kshitijk4poor
958585688d test(models): drop the codex retain-row test the generic fallback tests cover 2026-10-02 13:34:54 +05:30
kshitijk4poor
7793c19660 refactor(models): one fallback marker for every catalog fetcher
get_codex_model_ids returns CuratedFallbackModels directly instead of a
second codex-only marker that _codex_catalog re-wrapped; the class moves to
the static catalog leaf so codex_models can use it. The catalog identity
normalizes its route with normalize_route_base_url like every other route
key.
2026-10-02 13:34:54 +05:30
kshitijk4poor
4a83590576 test(models): drop the -900k picker test the salvage carried in
main already pins the adjacent -900k variant for every verified slug
(test_picker_synthesizes_900k_variants_for_verified_slugs); the copy was
unrelated to the fallback fix.
2026-10-02 13:34:54 +05:30
AhmetArif0
ba2a38e771 fix(models): mark the Anthropic curated fallback as a placeholder
_anthropic_catalog() returned the curated list as a plain list when the live
/v1/models fetch failed, so a transient outage on a proxy base_url was cached
with the full 1h TTL as if it were the account's/proxy's real catalog, and
neither a same-credentials restart nor Refresh Models could shake it before the
TTL lapsed. Wrap the fallback return in CuratedFallbackModels, the placeholder
type #107391 introduced for the same class of bug.

Salvaged from #121248 (the Anthropic half; the Codex half lands via the
typed-fallback commit before this one).
2026-10-02 13:34:54 +05:30
FroRaut
3fbace2e76 fix(models): keep account-gated Codex rows when discovery falls back
A failed ChatGPT Codex catalog fetch returned the static offline hints as if
they were the account's catalog, so the picker cached them and dropped
account-gated rows such as GPT-6 Astra until the cache expired. The hints are
now typed (CodexFallbackModels) and cached as short-lived curated fallback
rows that never replace a verified account fetch, and the catalog cache key
includes the resolved route, not only the ChatGPT principal.
2026-10-02 13:34:54 +05:30
kshitijk4poor
9fc5ad903a chore: map Froraut's commit email for attribution 2026-10-02 13:34:54 +05:30
Nicolò Boschi
10c6188de1 catalog(hindsight): pin the released v1.2.1
v1.2.1 adds the first-run notice for the embedded server download and serialises client creation
so the start worker and the first memory operation cannot each start a daemon.
2026-10-02 00:01:08 -07:00
Nicolò Boschi
0ab1bb1d94 catalog(hindsight): pin the released v1.2.0 2026-10-02 00:01:08 -07:00
teknium1
95db0e2ada test: read the batch probe log BOM-tolerantly
Windows footgun gate: reads use utf-8-sig.
2026-10-01 23:50:31 -07:00
teknium1
9de7853a05 docs(tool-search): describe local tool_call batch splitting
The page still said multi-local and mixed tool_call batches are rejected.
The agent now splits a batch containing local tools into one call per
entry; document that and the connector-only dispatch.
2026-10-01 23:50:31 -07:00
Ryan Hill
2ce704550b fix(agent): execute local tool batches through per-tool pipeline 2026-10-01 23:50:31 -07:00
teknium1
2e9401a6c0 test(e2e): compare against the real checkout identity, not the root seed
E2E tests diff in-process get_version_info() against a real hermes process
(Windows boot lifecycle: --version must report this checkout). Drop the
root conftest's seed for the e2e tree; those processes are heavy anyway.
2026-10-01 23:49:36 -07:00
teknium1
7c338c9ba3 test: seed the version cache once per test process instead of shelling out to git
Each test file runs in its own process, and the first User-Agent build in a
checkout without an install stamp runs 7 git subprocesses. In a large local
clone that is ~0.5 s per file (tests/cron: 91 s -> 54 s wall, 610 -> 336 CPU-s).
CI's depth-1 checkout pays only ~7 ms, so the seed uses the identity CI already
produces. Tests that exercise resolution reset the cache themselves.
2026-10-01 23:49:36 -07:00
teknium1
6174e342e1 fix(tests): a nested test runner must not inherit the outer runner's slice
The runner's own probe tests start runners from inside a test process,
which inherits HERMES_TEST_SLICE from the CI slice job. Slice 1/2 then
ran half of each probe's file set and slice 2/2 ran none of a one-file
probe ("probe never wrote handoff file"). Read the slice once and drop
it from the environment.
2026-10-01 23:49:20 -07:00
teknium1
093f8d4480 ci: run the Python suite and the macOS lane as two slices each
The full suite was the last job to finish in 33 of 43 CI runs since it
moved from a 96-core to one 32-core runner (p50 666 s). That runner is
~90% packed, so only more machines help, and one bigger box is poor
value: on 96 cores the same files took 25-50% longer each and the suite
ran ~1.8x faster for 3x the price. Two 32-core slices scale linearly
(simulated from per-file CI timings: 262 s / 252 s vs 514 s on one box).

macOS (free 3-core runner, ~7.5 min) would become the slowest required
job once Linux splits, so it runs as two slices on the same free runner.

--slice now partitions from the checkout alone (sorted round-robin):
each slice job restores its own duration cache, and a main push saving
a newer cache between the two restores made timed LPT compute different
partitions per job, silently dropping or double-running files.
Round-robin lands within 5 s of timed LPT at this suite size. Each slice
saves its own cache key on main; the runner keeps the restored entries
for files outside its slice, so the cache stays complete.
2026-10-01 23:49:20 -07:00
teknium1
013b776ab0 ci(label-rerun): never rerun a CI run whose PR head has moved
Adding ci-reviewed starts a job that waits for the PR's CI run, then reruns
its failed jobs. A push during the wait cancels that run, the waiter sees
"completed", reruns it, and the rerun rejoins the per-PR concurrency group
and cancels the new head's run. The PR is left testing a stale commit (seen
on #131236 and #131246). Skip the rerun when the head moved: the new run's
own event already carries the label. Same guard on the run-e2e job.
2026-10-01 23:49:01 -07:00
ethernet
00373b5376 fix(plugins): memory provider setup lists the dependencies PM would install
The memory-provider setup UI labelled a provider's Python dependencies from the manifest's
`pip_dependencies` / `python_dependencies` only. PM installs from an authored `pyproject.toml`
when one exists (it wins over the manifest lists), so a provider that declares its requirements
there showed an empty list, or a stale one, while the install button prepared different packages.

Read the same `read_python_declaration(...).install_requirements` PM consumes, so the label
matches what would be installed, including PM's own filtering (hermes-agent self-dependency,
direct URLs, `python_runtime: external`).
2026-10-01 23:27:08 -07:00
hermes-seaeye[bot]
f7da80073a fmt(js): npm run fix on merge (#131251)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-10-02 06:22:10 +00:00
teknium1
2fa2f42d41 docs(desktop-sdk): document model menu row decorations and data-session-switching
Both seams are public contracts for plugin/theme authors (first consumer:
t3-code-theme, #129600); the page documents the arbitration, failure
isolation and lifetime guarantees so a theme never needs DOM reads.
2026-10-01 23:09:43 -07:00
teknium1
b33c32fcf3 feat(desktop-sdk): data-session-switching marker on the chat surface
Theme CSS had no supported way to style a chat switch: t3-code-theme's
installSwitchFade watched the focus store, polled transcript rows and
scroll position in a rAF loop and toggled its own root attribute — DOM
reads catalog rule 8 now refuses.

Core already knows both phases of a switch, so it publishes them: the
chat surface root ([data-chat-surface], per pane) carries
data-session-switching="true" while a routed session is loading
(Thread loading === 'session') and while the thread list's scroll
restore places the new rows, removed on settle, user input, failed
resume or unmount. Each phase holds a token; the attribute goes when the
last holder releases, so the load ending never clears the restore's
mark mid-switch. An attribute (not a view-transition or motion token)
because the need is "know when", the duration is core's real settle
time, and CSS composes it with any theme without JS.
2026-10-01 23:09:43 -07:00
teknium1
c0c029c376 feat(desktop-sdk): model menu row decorations (MODEL_MENU_ROW_AREA)
Plugins could relabel the model pill (COMPOSER_AREAS.modelPill) but the
menu it opens was core-only, so a theme wanting provider marks next to
each model (t3-code-theme, catalog #129600) had to MutationObserve the
open menu and paint into its rows, which catalog rule 8 now refuses.

A `modelMenu.row` data contribution supplies decorate({ provider, model,
label }) -> { icon?, badge? } | null. ModelFamilyRow paints the answer in
two fixed slots it owns: a leading 1rem icon box before the name and a
trailing chip after core's own chips. Per slot the first usable answer in
registry order wins, so an icon plugin and a badge plugin compose; a
throwing decorator or a non-element/non-string value declines, and the
icon renders inside its own error boundary so a crashing plugin component
blanks only its slot, never the menu. Applies to every ModelCatalogMenu
host (composer pill, kanban override), since a mark belongs to the model.
2026-10-01 23:09:43 -07:00
Angello Picasso
36922ad064 feat(plugin-catalog): add go-whatsapp 2026-10-01 22:53:10 -07:00
teknium1
e5df4320f6 fix(gateway): /fast and its Ultrafast gate judge the session's own route
/fast now asks the same question the next turn asks: resolve_fast_mode_overrides
over the route _resolve_session_agent_runtime returns (session /model override,
channel override, persisted override after a restart), with provider and
base_url. Both the Priority gate and the Ultrafast gate added in #128307 read
that one route, so GPT-6 Astra picked with session /model on openai-codex gets
/fast and /fast ultrafast, and a fast-capable default no longer admits a
session route whose requests never carry the tier (gpt-5.4 via OpenRouter).

The tier override is also stored under the topic-recovered session key, the
one the next turn reads (#30479 class; /model and /reasoning already do this),
so on Telegram DM topics a session /fast is no longer dropped.

Route resolution runs off the event loop (it can refresh an OAuth token, as
the turn does); with no usable route at all the configured model is judged
alone, as before.

Tests: the four salvaged cases collapse into two invariants on the real
fast-mode tables (both gate directions + the turn's request_overrides, and
the recovered-topic key); red on origin/main.
2026-10-01 22:47:04 -07:00
Willhong
cd2c5cf9af fix(gateway): resolve /fast eligibility against session model override
Route /fast eligibility through _resolve_session_agent_runtime (the same
resolver normal turns use) instead of the global _resolve_gateway_model,
so a session-scoped /model override or channel override keeps /fast in
sync with the model the next agent turn actually routes to. Normalize the
command source exactly like /model does so the override is read under the
key it was stored, and let the resolver rehydrate persisted overrides a
gateway restart cleared from memory.

Rebased onto current main (was DIRTY): the lazy import in
_handle_fast_command now pulls only _load_gateway_config; the sibling
picker tests in test_choice_picker.py::TestFastChoicePicker are updated to
stub _resolve_runtime_agent_kwargs (matching test_fast_command.py) so the
no-override path doesn't hit real provider resolution in CI.

Tests: tests/gateway/test_fast_command.py (10) +
tests/gateway/test_choice_picker.py::TestFastChoicePicker (4) pass.

(cherry picked from commit 9885267a6f6e124b470fa2f597ef9c3623063549)
2026-10-01 22:47:04 -07:00
teknium1
6dbb7b9ab8 chore: map contributor email for 0bull 2026-10-01 22:41:14 -07:00
teknium1
b4152ea407 plugin-catalog: 0bull — add the risk disclosure to the description
The reviewed behaviour stays as is; the catalog entry now states it so users see it before installing.
2026-10-01 22:41:14 -07:00
0bull
7d67d552c4 feat(plugin-catalog): add 0bull community plugin 2026-10-01 22:41:14 -07:00
teknium1
7372e373d6 chore: map contributor email for jacobhausler 2026-10-01 22:40:44 -07:00
teknium1
acadf3c415 plugin-catalog: hermes-workflows — add the risk disclosure to the description
The reviewed behaviour stays as is; the catalog entry now states it so users see it before installing.
2026-10-01 22:40:44 -07:00
Jacob Hausler
24d216c3e9 plugin-catalog: add hermes-workflows (community, automation) @ v1.1.3
Pins jacobhausler/hermes-workflows at edc8b3b (v1.1.3). Consolidates the PR history
(v0.9.0 -> v1.1.3 re-pins) into one commit by the plugin author.
2026-10-01 22:40:44 -07:00
teknium1
7f7e1ab402 fix(desktop-update): any failure after the banner keeps a killed update failed
The 124 remap was vetoed only by "Update not complete", but the restart
and verify phase prints other failure lines after "Update complete!":
"Update incomplete — …", "✗ <unit> failed to come back/stay running
after restart", "verification incomplete". Any of them now keeps the
watchdog kill a failure. Only text after the last banner is inspected,
and ✗ is matched as \u2717 because Windows PowerShell reads the BOM-less
script as ANSI.

Reported by @unsupportedpastels in review.
2026-10-01 22:35:29 -07:00
teknium1
00302e636c fix(update): a same-commit update that failed still owes its tail on launch
The completed-tree shortcut treated "stamp names HEAD" as "nothing is
owed". But `hermes update` at an unchanged commit ("Already up to date")
re-arms the completion marker before running the tail; when that tail
fails or is killed, the old stamp still names HEAD and the next launch
silently cleared the marker instead of retrying it.

The stamp only proves the tail that wrote it. A marker is now discharged
only when it predates the stamp (the #123314 leftover from a previous
install); one armed after it is retried as before. The shortcut also
requires a clean tree on both sides: a stamp recorded dirty, or local
edits since, are no proof that the built products match the checkout.

Reported by @unsupportedpastels in review.
2026-10-01 22:35:29 -07:00
teknium1
91e8f7595d refactor(desktop-update): keep only the reachable 124-after-completion remap
The Windows hand-off's idle watchdog is the only producer of exit 124 on
main (posix.sh has no watchdog and `hermes update` never exits 124), so
the posix remap was dead and goes. The retry-state helper and its
self-test arm collapse into one Resolve-HermesUpdateOutcome check, now
proved end to end through the real hand-off instead: a fake update that
prints its banner and parks silently past a 3 s ceiling surfaces as a
verified success with the gateway fleet restored.

The remap also refuses a run whose output already carries the
"Update not complete" verdict, which supersedes the banner.
2026-10-01 22:35:29 -07:00
teknium1
38254027bf fix(update): an adoption stamp never discharges an owed completion tail
Boot-time adoption of an unstamped blessed checkout publishes the same
identity a finished install/update does (commit == HEAD) but builds
nothing. Read as completion evidence, the launch that adopted (and every
later one) cleared a pending source-completion marker whose tail never
ran, and nothing on disk recorded the debt afterwards.

Adoption now stamps `adoptedAt`; the completed-tree check ignores such
stamps, and the next real completion rewrites the stamp without it. The
check also drops its blanket try/except: both reads it wraps never raise.
2026-10-01 22:35:29 -07:00
Brooklyn Nicholson
1b0d8aa3d5 fix(desktop-update): completed update killed with exit 124 is terminal, never re-run (#96205)
Port of #96340 (Finn763) onto the current legacy-scoped retry gates,
narrowed to the watchdog sentinel only: exit 124 + the "Update complete!"
marker maps to success so the hand-off verifies and relaunches the new
build instead of re-running the completed update (the 70+ minute popup
retry storm). A plain exit 1 after the marker is the stale-gateway
verdict (hermes_cli/update_receipt.py) and must keep failing.

As first written this also added a posix.sh gate and a retry-state
helper with a -SelfTestRetryState arm; the next commit drops the posix
gate (nothing there exits 124) and replaces the helper and self-test
with one end-to-end test through the real hand-off.

Co-authored-by: Finn763 <165816600+Finn763@users.noreply.github.com>
2026-10-01 22:35:29 -07:00
finn763
6770bc9578 fix(update): skip source-update completion when the stamp names HEAD
A fresh install records its tree in install-stamp.json; a first launch under a preserved or pristine home then re-entered the completion tail against the same SHA (stale pending marker, or no per-home facts). When the stamp commit is HEAD the products were already built for this tree: provision a stale venv without arming, discharge leftover markers, and run the agent. Fixes #123314.
2026-10-01 22:35:29 -07:00
teknium1
e9d7a18dc5 fix(desktop): embed host answers malformed paths with 404 instead of throwing
`new URL("//", origin)` threw inside the http request handler, so a
request like `GET //` escaped as an uncaught main-process TypeError and
was never answered. Reject unparseable targets up front.
2026-10-01 22:27:30 -07:00
teknium1
25aa38c008 fix(status): one row per messaging platform, judged by the gateway (#96190)
`hermes status --full` printed two lists for the same platforms: a legacy
table judged by one env var, then "(plugin)" rows judged by the gateway.
Slack configured in config.yaml showed "✗ not configured" and
"✓ configured (plugin)" in the same section, and the legacy verdict could
disagree with the summary's Platforms line.

Render one list from the gateway config instead: registry entries bring
their labels, a small label table covers built-in adapters without one,
"configured" is get_connected_platforms() (the summary's verdict), and the
home channel comes from the loaded config, which also fixes the legacy
table reading QQ_HOME_CHANNEL where the gateway reads QQBOT_HOME_CHANNEL.

Dedupe idea from #54131; root-cause analysis of the check_fn verdict from
#96200.

Co-authored-by: igparkernz-coder <297591502+igparkernz-coder@users.noreply.github.com>
Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
2026-10-01 22:27:03 -07:00
teknium1
a184f85924 docs: explain Desktop's default vs Classic Hermes and the reserved classic name
On Desktop `default` and /skin default mean Nous (a CLI/TUI or config switch
to `default` moves a Desktop pick back to Nous), the gold look is the
separate `classic` theme reached by /skin classic, gold or hermes, and
`classic` joins the Desktop built-in names a user skin file cannot recolor.
2026-10-01 22:27:00 -07:00
teknium1
9b6e759751 fix(desktop): one Classic Hermes card after the reverted build, and a light Classic
A Desktop that ran the reverted #130015 build has the CLI `default` skin
cached under `hermes-desktop-backend-themes-v1`, labelled "Classic Hermes".
readCached only dropped built-in names, so that entry survived next to the
new `classic` theme: two Classic cards, the stale one previewing gold and
applying Nous, and two Classic rows in /skin list. readCached now also drops
every retired name (`default`, `gold`, `nous-light`, moved to presets.ts as
RETIRED_SKINS so the store and the theme provider share it) and writes the
cleaned map back, so the entry is gone from disk on the first launch. The
same rule keeps ingestBackendSkin from registering a theme under a retired
name.

Classic also ignored light mode: it took the single-mode converter output,
dark palette in both slots. Its light variant now comes from the CLI
default skin's `light_colors` ladder (#F5F5F5 surface, goldenrod accent)
with a deeper ink of the same brown, so Desktop's 74% / 54% secondary and
tertiary text land at Nous Light's contrast (6.5:1 / 3.5:1; body 12.3:1).
Dark is unchanged. The description now reads "Gold on navy, the CLI's
original look" and the `as DesktopTheme` cast is gone.
2026-10-01 22:27:00 -07:00
teknium1
77f54c5c35 feat(desktop): offer Classic Hermes gold/navy as an explicit theme pick (#76579)
Desktop users can now choose the CLI's classic gold-on-navy look in
Settings > Appearance (and via /skin classic, gold or hermes).

It ships as a Desktop built-in under its own id `classic`, converted from
the CLI `default` skin colors through the same skinToDesktopTheme path a
backend skin takes. It is deliberately NOT registered under `default`:
stock config ships `display.skin: default`, and backend sync / the boot
fallback adopt that value, so un-retiring `default` (the reverted #130015)
repainted every user who never picked a theme in Classic gold and cached
it. `default` stays retired and aliased to Nous; Classic is only painted
when someone picks it. Desktop never writes its pick to display.skin, so
the pick stays Desktop-local like every other Desktop theme.

Re-lands #130015 (reverted in #130852).

Co-authored-by: 686f6c61 <github@00b.tech>
2026-10-01 22:27:00 -07:00
teknium1
b5be2a3c8b refactor(desktop): render shell menu items directly
Call renderActionItem instead of declaring an Item component inside
shellSections, which minted a new component type on every render and
remounted the menu rows. Restore the tree-group file doc comment above
its imports.
2026-10-01 22:15:59 -07:00
unsupportedpastels
63867308bb test(desktop): consolidate pane ownership invariant coverage 2026-10-01 22:15:59 -07:00
unsupportedpastels
40a2637fae fix(desktop): scope pane fallback menus to the clicked content
Keep explicit nested menus and bare pane operations while restoring text, editable, link, and image menus. Reuse the app actions in a fallback section of the pane-body menu.
2026-10-01 22:15:59 -07:00
Yuan Li
49f5f78e50 test(desktop): cover editable targets inside context triggers
(cherry picked from commit 9b77c43048a040e77084efc376fde1530ddee617)
2026-10-01 22:15:59 -07:00