Commit Graph

4106 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
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
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
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
a3149156ec feat(cli): make the summary the default hermes status, --full for every section
`hermes status` now prints the one-screen summary (model, provider, connected
providers, gateway, platforms, jobs); `--full` (alias `--all`, which never
changed the output) and `--deep` print every section as before. Hermes
Console keeps the full report since it accepts no flags.

The summary reads the canonical sources instead of re-deriving them:
- Providers: list_authenticated_providers, the list the /model picker offers
  (cached catalogs only, no custom-endpoint probes), so it honours
  model_catalog.excluded_providers, pools and plugin providers for free.
- Platforms: GatewayConfig.get_connected_platforms, the gateway's own
  decision about which adapters to start (it also rejects placeholder tokens).

The full Messaging Platforms section now uses the same verdict for plugin
rows. It called check_fn, a passive "SDK importable" probe that is True for
every bundled plugin, so a fresh install listed Discord, Slack, Matrix,
Teams, WhatsApp and ~9 more as "configured" with no credentials.
2026-10-01 22:03:11 -07:00
PhoeniXAbhisheK
c82ad613ec feat(cli): add hermes status --short
Compact, secret-free summary of the component status: model, provider,
connected providers, gateway, messaging platforms, and scheduled jobs.

The provider list is registry-driven (PROVIDER_REGISTRY API-key rows,
refresh-free OAuth getters, plugin-mirrored OAuth providers, the
OpenRouter rung, configured custom endpoints), so new providers and
endpoints surface without touching the short renderer. Plugin-platform
configuredness prefers the is_connected hook over the deps probe (same
rule as hermes setup and the #102183 fix); every lookup is fail-soft,
so a failing probe degrades one row, never the command.

Full `hermes status` output and the console_engine call shape are
unchanged. Docs: website/docs/reference/cli-commands.md. Tests: 8 new
cases in tests/hermes_cli/test_status.py (25/25 pass via run_tests.sh).
2026-10-01 22:03:11 -07:00
teknium1
fd6a6fbf2e docs: relative links in the catalog submission pages (docs-site link check) 2026-10-01 21:58:11 -07:00
teknium1
d802c47238 docs: one complete plugin catalog submission guide, synced with the README rules
Submitters only saw a stale 5-point summary on the catalog page (it even listed
a 'Released' requirement nothing enforces), while reviewers applied ten README
rules plus criteria written nowhere public (credentials, approvals, disclosure,
compatibility metadata, forks of bundled plugins).

- plugin-catalog/README.md: rules 11-15 write down those review criteria; the
  rule list sits between admission-rules markers.
- New developer-guide page 'Submitting to the plugin catalog': before you
  submit, the PR flow, the full rule block (verbatim mirror), pin updates,
  delisting vs removal, asking for a hook.
- The /plugins 'Submit' links and the user-guide section point at it.
- tests/website/test_catalog_rules_mirror.py fails when the two rule blocks
  differ; classify_changes makes a README-only edit run it.
- AGENTS.md, plugins/AGENTS.md and CONTRIBUTING.md reference both files.
2026-10-01 21:58:11 -07:00
Superfly73
336b557bec fix(gateway): skip cron runs in their own restart-safe scope in the restart wait
A cron run handed to an external worker in its own transient systemd scope
outlives the gateway: the shutdown drain deliberately leaves it unmarked
(``mark_running_jobs_interrupted``) and its final send rides the durable
delivery queue, drained by whichever gateway is live next. The after-turn
restart wait still counted it as active work, so ``/restart``, SIGUSR1 and
``hermes update`` held the gateway in ``draining`` — refusing every new turn —
for up to ``agent.restart_after_turn_timeout`` (30 min by default) while the
worker kept running regardless. Observed in the field: a 100-minute job turned
a restart into a ~30-minute lockout.

- ``cron.scheduler`` records whether an accepted handoff got its own scope
  (``dispatch.mode == "scoped"``) and exposes
  ``get_restart_safe_external_job_ids()``; runs already reported as wedged are
  excluded there too, because the gateway subtracts both counts and a run in
  both sets would be subtracted twice.
- ``_awaitable_work_count()`` subtracts that count as a second, disjoint
  exclusion; ``_describe_active_work()`` marks the unit (``restart_safe``).
- A degraded worker (external, but no reachable user bus, so it shares the
  gateway cgroup) is NOT listed and is still awaited — the restart would kill
  it mid-run.
- The wait and observation log lines now name both exclusion classes instead of
  claiming everything still listed will be interrupted.

Tests: tests/gateway/test_restart_drain.py (skip + control),
tests/cron/test_restart_safe_worker.py (registry contract).
Docs: updating.md restart section, cli-config.yaml.example.

(cherry picked from commit 6a2f0b77cf07a60510430fc4ef8b75539ee422fb)
2026-10-01 22:25:21 -04:00
teknium1
a959a6ede1 fix(wecom): split long messages instead of silently truncating them
Both WeCom adapters dropped everything past the platform limit with no
error: the AI Bot adapter sliced markdown content at 4000 chars, and the
callback (self-built app) adapter sliced text at 2048 *characters* while
message/send keeps only the first 2048 *bytes* (so CJK replies lost
everything past about 680 characters). Cron output was additionally cut
to 4000 chars with a gateway-host file pointer.

AI Bot: send() queues one markdown message per MAX_MESSAGE_LENGTH chunk,
so every chunk draws its own token from the 30 msgs/min per-chat bucket
(errcode 846607), and each goes out on the cached reply req_id like any
other send in that chat (group chats cannot send proactively). Callback: chunk by UTF-8 bytes at 2048.
Both declare splits_long_messages so cron delivery hands over the full
payload. Each stops at the first failed chunk and returns its error.

Fixes #19689
Fixes #107443

Co-authored-by: luyao618 <364939526@qq.com>
Co-authored-by: briandevans <252620095+briandevans@users.noreply.github.com>
Co-authored-by: LeonSGP43 <cine.dreamer.one@gmail.com>
Co-authored-by: Andy <sz.private@gmail.com>
2026-10-01 17:18:56 -07:00
dhruv.kejriwal
164a697d0c fix(cron): ignore measured durations in the incident error signature
The inactivity watchdog polls every 5s, so the same stall reports
"idle for 601s" on one run and "idle for 604s" on the next. The incident
signature hashed that raw number, so each run minted a new incident: an
acked timeout pinged again and the repeat-alert cooldown never applied.
Mask duration tokens before hashing; status codes and other numbers still
split incidents.

Fixes #129587
2026-10-01 20:14:31 -04:00
FroRaut
4152c69636 fix(whatsapp): bind cloud webhooks to configured identity
(cherry picked from commit a2efc1d62c5be444274ae58fda1aa86d1d9793ee)
2026-10-02 02:58:18 +05:30
kshitijk4poor
ed7909bad9 refactor(webhook): drop dead revision snapshot, lock-free reads, CAS owns the disable rule
The sha256 revision returned by _load_subscriptions_unlocked was never compared by
anyone, so _load_subscriptions_snapshot was dead weight; the loader now returns the
dict and folds its duplicate excepts. _load_subscriptions reads without the store
lock: writers publish by atomic rename, so readers always see one whole snapshot,
and read-only homes (where the lock file cannot be created) keep listing routes.
Nothing re-enters the lock any more, so the thread lock is a plain Lock.

The "keep an explicit enabled: False" rule was copied into both the CLI subscribe
path and the dashboard create path; it now lives in _replace_subscription after the
CAS check (where the current record is authoritative) and the published route is
returned, so the dashboard summary reflects what was actually stored.

The rebind secret selection collapses to one expression (same result for all four
profile-changed x --secret combinations), and the test-only _save_subscriptions is
removed; tests seed through _mutate_subscriptions.

The gateway's dynamic-route reload now records the stat key for a malformed or
non-dict file too, so a bad version is parsed once rather than on every POST while
the last good routes stay live. Docs describe the reload as stat-gated and lock-free.
2026-10-02 02:54:18 +05:30
FroRaut
3082d00c82 fix(webhook): transact subscription mutations
(cherry picked from commit 8c4ee86733a1193687f379e158e870ee4bcf56b5)
2026-10-02 02:54:18 +05:30
teknium1
a743ca54c3 Revert "docs: approvals.timeout and agent.clarify_timeout are the messaging-platform deadlines"
This reverts commit d7fdebf45a.
2026-10-01 13:23:12 -07:00
teknium1
423eef696e Revert "chore: review follow-ups — stale timeout comments, softer docs claim, group-turn cap credits only user-blocked intervals"
This reverts commit bdf4a5d632.
2026-10-01 13:23:12 -07:00
John Paul Soliva
00e781e193 docs(worktrees): hermes -w checks out without the repository's hooks, fsmonitor and filters
Note the Git LFS consequence (pointer files) and the fix (git lfs pull in the worktree), EN and zh-Hans.

(cherry picked from commit 9870e29a42a77865eac79d01d3b99d516e5ebf0f)
2026-10-02 01:24:41 +05:30
teknium1
aa859fd4c8 feat(plugin-catalog): refuse plugins that override Hermes core or Desktop at runtime
Catalog policy (Teknium): a listed plugin extends Hermes only through public
surfaces. A plugin that replaces core functions, methods or Desktop UI in place
collides with every other plugin patching the same seam and breaks on any core
release.

- New `no core override` admission check (hermes_cli/plugin_validate_core_override.py):
  static taint pass over the import closure of __init__.py and dashboard/*.py.
  Core values are Hermes modules (plus hermes_plugins.*) and attribute chains off
  them, reached by import, importlib/sys.modules lookups, getattr, iteration, or a
  plugin helper that returns one. Rebinds are attribute assignment/deletion,
  setattr/delattr, sys.modules writes, writes into a core module's tables,
  mock.patch, and passing a core value to a helper that setattr's its parameter.
  Tests, benchmarks and CI/smoke scripts outside the import closure are not judged.
- Desktop lint: new `app DOM reach` rule (document-level queries of the app's own
  data-slot/data-tour/data-sidebar/data-testid markup, and document.body
  childList/subtree MutationObservers).
- Desktop lint bug: the comment stripper did not skip string literals, so a string
  holding `/*` (a glob like '**/*.md') opened a fake comment and hid every line up
  to the next `*/` from all rules. Strings are now matched first and kept.
- README rule 9, the plugin-catalog user guide and the plugin developer guide's
  compatibility contract state the rule.

A/B over 466 pinned trees (121 open catalog PR entries + 349 listed entries +
humalike): the new checks flag exactly the hand-verified overrides; zero crashes;
no previously reported Desktop finding disappears.
2026-10-01 12:41:57 -07:00
kshitijk4poor
5a8dea85fb docs(webhook): header-less deliveries get a random per-request id, not a timestamp 2026-10-02 01:07:31 +05:30
kshitijk4poor
db827a1b2b fix(docs): state the real stream-loop bound for newline-free loops
The provider docs said a streaming repetition loop is cut within about
100,000 characters of starting. That holds for line-structured loops, but
a newline-free loop needs more of the 64K tail before the detector fires,
and checks land one tail window apart, so the worst case is about 127K.
Both the English and zh-Hans copies now say about 130,000 characters. The
check stride is unchanged.
2026-10-02 00:53:19 +05:30
kshitijk4poor
48e3894d0e fix(agent): check a streaming loop every tail window once the reply is long
RunawayStreamWatch doubled the gap between checks, so a loop that began
after N legitimate characters streamed roughly N more before any check
saw it (100K of prose let ~156K of loop through). Each check already
reads only the 64K tail, so after the 16K -> 32K -> 64K start the watch
now checks every tail window; total work stays linear and the cut lands
within about 100K characters of the loop starting. The provider docs
promised a cut at 16,000 characters, which was never true for a loop
behind a long prefix; they now state the real bound.

The loop-cut test gains a row streaming ~100K distinct characters before
the loop and stalling once 100K loop characters have been sent, so a
watch that waits for the reply to double hangs the turn and fails.
2026-10-02 00:53:19 +05:30
John Paul Soliva
b6aae42c3b fix(agent): cut a runaway repetition loop while it streams, callback or not
A looping model kept a turn streaming for as long as an uncapped endpoint
sent tokens: every repetition check ran after the stream ended, the stale
detector only fires on silence, and no max_tokens reaches these endpoints
by design. The stop path's own criterion (is_runaway_repetition at >= 16k
chars) now runs on the raw content and reasoning channels of the
chat-completions and Anthropic Messages streams, at 16k and every doubling
on a bounded tail. A hit closes the stream on its owner thread and ends the
turn through the repetition stop, without continuation.

