Commit Graph
3060 Commits
Author SHA1 Message Date
calvinnwq 9c1a75e56f fix(gateway): Discord's config allow_from also governs guild channels
Under multiplex, authorization reads each profile's own secret scope, so the
DISCORD_ALLOWED_USERS the YAML bridge copies from discord.allow_from into os.environ is never
seen. The fallback to the adapter's extra then read group_allow_from for 'group' sources:
Telegram's group-only list, which Discord does not have. An allowlisted user's guild-channel
messages were dropped and their button clicks refused, while threads and DMs worked.

Discord declares allow_from_covers_groups, so its groups read allow_from. Adapters with a
separate group list (Telegram) are unchanged; a test pins that a Telegram DM-only allow_from
still does not admit the user in a group.
2026-10-26 13:04:38 +05:30
calvinnwq 2712a096db fix(discord): a button click in a thread gets the thread's authorization verdict
#133213 confirms a snapshot-admitted clicker with the gateway's live check, but labelled every
guild click 'group' and passed no thread id. Message intake labels the same channel 'thread',
so a user the gateway admits in a thread could be refused on its approval card ("This bot is
private..."), and a thread_id profile route never matched the click. Label the click like
intake does: a discord.Thread is 'thread' with its id as thread_id.
2026-10-26 13:04:38 +05:30
Teknium b6fa7cd951 fix: Hermes child interpreters no longer import packages from the launch directory
python -m / -c put the current directory ahead of PYTHONPATH and site-packages, so a
hermes_cli/, tui_gateway/, agent/ or plugins/ package in the directory Hermes runs in
(a cloned repo, a task workspace) ran in place of Hermes' own. Spawn these children with
-P (safe path, Python >= 3.11) and keep their cwd: TUI slash worker, TUI cli.exec, setup
import-agent, profile uninstall, gateway restart watcher, plugin validate probe, Google
Meet bot, and the codex hermes-tools MCP server config. The validate probe now names the
Hermes tree on PYTHONPATH instead of finding providers through the cwd.
2026-10-25 21:57:29 -07:00
kshitijk4poor ab3e4affd6 fix(config): meet the code-health ratchet and keep pm's runtime tree off the YAML stack
CI on the salvage was red on the code-health ratchet (over-cap facades grew; two blind excepts)
and on the pm observer fixture, whose isolated source tree carries pm/ + hermes_cli/ but no utils.

- config_backend imports become module-level in the facades (it imports only stdlib); gateway,
  auth and telegram take config_exists through the hermes_cli.config import they already have.
- Pure helpers move to topical siblings, offsetting the growth: _csv_or_list_to_set ->
  gateway/run_common.py, the dotted config-path pair -> hermes_cli/backup_restore.py, the
  voice/timezone/web-backend checks -> hermes_cli/config_value_checks.py, _load_config_dict ->
  hermes_cli/profile_memory_config.py (both callers are memory-provider code).
- _load_config_dict catches YAMLError/OSError/UnicodeError instead of Exception.
- _load_config_cache_hit probes readability with config_backend.probe_config_readable (a 1-byte
  read through the backend's open) instead of a full parse plus a blind except.
- pm/plugins_state.read_home_selection checks config_exists before importing the YAML stack, so
  a home with no config never needs utils.
- developer-guide/config-backend.md describes this build (file backend only).

Same approach as @benbarclay's 74421be709 in #122509, which does not cherry-pick onto current main.
2026-10-25 18:54:09 -07:00
Ben Barclay 871900df63 feat(config): route every user config.yaml read and write through a ConfigBackend seam
Add hermes_cli/config_backend.py (config-config design §4.1, D10/D11): a
ConfigBackend protocol, the FileBackend (today's behaviour), selection by
HERMES_CONFIG_BACKEND only (default file; 'remote' and unknown values fail
closed with ConfigBackendUnavailable), and path-level helpers
(read_config_doc, read_config_doc_readonly, config_version, config_exists,
write_config_key, write_config_document, supports_file_tooling).

Migrate every direct reader/stat/exists/bypass writer of a user config.yaml
to the helpers (config.py loaders + caches, config_effective, env_loader,
gateway, TUI, CLI watchers, cron, tools, profiles, logging, time). File
tooling (config edit, backups, xAI migration rewrite, raw dashboard editor)
checks supports_file_tooling() first; migrations skip write-back without it.

Add scripts/check_config_yaml_readers.py (CI lint next to the writers gate)
that rejects a direct read of a config path outside the backend.
2026-10-25 18:54:09 -07:00
Teknium d509ab10d7 fix(providers): lazy provider headers stay a real dict for plugin overrides
Review finding (P2, #144640): LazyHeaders was a read-only Mapping, so a
user provider override doing get_provider_profile('gmi').default_headers.copy()
(or |, update, json.dumps, ==) raised AttributeError and the provider
disappeared, in-process and under plugins.isolation: host.

LazyHeaders is now a dict subclass that stores every key up front and
resolves callable values (the release-carrying User-Agent) on first value
read. Every value-reading/mutating method resolves first; __iter__ is
overridden so dict(x)/{**x}/f(**x) leave CPython's raw-storage fast path;
json.dumps takes items() because the stored keys make it non-empty.
.copy()/|/copy/pickle yield plain resolved dicts. Importing provider plugins
still runs no git. The plugin-host Mapping materialization is gone: encode()
reads a dict subclass through items(), which resolves.
2026-10-25 17:50:59 -07:00
Teknium 68e4c2b31a fix(startup): build version-bearing User-Agents when a request is built, not at import
Every CLI start imports all provider plugins (hermes_cli.config lists their env vars), and
fireworks/gmi/xai/router/kimi/opencode plus auth_constants, models, model_catalog,
auxiliary_client, gemini_native_adapter, web_server_messaging, slack, yuanbao and perplexity
built a `<product>/<release>` string at module level. In a source checkout the release comes
from the 7-call git provenance walk (`git tag --merged HEAD` alone is ~0.35 s in a large
clone), so `hermes sessions list` paid it for headers it never sends.

Provider profiles take a LazyHeaders mapping (resolved on first read by the client builders,
which already copy with dict()); core modules call version_info.hermes_user_agent() where the
request is built. Header values are unchanged.
2026-10-25 17:50:59 -07:00
TekniumandHalldrix 2eec2a7d24 feat(plugins): register_hook(..., hold_stream=True) for transform_llm_output
A plugin that rewrites the reply (redaction, policy filters) can declare that it
must see the reply before the user does. The declaration rides the hook's
registration handle, so it lives exactly as long as the registration and is
scoped to the plugin manager of the profile that loaded it; stream_held()
answers for the active profile. Plain transform registrations keep streaming
(opt-in), and the flag on any other hook is ignored with a warning. The memory-provider hook proxy and the
`plugins validate` probe stub accept the keyword so a declaring plugin loads
through every registration seam.

Shape and seams salvaged from #102481 (mutates= on register_hook, the memory
proxy and validate-stub forwarding).

Co-authored-by: Halldrix <12357213+Halldrix@users.noreply.github.com>
2026-10-25 06:18:48 -07:00
Teknium 808eaf32f9 fix(plugins): hosted providers keep their declared types across the plugin-host wire
Under plugins.isolation: host every value crosses as JSON, so a dataclass
arrived as a generic Record, a Path as str, an enum as its value and a
plugin object as an Opaque placeholder. Core consumers that type-check
broke:

- secret sources: SecretSource.fetch() came back as Record, the
  orchestrator rejected it ("fetch returned Record instead of
  FetchResult") and applied no secrets; fetch() also received home_path
  as str, so `home_path / "x"` raised in the plugin.
- context references: ContextCompletionItem (a __slots__ class) became
  Opaque, so @prefix: autocomplete silently returned nothing.
- context engines: clone_for_agent() returned Opaque, so no agent could
  get its own copy of a hosted engine.

The provider ABC's annotations are the contract, so the wire now
restores against them: the parent rebuilds results from the declared
return type (dataclasses, enums, Paths, typed containers; a declared
provider ABC becomes an object proxy) and the host rebuilds method
arguments from the declared parameter types. Type names never come from
the host. ContextCompletionItem becomes a slots dataclass so it crosses
as data.
2026-10-25 05:10:15 -07:00
Maciej Lisiewski 05b83f632f Merge pull request #143719 from NousResearch/bulk-update-js
chore(deps): bump npm dependencies to latest minor/patch releases
2026-10-25 12:54:29 +01:00
emozilla 53e551c58f fix(webapp): a running gateway's Webapp signs in with Web Access's own Portal client
The previous change kept the Webapp's Portal client in the Nous sign-in plugin's module. Plugin
discovery imports that bundled plugin under a module name of its own
(hermes_plugins.dashboard_auth__nous), so the gateway's import of plugins.dashboard_auth.nous was
a copy discovery never ran. In a real gateway the Webapp kept signing in with
HERMES_DASHBOARD_OAUTH_CLIENT_ID, and on a home with only Web Access's client the Webapp found no
sign-in provider and fell back to the private listener. A live run of the gateway's own start
showed both; the earlier tests imported the plugin directly and passed.

The listener's client and the plugin's provider swap now live in
hermes_cli.dashboard_auth.listener_client, one module that the plugin, however it was imported,
and the gateway share. The plugin offers its swap when discovery runs it, so a plugin that
plugins.disabled keeps from loading still gives the Webapp no Nous sign-in. The tests load the
plugin the way discovery does.
2026-10-25 03:22:38 -07:00
emozilla d740c0f757 fix(webapp): Web Access signs the Webapp in with a Nous Portal client of its own
Web Access registered the Webapp's sign-in callback on the standalone dashboard's Portal client
(HERMES_DASHBOARD_OAUTH_CLIENT_ID), saved that id in .env, and `hermes dashboard register` called
back into Web Access so it would register again. If the Portal replaces a client's redirect URI on
update, turning on Web Access broke Nous sign-in on an existing `hermes dashboard` deployment.

Start now registers a client of Web Access's own (named webapp_<adjective>_<noun>) and keeps its
id, its Portal and the callback in webapp/sign-in-registration.json under the gateway home, user
only. It never sends the dashboard's client id and never writes .env. A second Start with the same
callback asks the Portal nothing; a moved callback updates Web Access's client only. While the
gateway serves the Webapp, the Nous sign-in provider uses that client, and a plugin reload keeps
it. The gateway's private listener and the standalone dashboard keep reading
HERMES_DASHBOARD_OAUTH_CLIENT_ID as on main, and a Webapp whose Nous sign-in was set up by hand
keeps the configured client. A home whose record came from an earlier build of Web Access names
the dashboard's client; its next Start registers a client of its own and leaves the dashboard's
alone. `hermes dashboard register` no longer touches Web Access's record.

The desktop guide said Start saves the client id in .env; it now says where the id lives.
2026-10-25 03:22:38 -07:00
Teknium d178550247 fix(pm): retries never displace an enabled plugin and cost nothing while unchanged
Independent review findings on update-disabled plugins:
- Recorded plugins are tried only after the enabled plugins have built, one
  at a time on top of them; an update that has to disable an enabled plugin
  re-enables nothing, so a download failure can never swap two plugins.
- A resolver or build verdict records a fingerprint of what was tried (core's
  dependency inputs plus the plugin's) and is not built again until one side
  changes; a download failure is retried by the next update, and those go
  first because they fitted beside the rest before.
- The update publishes against the config snapshot it decided from, and a
  rejoin needs the record to still be there, so a user's mid-update choice
  stands.
- Recorded reasons keep their readable start, not the tail of a uv error.
- An installed provider that fails to import is reported as such instead of
  running recovery, and the startup notices dedupe per kind.
2026-10-25 03:01:26 -07:00
Teknium 8c7cbd37d4 fix: review findings on update-disabled plugins
- A provider-only record is retried only while memory.provider still names
  it, so a rejoin never installs a provider the user switched away from.
- An installed provider that still fails to load after startup recovery is
  reported even when recovery printed an install notice.
- One spelling reader: plugin_spellings uses the shared native manifest
  reader (plugin.yaml or plugin.yml), and the memory loader uses it too.
2026-10-25 03:01:26 -07:00
Teknium ffe2f6411c fix(pm): an update that disables a plugin keeps memory.provider and retries it
When an update had to disable a plugin it also cleared memory.provider, so
the memory provider stayed lost even after the plugin was re-enabled, and
nothing ever retried the plugin: one transient failure (a TLS proxy, a mirror
outage, a since-fixed resolver bug) left it off for good.

- The dependency union now honours plugins.disabled for memory.provider the
  same way the provider loader already does, so eviction no longer has to
  blank the selection. Re-enabling the plugin brings the provider back.
- The eviction is recorded in the home's config under
  plugins.disabled_by_update (was it enabled, and why). Every later update
  retries recorded plugins after the enabled ones and undoes exactly the
  edit once a plugin fits again.
- A user's own enable/disable/remove takes the decision back: PM's selection
  publication forgets the record for the plugin the user settled, and
  `hermes plugins disable` on an update-disabled plugin now writes that.
- Spellings come from the plugin's own manifest instead of a plugin scan of
  the PM process's home, so a profile's plugins are matched in that profile.
- A parked provider is reported, not re-installed: agent init tells the
  user once why external memory is off (naming the update's reason), and
  `hermes memory status` and `hermes doctor` show it instead of "plugin not
  found" or "All checks passed". Doctor lists every update-disabled plugin.
2026-10-25 03:01:26 -07:00
Teknium 43cc33ec6f fix: address review — Feishu plain .ogg stays a file, Ogg duration walks pages
- Feishu routed .ogg/.opus to an "audio" (voice) message by extension even
  for send_document / is_voice=False; Opus is now an audio message only when
  the caller requested one.
- Discord _ogg_opus_duration walks page headers by segment table instead of
  rfind("OggS"), which could match packet data and misread the granule.
- Tests: successful transcode path (bytes, duration, temp removed), OggS run
  inside packet data, Feishu plain .ogg/.opus and the voice-intent control.
2026-10-23 17:40:07 -07:00
Teknium 183cd6cc35 fix(gateway): keep plain audio MEDIA out of WhatsApp/BlueBubbles voice notes
Same class as the Discord and Feishu fixes: these adapters' send_voice
ignored is_voice, so a plain audio MEDIA file (music, a sound effect)
became a voice note. The WhatsApp bridge always sends "audio" as ptt
(transcoded to Opus), WhatsApp Cloud converted every local mp3 into an
Opus voice bubble, and BlueBubbles flagged every file isAudioMessage.

is_voice=False now goes as a named document (WhatsApp bridge), as the
original audio/mpeg (WhatsApp Cloud), or as a plain attachment
(BlueBubbles). Voice intent and the no-flag auto-TTS path are unchanged.
2026-10-23 17:40:07 -07:00
Teknium e2fe88cf7a fix(discord): send plain audio as an attachment, voice messages only as Ogg/Opus
The media dispatch routes every audio MEDIA file to send_voice with is_voice,
but the Discord adapter swallowed the flag and uploaded every file as a native
voice message named voice-message.ogg (audio/ogg), whatever the bytes were.
A sound-effect mp3 or a wav arrived as a voice note that does not play, with a
duration guessed from the byte size (mutagen is not a dependency).

is_voice=False now goes out as a regular attachment under its real name.
Voice intent ([[audio_as_voice]], auto-TTS) sends real Ogg/Opus: non-Opus
input is transcoded with ffmpeg, without ffmpeg the original goes out as an
attachment, and duration_secs is read from the Ogg granule position.
2026-10-23 17:40:07 -07:00
Teknium 95505c60e9 docs(tools): lead every tool description with its key capability
The deferred tool_search catalog shows each tool's first sentence clipped to
60 chars, so lines like 'image_generate: Generate high-quality images from
text prompts, or edit /…' hid that it edits with reference images on the
user's configured model. An agent skipped it and called FAL directly with a
hardcoded model.

- catalog line cap 60 -> 80 (CATALOG_SUMMARY_MAX_CHARS); every built-in
  tool's first sentence now fits and leads with what it does
- image_generate/video_generate say they use the configured model;
  image_generate says to use it rather than direct API calls/scripts
- web_search/web_extract/browser_exec name when to use each other
- drop a duplicated cronjob sentence and filler in skill_view/video_generate
- regenerate tests/fixtures/prompt_surface
2026-10-23 16:04:50 -07:00
Maciej Lisiewski 48217bc858 chore(deps): bump spectrum-ts to 12.10.1 (plugins/platforms/photon/sidecar)
spectrum-ts: 12.7.0 -> 12.10.1 (pins edited: 1; also: {'@opentelemetry/resources': "['2.10.0']->['2.12.0']", '@opentelemetry/sdk-trace': "['2.10.0']->['2.12.0']", '@opentelemetry/sdk-trace-base': "['2.10.0']->['2.12.0']", '@spectrum-ts/core': "['12.7.0']->['12.10.1']", '@spectrum-ts/imessage': "['12.7.0']->['12.10.1']", '@spectrum-ts/slack': "['12.7.0']->['12.10.1']", '@spectrum-ts/telegram': "['12.7.0']->['12.10.1']", '@spectrum-ts/terminal': "['12.7.0']->['12.10.1']", '@spectrum-ts/whatsapp-business': "['12.7.0']->['12.10.1']", 'es-module-lexer': "['2.3.2']->['3.0.3']", 'import-in-the-middle': "['3.3.3']->['3.5.2']"})
2026-10-23 22:00:12 +02:00
Maciej Lisiewski 9b53c4d2fb chore(deps): bump protobufjs to 8.8.0 (plugins/platforms/photon/sidecar)
protobufjs: 8.7.1 -> 8.8.0 (pins edited: 1; also: {})
2026-10-23 22:00:12 +02:00
Maciej Lisiewski 069f15795e chore(deps): bump @opentelemetry/otlp-transformer to 0.223.0 (plugins/platforms/photon/sidecar)
@opentelemetry/otlp-transformer: 0.218.0 -> 0.223.0 (pins edited: 1; also: {'@opentelemetry/resources': "['2.7.1']->['2.12.0']"})
2026-10-23 22:00:12 +02:00
Maciej Lisiewski c88d3a492c chore(deps): bump @opentelemetry/otlp-exporter-base to 0.223.0 (plugins/platforms/photon/sidecar)
@opentelemetry/otlp-exporter-base: 0.218.0 -> 0.223.0 (pins edited: 1; also: {})
2026-10-23 22:00:12 +02:00
Maciej Lisiewski 85938b8b63 chore(deps): bump @opentelemetry/exporter-trace-otlp-http to 0.223.0 (plugins/platforms/photon/sidecar)
@opentelemetry/exporter-trace-otlp-http: 0.218.0 -> 0.223.0 (pins edited: 1; also: {'@opentelemetry/resources': "['2.7.1']->['2.12.0']"})
2026-10-23 22:00:12 +02:00
Maciej Lisiewski bf02f6133d chore(deps): bump @opentelemetry/exporter-logs-otlp-http to 0.223.0 (plugins/platforms/photon/sidecar)
@opentelemetry/exporter-logs-otlp-http: 0.218.0 -> 0.223.0 (pins edited: 1; also: {})
2026-10-23 22:00:12 +02:00
Maciej Lisiewski a42905d0e7 chore(deps): bump @opentelemetry/core to 2.12.0 (plugins/platforms/photon/sidecar)
@opentelemetry/core: 2.10.0 -> 2.12.0 (pins edited: 1; also: {})
2026-10-23 22:00:12 +02:00
Maciej Lisiewski d0b2d6bb71 chore(deps): bump indirect npm dependencies within existing ranges (photon)
- @bufbuild/protobuf: 2.14.0 -> 2.16.0
- @opentelemetry/context-async-hooks: 2.10.0 -> 2.12.0
- @opentelemetry/sdk-metrics: 2.10.0 -> 2.12.0
- @photon-ai/advanced-imessage: 2.1.0 -> 2.2.0
- @photon-ai/otel: 3.7.0 -> 3.8.1
- cjs-module-lexer: 2.2.1 -> 2.3.0
- lru-cache: 11.5.2 -> 11.5.3
- marked: 18.0.11 -> 18.1.0
- zod: 4.5.4 -> 4.6.5
2026-10-23 22:00:12 +02:00
Maciej Lisiewski 8edc5ea255 Merge pull request #143456 from NousResearch/bulk-update
chore(deps): bump Python dependencies to latest minor/patch releases
2026-10-23 12:25:56 +02:00
Teknium a9dfc2500d docs(browser): drop references to deleted browser tools
Model-facing text, startup tips (all 17 locales; the tips probe needs contiguous keys, so the
five stale tips are rewritten to live browser.* features), .env.example, developer-guide file
maps and the provider parity script still named browser_navigate/vision/console, record_sessions,
agent-browser/AGENT_BROWSER_* and the deleted browser_tool* modules.
2026-10-23 01:42:14 -07:00
Teknium 68500d9ff9 refactor(browser)!: delete the built-in browser_* tools, agent-browser and bundled Camofox
browser_exec is the only core browser tool (plus browser_cdp, browser_dialog,
browser_vault, all CDP). Deleted: browser_navigate/snapshot/click/type/
scroll/back/press/get_images/vision/console and every module that served
them (browser_tool_session/_lifecycle/_cloud/_cdp/_real_profile/
_lightpanda_fallback/_eval_policy/_origin/_snapshot/_vision) plus
browser_camofox*.py (Camofox ships as the catalog plugin 'camofox').
tools/browser_tool.py and tools/browser_tool_install.py shrink to the
frozen old-updater stub warm_agent_browser_npx_cache.

toolsets: _HERMES_CORE_TOOLS / browser toolset list only the CDP tools.
model_tools: browser_tools legacy alias and the browser_navigate /
browser_type schema rewriters removed. Cloud providers no longer request an
install post_setup hook (nothing to install).
2026-10-23 01:41:24 -07:00
Maciej Lisiewski 154748800b chore(deps): bump google-auth-oauthlib to 1.5.0
google-auth-oauthlib: 1.3.1 -> 1.5.0 (pins edited: 1; also: {})
2026-10-23 09:23:34 +02:00
Maciej Lisiewski 77da0e47a1 chore(deps): bump google-auth-httplib2 to 0.4.4
google-auth-httplib2: 0.3.1 -> 0.4.4 (pins edited: 1; also: {})
2026-10-23 09:23:33 +02:00
Maciej Lisiewski 9200e9202b chore(deps): bump google-auth to 2.62.0
google-auth: 2.55.1 -> 2.62.0 (pins edited: 2; also: {})
2026-10-23 09:23:32 +02:00
Maciej Lisiewski 5bc07faa3c chore(deps): bump google-api-python-client to 2.201.0
google-api-python-client: 2.194.0 -> 2.201.0 (pins edited: 1; also: {})
2026-10-23 09:23:31 +02:00
Teknium 7ac2c6dc29 Merge pull request #142308 from rob-maron/window-plans
feat: usage-window plan support
2026-10-22 14:55:39 -07:00
Teknium 5c353467dc refactor(delegation): the default delegation strategy is a bundled plugin
Move today's delegation routing (every child follows the delegation section,
else the parent's route) out of core into plugins/delegation/default, a bundled
kind: backend plugin that registers through ctx.register_delegation_provider
like any other strategy. Core keeps only the name 'default' as the fallback
selection; no built-in provider class and no reserved name remain.

The registry loads a bundled strategy on first use when the plugin sweep never
registered it (runtimes that pinned discovery with zero plugins), so
delegate_task never depends on the sweep; plugins.disabled still wins and a
disabled default is a clear refusal. Config names are validated before they
become a path.
2026-10-22 14:48:49 -07:00
Teknium c00f999ba4 Merge origin/main into window-plans
Resolves the desktop i18n conflicts: main moved en/ru errorCodes into
en_error_codes.ts / ru_error_codes.ts, so usage_window moves there; ar/ja/zh/
zh-hant keep both usage_window and main's workspace_unavailable. _keys.desktop.json
regenerated.
2026-10-22 12:53:41 -07:00
Teknium d99af4187b refactor: move the Singularity terminal backend out of core into the singularity catalog plugin
The Singularity/Apptainer backend (tools/environments/singularity.py) now ships
as the official 'singularity' plugin in NousResearch/hermes-official-plugins,
registered through the terminal-environment provider seam under the same
backend name and config keys (terminal.backend: singularity,
terminal.singularity_image / TERMINAL_SINGULARITY_IMAGE, terminal.container_*).

- 'singularity' leaves BUILTIN_BACKEND_NAMES so the plugin can register it;
  every hardcoded classification set drops it (the provider declares
  is_remote/is_container/supports_streams instead).
- LEFT_CORE gains a singularity row: homes whose terminal.backend (or .env
  TERMINAL_ENV) is singularity get the catalog plugin once, on hermes update
  or agent/gateway start. An unknown-backend error for 'singularity' names
  the install command (new row field terminal_backend).
- A plugin backend receives its per-task '<backend>_image' override; batch
  runs register one for the active backend.
- BaseEnvironment's sudo-probe knob is renamed _passwordless_sudo_probe_supported:
  plugin backends set it, and the old spelling trips the install scanner.
- Core's scratch-dir disk warning and process-exit sweep were Singularity-only
  and move to the plugin; the singularity setup row, dashboard probe/row and
  config defaults/bridges go too. Docs point at the plugin.
2026-10-22 05:26:10 -07:00
rob-maron 6adca16a77 Merge branch 'NousResearch:main' into window-plans 2026-10-21 23:07:58 -04:00
Teknium e3a962d087 refactor(mcp): Hermes as an MCP server leaves core for the mcp-serve plugin
`hermes mcp serve` (mcp_serve.py) moves to the official mcp-serve plugin in
hermes-official-plugins, which registers `hermes mcp-serve` and also carries
the `--tools` mode from #83364.

- delete mcp_serve.py and tests/test_mcp_serve.py (the suite moved with it)
- drop the serve subparser, its dispatcher branch, the mcp agent-subcommand
  row and the inline MCP discovery path only `mcp serve` reached
- LEFT_CORE row (command + old_command): `hermes mcp serve` and a bare
  `hermes mcp-serve` without the plugin print the new spelling and the
  install line on stderr. MCP clients live outside Hermes, so no home shows
  the feature in use and nothing installs automatically.
- docs, locale tips and the bundled cli-reference point at the plugin

agent/transports/hermes_tools_mcp_server.py stays: the codex_app_server
runtime launches it as its hermes-tools MCP server.
2026-10-21 17:06:03 -07:00
Siddharth Balyan 22fc809869 feat(data-use): route connector results that need no-training to qualifying Nous endpoints
The tool gateway now tags execute results with dataUse.requires (no_training,
zero_retention). Hermes keeps that as `data_use` metadata on the tool message
(content unchanged; a batch carries the union), persists it inside the existing
display_metadata column (no migration; /branch, resume and compaction copies
keep it), and strips it from every provider request. The Nous profile reads the
union over the outgoing messages and sends provider.data_collection=deny (+ zdr
for zero_retention) on both the chat-completions and Anthropic Messages paths;
no other provider sees it. A 403 data_policy_denied classifies as its own
non-retryable reason with no fallback and copy that tells the user to switch
model, compress, or start a new session.
2026-10-21 15:46:19 +05:30
rob-maron 83c5737be2 merge main in 2026-10-20 21:19:31 -04:00
rob-maron 15307dece7 Usage-window plan support 2026-10-20 21:04:30 -04:00
Teknium 2b9846ffe8 fix(krea): the schema describes the stored model on direct Krea too
Direct dispatch passes image_gen.model to generate() explicitly, like the
managed path, so a KREA_IMAGE_MODEL override naming Krea 2 made the schema
hide Krea 3's thinking/resolution controls and describe edit inputs as style
references while Krea 3 ran.
2026-10-20 12:30:50 -07:00
Teknium 1107163931 fix(krea): Krea 3 price shows reference images by resolution
Krea charges +$0.02 for a 1K reference image and +$0.07 for a 2K one.
2026-10-20 12:30:50 -07:00
Mariano Nicolini d1400be1f3 fix(krea): review follow-ups: edits keep their framing, schema follows the stored model, Krea 3 wording 2026-10-20 12:30:50 -07:00
Mariano Nicolini 693777d4fa feat(krea): Krea 3 edits and generates through the managed gateway, with thinking and resolution controls 2026-10-20 12:30:50 -07:00
Teknium d2b3446b1e fix(a2a): one IPv6 server class; dual-stack peers keep their IPv4 identity
Folds the two IPv6 server classes into one: every IPv6 host gets
AF_INET6, and V6ONLY=0 makes the :: wildcard answer IPv4 too.

A dual-stack socket reports an IPv4 peer as ::ffff:a.b.c.d, so the
shared-token / no-token identity became ip:::ffff:127.0.0.1 and an
A2A_TRUSTED_PEERS entry written as ip:127.0.0.1 no longer matched
(403 "peer not trusted"). _client_ip unmaps it. The test drives a
token-gated :: bind over both stacks plus an IPv4 tasks/list call
behind an IPv4 allow-list.
2026-10-20 04:05:08 -07:00
John Paul Soliva 1f0981bd8e fix(a2a): accept the bracketed [::1] spelling of an IPv6 A2A_HOST
A2A_HOST=[::1] was not recognised as loopback (no-token runs fell back
to 127.0.0.1 with a misleading warning), failed to bind with a token, and
advertised http://[[::1]]:port/. Strip URL brackets when capturing the
host, and pin the Host-less advertised URL in the IPv6 test.
2026-10-20 04:05:08 -07:00
John Paul Soliva 9f908ca3dd fix(a2a): bind and advertise IPv6 hosts such as ::1
A2A_HOST=::1 is an accepted loopback bind, but ThreadingHTTPServer is
AF_INET only, so the listener failed with bind_failed. An IPv6 host now
uses the AF_INET6 server, and the Agent Card URL brackets the literal.
2026-10-20 04:05:08 -07:00