Test loops are the reported incidents from #125650, #94224/#94348,
#125351 and #78551.

Fixes #127234

Co-authored-by: auroracapital <info@auroracapital.nl>
Co-authored-by: BrunoBza <189763786+BrunoBza@users.noreply.github.com>
Co-authored-by: gkd2323c <gkd2323c@users.noreply.github.com>
Co-authored-by: Enzo Adami <enzo.eliott.adami@gmail.com>
(cherry picked from commit 5379a417c560e1f669fa2d27e61837b2004e125e)
2026-10-02 00:53:19 +05:30
teknium1
9c82eab919 fix(agent): one rejected reasoning blob no longer ends replay for the session
An invalid_encrypted_content 400 (or the Codex 401 token_expired twin)
used to strip every cached codex_reasoning_items blob AND turn
replay off for the rest of the agent's life, so a single rotated
sealing key cost all cross-turn reasoning continuity in a long CLI
session or a cached gateway agent.

The first rejection now only strips the stale blobs (persisted messages
and the request copy the retry is rebuilt from); blobs the route mints
afterwards are sealed with its current key and keep replaying. A second
rejection means the route cannot round-trip its own fresh blobs, so
replay is disabled for the session as before: a hostile route costs one
extra 400 compared to main.

The request-copy strip is required: main's global kill switch hid that
the retry was rebuilt from api_messages, which still carried the blob.
2026-10-01 12:08:13 -07:00
kshitijk4poor
9ab49e73b4 fix(web): honest guest refusal, and one route walk per search dispatch
Two review findings on the free fast-search route.

A guest with a stored `web.backend: nous` holds a real Nous identity and a
usable token; the route excludes it by tier, not by absence. The refusal said
"no Nous identity or unreachable", which is wrong for exactly the cohort this
route was gated against. Reuse the canonical FREE_TIER_NEEDS_ACCOUNT text for
the anonymous tier and keep the unreachable wording for everyone else.

search() then called _managed_web_search() a third time just to choose that
message, after _managed_gateway() had already resolved the route — a full
autodetect ladder plus a Portal account read and token peek. Let _managed_gateway
accept the answer its caller already computed so a dispatch walks the ladder
once.

Docs: the autodetect row implies the keyless switch governs a registered
identity's web_search. It does not.
2026-10-01 23:55:54 +05:30
kshitijk4poor
cee3000c24 fix(web): exclude the anonymous guest tier from free fast search
The managed Perplexity ``search_type: "fast"`` route is served without funding
checks, but eligibility is still identity-scoped: the anonymous guest tier has no
Portal account behind it, so it must keep the keyless ring rather than claim a
route the gateway may not serve it.

``resolve_free_search_gateway`` resolved on the presence of a Nous token alone,
and the guest tier has a perfectly usable one, so every anonymous install would
have been routed to the gateway. Add ``fast_search_entitled`` — a registered
Portal identity of any tier, no credit or tool-pool requirement, anonymous tier
excluded, failing closed on unknown/error — and gate the free route on it.

Guests are byte-identical to before: the resolver returns None, autodetect falls
through to the keyless ring, and no gateway request is made. A registered
zero-credit account still gets free fast search. Extract and every other managed
vendor keep the paid entitlement gate.
2026-10-01 23:55:54 +05:30
Robin Fernandes
fd24751a9f fix(web): serve free managed fast search to every Nous identity
The Tool Gateway serves Perplexity POST /search with search_type: "fast"
to any Nous identity without funding checks, but Hermes gated the route
locally behind managed_nous_tools_enabled() (paid access or a live tool
pool), and autodetect only selected managed Perplexity after the gated
Firecrawl probe passed. Anonymous and credit-exhausted users fell through
to the keyless ring without trying it.

Resolve the Perplexity search route on identity alone
(resolve_free_search_gateway: usable Nous token, same guest-disabled and
refresh rules), and let autodetect pick it when nothing explicit is
configured. Every other vendor keeps the entitlement gate, and the billed
managed-Firecrawl fallback now requires it too, so a free-search-only
caller whose call fails goes to the keyless rescue ring as before.
Explicit selections and direct keys keep precedence.
2026-10-01 23:55:54 +05:30
kshitijk4poor
615bf73b09 docs(codex): the app-server runtime runs the Hermes-selected model
Document that model.default and /model reach codex on thread/start and every turn/start, and that -900k variants go out as the base slug.
2026-10-01 23:33:52 +05:30
beardthelion
8444e9eb18 fix(ci): confine plugin-catalog admission gate to the pinned clone
The pinned-source job joined a PR-controlled subdir onto the clone root
with no validation, accepted file:// repos and ref names as the "pin",
and followed committed symlinks out of the clone, so the gate could
validate attacker-planted files instead of the declared commit's
contents. Harden the step end to end:

- validate repo (https://, whitespace-free), sha (40-hex, commit object),
  and subdir (plain relative path) in the parser before any use
- run the parser under python3 -I against installed ruamel.yaml so the
  PR checkout cannot shadow its imports, capture parser output, and
  reset shell variables per entry so a dead parser cannot replay the
  previous entry's fields
- confine PLUGIN_DIR and every symlink beneath it to the clone root
- include renamed and typechanged entry files in the changed set
- feed entries over fd 3 and keep plugin-executed commands off stdin so
  validate cannot drain the remaining list
- NUL-delimit both self-updater grep stages and sanitize the echoed
  filename list so crafted names cannot forge workflow commands
- mirror the same repo/subdir rules in the runtime catalog loader and
  the site extractor, and use \Z anchors so a trailing newline cannot
  split the verdict between gates
2026-10-01 10:46:45 -07:00
kshitijk4poor
40017f8a0f docs: drop gpt-6-terra-900k from the -900k variant examples
gpt-6-terra was never published and is not -900k eligible; /model rejects it.
2026-10-01 16:40:58 +05:30
GodsBoy
5bb6127c5b feat(models): add the gpt-6.1-sol-900k Codex OAuth picker variant
#128248 registered gpt-6.1-sol but left it out of the -900k opt-in because
nothing was verified above 272K on Codex. Live probe on the ChatGPT OAuth
route (2026-09-29, streaming /responses, server-reported input_tokens, effort
low): gpt-6.1-sol completed at 684,566, 898,681 and 918,137 input tokens with
all three needles recalled, and was rejected with context_length_exceeded at
about 931K and above, in line with the model page's 922,000 max input. The
live catalog advertises 272K with an 872K ceiling, which still caps the bump.

Eligibility is by exact slug, not the gpt-6-sol family prefix, so the dotted
slug is its own verified line and no -pro variant is synthesized. The variant
is billed as the base model.

(cherry picked from commit 4772e9db5d7b0fe22da80dea4f3c662672ea5302)
2026-10-01 16:40:58 +05:30
kshitijk4poor
1c32b18378 docs(image_gen): document the supports_upscale and creative_controls capability keys
Both keys shape the dynamic image_generate schema but neither the
ImageGenProvider.capabilities() docstring nor the provider-plugin guide
mentioned them.
2026-10-01 16:22:28 +05:30
Mariano Nicolini
bf1bc78e63 feat(image_gen): expose the Krea 2 sliders to the agent only when the active backend declares them
(cherry picked from commit cbbd7f2b7b961fc822b47f4783b29fbc581f06b6)
2026-10-01 16:22:28 +05:30
Mariano Nicolini
87f3fa8881 fix(image_gen): cap local Krea style references by total size so multi-file requests fit the gateway limit
(cherry picked from commit 3e4e2bc4dbcd3143099455a9b604fb36af35f34a)
2026-10-01 16:22:28 +05:30
Mariano Nicolini
f5928fe003 fix(image_gen): embed local Krea style references so files from disk work outside a sandbox
(cherry picked from commit de6fe8e09002386786f6e64755d948a33c17bd89)
2026-10-01 16:22:28 +05:30
Mariano Nicolini
ff6b1c913f docs(tool-gateway): describe the single managed image picker and drop the per-call model flag that never existed
(cherry picked from commit 39c6f025b4c0391a14e8d667de106ba0e7fcc60a)
2026-10-01 16:22:28 +05:30
Brooklyn Nicholson
bdf4a5d632 chore: review follow-ups — stale timeout comments, softer docs claim, group-turn cap credits only user-blocked intervals 2026-10-01 03:50:55 -05:00
Brooklyn Nicholson
d7fdebf45a docs: approvals.timeout and agent.clarify_timeout are the messaging-platform deadlines 2026-10-01 03:50:55 -05:00
Chukuwebuka-2003
b27d9eb7c0 feat(desktop): add review-pane diff scope selector (Uncommitted / Branch / Last turn)
The review pane docs promised three diff scopes, but the renderer hardcoded
'uncommitted', leaving Branch and Last turn unreachable even though the
Electron implementation and i18n strings were complete. Wire a scope picker
into the pane header and thread the chosen scope through the list/diff reads.

- Add $reviewScope (persisted) + $reviewTurnBase (HEAD-per-cwd map) atoms
- Thread scope + last-turn baseRef through refreshReview/selectReviewFile
- Capture HEAD at turn start per repo cwd, driven off $sessionStates so
  pinned tile worktrees and background sessions get their own baseline
- Gate stage/unstage/revert and the commit/PR ship bar to the uncommitted
  scope (branch/last-turn are read-only)
- Clarify the Last turn / read-only semantics in the desktop docs

Fixes #84347
2026-10-01 00:26:52 -05:00
teknium1
7a06cb50d7 feat(telemetry): shared-metrics opt-in offer on every surface
One answer per profile, asked once, with the Desktop strip's three equal
choices (Send to Nous / Local only / No thanks) now offered by:

- `hermes setup`: at the end of every flow (Quick, Full, Blank Slate,
  Portal, --quick), via the setup-completed record they all funnel into.
- `hermes` / `hermes --tui`: once before an interactive chat on a profile
  that never answered. Skipped for -q, piped/JSON output, spawned actions
  (HERMES_NONINTERACTIVE), Desktop-hosted panes and managed installs.
- Web dashboard: a banner for the profile being managed, backed by
  GET/PUT /api/shared-metrics/consent.

"No thanks" is the terminal default so Enter never opts anyone in; Esc and
the banner's X leave the question open.

Also fixes the answer that could not stick: a CLI "no" equals the shipped
defaults, so save_config stripped it and every surface (Desktop strip
included) kept asking. The answer is now written to config.yaml directly,
and `decided` has one definition shared by the RPC, the CLI and the router.
2026-09-30 21:15:08 -07:00
Brooklyn Nicholson
4235742381 fix(desktop): fall back the Skills Hub picker to GitHub Pages when Vercel refuses
Vercel's WAF denies some residential IP ranges for the whole docs domain,
which leaves the Bot Mode Skills Hub picker on a 403 block page with no
alternate origin (#118203). Probe the primary picker URL before each
browse session and fall back to the equivalent GitHub Pages deployment
when it is unreachable, refuses us, or hangs past an 8s deadline; picker
installs are accepted from either trusted origin but still gated on our
own frame's contentWindow. Documented as an FAQ troubleshooting entry.

Re-lands the surviving half of #118206 (its Capabilities picker target
was since deleted by the native catalog).

Fixes #118203

Co-authored-by: KoNit-K <konit.block@protonmail.com>
2026-09-30 23:07:37 -05:00
Brooklyn Nicholson
6489ef982c fix(desktop): route Ctrl+W to the shell over an interactive terminal
Main claims Cmd/Ctrl+W on every window (window-accelerator's
before-input-event + preventDefault, kept for the #105498 swallow
guard and the Win/Linux close path) and routes it to the renderer via
hermes:close-preview-requested; the renderer's close rung maps any
focused [data-terminal] to closeActiveTerminal(), so the chord closed
the pane and killed the live shell instead of reaching xterm as
readline unix-word-rubout (#65457).

Route it the way #108550 routes Cmd/Ctrl+R: keep main's claim exactly
as-is, and add the missing terminal rung in the renderer.

- TerminalInstance (the user PTY) gains [data-interactive-terminal];
  the read-only agent mirror keeps only [data-terminal], so its tab
  stays deliberately closeable (the #65524 breadth complaint).
- The terminal handle registry (terminal-context-menu) keys by the
  [data-terminal] scope rather than the nested xterm host — the same
  fix #108550 carries — and grows a wordErase verb: the user terminal
  re-delivers the ^W byte to its PTY, the agent mirror's is null.
- The close-preview IPC rung (use-desktop-integrations) and the
  view.closeTab keybind dispatch (use-keybinds) both consume the
  bare platform chord through that verb instead of closing; Shift /
  Alt / rebound variants keep closing.
- The non-mac Window>Close menu item becomes click-only
  (registerAccelerator: false): the 'close' role's default
  CommandOrControl+W accelerator would claim the chord before the
  before-input-event run. Clicking it still closes the window.

Co-authored-by: Kyssta <218078013+kyssta-exe@users.noreply.github.com>
Co-authored-by: percival <ignacio@agenticolabs.io>
2026-09-30 23:04:06 -05:00
Brooklyn Nicholson
9c6fe3aa75 docs(desktop): keep the ARM64 window-context note inside the Troubleshooting section
The new ### heading was inserted between '### Troubleshooting' and its bullet
list, orphaning the dashboard sign-in bullets out of their section.
2026-09-30 22:27:11 -05:00
itsflownium
ff19f80f86 fix(desktop): make ARM64 binding guidance version-independent 2026-09-30 22:27:11 -05:00
itsflownium
9b363a33db fix(desktop): explain unavailable Windows ARM64 window context 2026-09-30 22:27:11 -05:00
unsupportedpastels
9c2d40d60a fix(gateway): preserve managed values and bridge authored subdicts
Restore managed extra values after cross-shape merges and expose authored subdict conflicts to plugin hooks before global env fallbacks. Consolidate the precedence coverage into two invariant matrices and qualify documentation for managed policy and explicit env precedence.

Co-authored-by: Victor Kyriazakos <victor@rocketfueldev.com>
2026-09-30 20:10:03 -04:00
Victor Kyriazakos
de63c6d275 fix(gateway): authored platforms.<plat>.extra wins over the top-level platform block
Replace _fill_extra with a _authored_wins overlay applied at BOTH copy
sites: the shared-key bridge and the plugin YAML hook. The hook now runs
on the overlaid block, so its YAML→env bridge seeds the authored nested
value into the env rung that env-first adapter readers resolve first.
Conflicting (platform, key) pairs warn once per load via a per-load set
threaded through both functions; _fill_extra and the module-level dedupe
set are gone. Docs note the precedence in slack.md.

(cherry picked from commit 39d047224ee7a6987d7b20ef92c8e707d29f9c70)
2026-09-30 20:10:03 -04:00
Brooklyn Nicholson
6b2fe92af6 docs(desktop): describe favorite models in the picker 2026-09-30 17:31:21 -05:00
Brooklyn Nicholson
641af6dfc5 feat(desktop): shift-click a model to pin it to the top of the picker
Pin a model the way you pin a chat: shift-click its row, or choose Pin in
its options submenu. It lifts into a Pinned section above the provider
groups, in the order you pinned it, with the provider named on the row
so two labs sharing a model id stay apart. Shift-click neither picks the
model nor closes the menu, so a second shift-click (on the row, now in
the Pinned section) unpins it; a plain click still selects. That section
is the row's only place in the menu, with no duplicate down in its
provider group, and a search is exempt, since a query means "list every
match in its provider's place".

With nothing pinned there is no empty section. The gesture is taught
where the action lives instead: the submenu's Pin item carries the
shift-click hint in the menu's shortcut slot.

The catalog owns the preference like it owns the Edit Models shortlist,
so the composer pill, session tiles and the kanban override all show
the same section. The per-family row moves into a shared ModelFamilyRow
so the Pinned section and the provider groups can never paint a model
differently, and the keyboard row list mirrors the render order (pins
first). A pin whose provider is not connected paints no row and is kept,
not dropped; pins ignore the shortlist because a pin IS an explicit
"always show me this one". The submenu stays pure: the pin row only
paints when the host passes onTogglePin, so the reasoning pill (which
has no catalog behind it) is unchanged.

Co-authored-by: Jahaziel Hernandez <jahaziel.hernandez@baubap.com>
2026-09-30 17:31:21 -05:00
brooklyn!
bda33b601d Merge pull request #128992 from NousResearch/bb/bot-focus
fix(bot-mode): make bot-to-bot messaging, Bot Chat opens, and lease handoff actually work
2026-09-30 14:40:57 -05:00