Skip to content

Nous Portal subscription integration - #25

Closed
rewbs wants to merge 1 commit into
mainfrom
rewbs/nous-portal-integration
Closed

Nous Portal subscription integration#25
rewbs wants to merge 1 commit into
mainfrom
rewbs/nous-portal-integration

Conversation

@rewbs

@rewbs rewbs commented Feb 19, 2026

Copy link
Copy Markdown
Contributor
  • Integration with Nous Portal via subscription, using device auth flow, standard refresh/access token flow, and short-lived API key rotation
  • Optional request dumping for debugging

The flow is:

  • User runs hermes login
  • Hermes calls Portal POST /api/oauth/device/code with client_id=hermes-cli and scope inference:mint_agent_key.
  • Hermes opens the browser to verification_uri_complete and polls POST /api/oauth/token (device_code grant).
  • After user approval on Portal, Hermes receives access_token + refresh_token (+ optional inference_base_url) and stores them in auth state.
  • Hermes immediately resolves runtime credentials:
    • refreshes access token if near expiry,
    • mints or reuses a short-lived agent inference key via POST /api/oauth/agent-key with min_ttl_seconds.
  • Hermes caches the minted key + expiry and reuses it across concurrent processes when still valid.
  • During chat/inference, Hermes auto-refreshes token and rotates agent key when needed (or preemptively when below TTL threshold), then calls the Nous OpenAI-compatible inference API with that key.

Note there are three different credentials with different jobs:

  • access_token: short-lived bearer token the CLI uses to call Portal OAuth-protected endpoints, such as the one to mind API keys.
  • refresh_token: longer-lived token used only to get a new access token, so users don’t have to re-login constantly.
  • short-lived/rotating inference api_key: minted by Portal for the inference API (since that backend only understands API keys, not user/session identity).

The access_token/refresh_token pairing is standard OAuth: keep the token used on normal requests short-lived, and use a separate long-lived credential for renewal. That limits blast radius if an access token leaks and allows safer rotation/revocation policies around refresh tokens.

…ow, standard refresh/access token flow, and short-lived API key rotation

- Optional request dumping for debugging
@teknium1

Copy link
Copy Markdown
Contributor

Ported in the fundamentals, changed the way it was executed (needed multi-provider structure) - thanks!

@teknium1 teknium1 closed this Feb 21, 2026
h4x3rotab pushed a commit to Clawdi-AI/hermes-agent that referenced this pull request Apr 10, 2026
…ousResearch#25)

Replaces hardcoded authenticated=true with real auth check via /api/auth-check endpoint.

Co-authored-by: clawjasper56
h4x3rotab pushed a commit to Clawdi-AI/hermes-agent that referenced this pull request Apr 10, 2026
The auth-check endpoint had its own isBackendReachable() function that
hardcoded http://127.0.0.1:8642 and never tried the fallback port 8643.
This bypassed the multi-port auto-detection logic in gateway-capabilities.ts,
causing the startup screen to loop on 'Connecting...' when the gateway
was running on an alternate port.

Replace the standalone reachability check with ensureGatewayProbed() which
already handles port auto-detection (8642 → 8643) and caches the result.
@tradewind425

Copy link
Copy Markdown

大枠は正しい方向。但し、いくつか実装グリップを詰める必要あり:

gateway/run.py (10.3K行) は Discord 特異的な部分が ~36箇所で全体の ~1% 程度。WhatsApp等マルチプラットフォーム対応もあり、単純な移管ではなく Discord イベントハンドル + routing を抽出→Ouroboros、残りは hermes-agent に hooks として戻す設計が必要。現在 my-changes では tui_gateway/server.py が既にこのパターンを示唆している。

hermes_cli/personas.py (SOUL.md sync 方式) と model_switch.py (can_persist flag 追加) の hook パターンは良い候補。但し hook 呼び出し側と agent 側の API コントラクト(例:JSON-RPC、subprocess IPC)を明確に定義すべき。Discord.js 化するなら必須。

hermes_cli/models.py 破棄 ✓、tools/file_operations.py defer ✓ で合意。

• 本計画と分離として、agent↔Ouroboros の IPC/hook boundary spec を先行タスク化すると、以降の移管作業がスムーズになりそう。

renerocksai added a commit to renerocksai/hermes-agent that referenced this pull request Apr 28, 2026
…agents (phase 10)

The SDK landed PRs NousResearch#24/NousResearch#25/NousResearch#26 in synadia-ai/synadia-agents:
- verb-first subjects (`agents.prompt.{a}.{o}.{s}`, `agents.hb.{a}.{o}.{s}`,
  new `agents.status.{a}.{o}.{s}`) and `metadata.protocol_version="0.3"`
- pinned `_INBOX.agents` reply-inbox prefix (caller-side; no-op for us)
- `name`+`session` collapsed into a single `session_name` (the 5th subject
  token) — `Envelope.session` and the `session=` kwarg on `AgentService` /
  `Agent.prompt` are gone. One service = one session_name.

Package + import root rename: `natsagent` → `synadia-ai-agents`,
`synadia_ai.agents`. Service-side class `Agent` → `AgentService`.

Adapter changes:
- Adopt single-service-per-session: rely on Hermes profile isolation for
  multi-session deployments instead of building an envelope.session demuxer
  on top of `AgentService`. The `_session_locks` dict collapses to a single
  `_session_lock`.
- The SDK explicitly does not own NATS connections: callers build the
  client. Adapter calls `nats.connect(servers=...)` or
  `nats.connect(**sdk.load_context_options(name))` directly.
- Config: `extra.name` + `extra.session_default` → required
  `extra.session_name`; env var `HERMES_NATS_NAME`/`HERMES_NATS_SESSION` →
  `HERMES_NATS_SESSION_NAME`. No migration shim — branch hadn't merged.
- Lock identity rebuilt as `{agent}:{owner}:{session_name}`.

Tests + docs:
- conftest mock renamed `_ensure_natsagent_mock` → `_ensure_synadia_agents_mock`,
  installs under `sys.modules["synadia_ai.agents"]`, also stubs `nats` so
  the adapter's `nats.connect(...)` resolves under test.
- New `mock_nats` fixture in test_nats_connect.py; concurrent-distinct-
  sessions test removed (v0.2-only concept); positive test added that
  chat_id is sourced from `settings.session_name` regardless of any stray
  envelope field.
- design doc §1-§6/§11/§17 updated for v0.3; progress doc gains a Phase 10
  decision-log entry; user-facing nats.md rewritten with verb-first subject
  examples, status endpoint walkthrough, and `_INBOX.agents.>` permission
  note.

Live-verified end-to-end against `nats-server -p 4223` + `hermes-local`
context + `model: anthropic/claude-haiku-4.5` over OpenRouter: real prompt
streamed a real haiku reply through `agents.prompt.hermes.rene.local`,
multi-turn session continuity intact, `/status` slash command dispatched
through the gateway's command registry. Discovery shows
`protocol_version: 0.3`. Heartbeats fire on `agents.hb.hermes.rene.local`.
Status endpoint replies on `agents.status.hermes.rene.local`.

NATS gateway tests: 190/190 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
renerocksai added a commit to synadia-ai/hermes-agent that referenced this pull request May 6, 2026
…agents (phase 10)

The SDK landed PRs NousResearch#24/NousResearch#25/NousResearch#26 in synadia-ai/synadia-agents:
- verb-first subjects (`agents.prompt.{a}.{o}.{s}`, `agents.hb.{a}.{o}.{s}`,
  new `agents.status.{a}.{o}.{s}`) and `metadata.protocol_version="0.3"`
- pinned `_INBOX.agents` reply-inbox prefix (caller-side; no-op for us)
- `name`+`session` collapsed into a single `session_name` (the 5th subject
  token) — `Envelope.session` and the `session=` kwarg on `AgentService` /
  `Agent.prompt` are gone. One service = one session_name.

Package + import root rename: `natsagent` → `synadia-ai-agents`,
`synadia_ai.agents`. Service-side class `Agent` → `AgentService`.

Adapter changes:
- Adopt single-service-per-session: rely on Hermes profile isolation for
  multi-session deployments instead of building an envelope.session demuxer
  on top of `AgentService`. The `_session_locks` dict collapses to a single
  `_session_lock`.
- The SDK explicitly does not own NATS connections: callers build the
  client. Adapter calls `nats.connect(servers=...)` or
  `nats.connect(**sdk.load_context_options(name))` directly.
- Config: `extra.name` + `extra.session_default` → required
  `extra.session_name`; env var `HERMES_NATS_NAME`/`HERMES_NATS_SESSION` →
  `HERMES_NATS_SESSION_NAME`. No migration shim — branch hadn't merged.
- Lock identity rebuilt as `{agent}:{owner}:{session_name}`.

Tests + docs:
- conftest mock renamed `_ensure_natsagent_mock` → `_ensure_synadia_agents_mock`,
  installs under `sys.modules["synadia_ai.agents"]`, also stubs `nats` so
  the adapter's `nats.connect(...)` resolves under test.
- New `mock_nats` fixture in test_nats_connect.py; concurrent-distinct-
  sessions test removed (v0.2-only concept); positive test added that
  chat_id is sourced from `settings.session_name` regardless of any stray
  envelope field.
- design doc §1-§6/§11/§17 updated for v0.3; progress doc gains a Phase 10
  decision-log entry; user-facing nats.md rewritten with verb-first subject
  examples, status endpoint walkthrough, and `_INBOX.agents.>` permission
  note.

Live-verified end-to-end against `nats-server -p 4223` + `hermes-local`
context + `model: anthropic/claude-haiku-4.5` over OpenRouter: real prompt
streamed a real haiku reply through `agents.prompt.hermes.rene.local`,
multi-turn session continuity intact, `/status` slash command dispatched
through the gateway's command registry. Discovery shows
`protocol_version: 0.3`. Heartbeats fire on `agents.hb.hermes.rene.local`.
Status endpoint replies on `agents.status.hermes.rene.local`.

NATS gateway tests: 190/190 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
renerocksai added a commit to synadia-ai/hermes-agent-work that referenced this pull request May 18, 2026
…agents (phase 10)

The SDK landed PRs NousResearch#24/NousResearch#25/NousResearch#26 in synadia-ai/synadia-agents:
- verb-first subjects (`agents.prompt.{a}.{o}.{s}`, `agents.hb.{a}.{o}.{s}`,
  new `agents.status.{a}.{o}.{s}`) and `metadata.protocol_version="0.3"`
- pinned `_INBOX.agents` reply-inbox prefix (caller-side; no-op for us)
- `name`+`session` collapsed into a single `session_name` (the 5th subject
  token) — `Envelope.session` and the `session=` kwarg on `AgentService` /
  `Agent.prompt` are gone. One service = one session_name.

Package + import root rename: `natsagent` → `synadia-ai-agents`,
`synadia_ai.agents`. Service-side class `Agent` → `AgentService`.

Adapter changes:
- Adopt single-service-per-session: rely on Hermes profile isolation for
  multi-session deployments instead of building an envelope.session demuxer
  on top of `AgentService`. The `_session_locks` dict collapses to a single
  `_session_lock`.
- The SDK explicitly does not own NATS connections: callers build the
  client. Adapter calls `nats.connect(servers=...)` or
  `nats.connect(**sdk.load_context_options(name))` directly.
- Config: `extra.name` + `extra.session_default` → required
  `extra.session_name`; env var `HERMES_NATS_NAME`/`HERMES_NATS_SESSION` →
  `HERMES_NATS_SESSION_NAME`. No migration shim — branch hadn't merged.
- Lock identity rebuilt as `{agent}:{owner}:{session_name}`.

Tests + docs:
- conftest mock renamed `_ensure_natsagent_mock` → `_ensure_synadia_agents_mock`,
  installs under `sys.modules["synadia_ai.agents"]`, also stubs `nats` so
  the adapter's `nats.connect(...)` resolves under test.
- New `mock_nats` fixture in test_nats_connect.py; concurrent-distinct-
  sessions test removed (v0.2-only concept); positive test added that
  chat_id is sourced from `settings.session_name` regardless of any stray
  envelope field.
- design doc §1-§6/§11/§17 updated for v0.3; progress doc gains a Phase 10
  decision-log entry; user-facing nats.md rewritten with verb-first subject
  examples, status endpoint walkthrough, and `_INBOX.agents.>` permission
  note.

Live-verified end-to-end against `nats-server -p 4223` + `hermes-local`
context + `model: anthropic/claude-haiku-4.5` over OpenRouter: real prompt
streamed a real haiku reply through `agents.prompt.hermes.rene.local`,
multi-turn session continuity intact, `/status` slash command dispatched
through the gateway's command registry. Discovery shows
`protocol_version: 0.3`. Heartbeats fire on `agents.hb.hermes.rene.local`.
Status endpoint replies on `agents.status.hermes.rene.local`.

NATS gateway tests: 190/190 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
difeizheng pushed a commit to difeizheng/zdf-hermes-agent that referenced this pull request Jun 3, 2026
Fixes 12 remaining MEDIUM issues from the deep audit (19 total, 7 fixed in Round 12):

design_agent:
- NousResearch#15: add asyncio.wait_for(300s) around LLM API call to prevent infinite hangs
- NousResearch#17: replace 2x hardcoded 'claude-opus-4-8' with shared DEFAULT_MODEL constant

qa_agent / validate_agent:
- NousResearch#20,NousResearch#22,NousResearch#23: already fixed in Round 12 (verified — dynamic timeout/threshold values used)

memory.py:
- NousResearch#24: frontmatter parser uses regex r'^---$' instead of str.split('---',2),
  preventing false splits on content containing '---' (SQL, markdown tables)
- NousResearch#25: parse and preserve 'description' field from frontmatter in metadata,
  fixing write→load roundtrip data loss

profiles.py:
- NousResearch#26: ProfileConfig now frozen=True (immutable dataclass per coding standards)

deploy_agent:
- NousResearch#31: replace 2x sync subprocess.run with asyncio.create_subprocess_exec
- fix 5x .decode() → .decode('utf-8', errors='replace') for Windows CJK safety
- remove unused import subprocess

db.py:
- NousResearch#27: add class docstring explaining RLock + _unlocked pattern
- NousResearch#28: FK constraints already in DDL (verified PRAGMA foreign_keys=ON active)
- NousResearch#29: add _ensure_connection() with PRAGMA integrity_check(1) + auto-reconnect
       on 4 critical methods (create_task, get_task, claim_task, submit_result)
- extract _create_connection() static method for reuse by reconnect

Tests: 79 passed, 0 failed
whichguy added a commit to whichguy/hermes-agent-1 that referenced this pull request Jul 1, 2026
…y, auto-on by design

Adds a `premortem` lens to the families layer (scoped/contrarian/vantage +
premortem). It is the only lens that systematically hunts the `stakes` tail of
EVSI = Σ P·Δplan·stakes — the catastrophic/irreversible failure branch. This is
the generation-side, formula-FROZEN half of the deferred risk-averse tilt: it
only makes the catastrophic-tail question ENTER the candidate set; scoring stays
risk-neutral, so a lurid-but-improbable question still self-prunes on low P.

- pipeline.py: _LENS_DIRECTIVE["premortem"], families_prompt premortem param +
  schema enum, generate_families premortem="auto", conservative _premortem_relevant
  failure-surface gate (writes/deploys/payments/migrations/secrets).
- infogain.py: FAMILIES["premortem"]="auto" (auto-on, like vantage), wire-through,
  --premortem on|off|auto CLI + INFOGAIN_PREMORTEM env. Lens knob stays inside the
  families dict — scalar DEFAULTS cfg untouched (byte-identical safety invariant).
- validate_evsi.py: realized rows now carry lens/family for per-lens attribution.
- Nothing downstream branches on lens (voi.py has zero lens refs); every question
  still scores on its own merit and low-value families self-prune.

Smoke-verified: "migrate prod DB + deploy" yields a premortem family whose
questions survive scoring into the bucket (rollback/lock-window, value 0.75/0.53);
"summarize a paper" correctly suppresses it via the auto-gate. 80 unit tests green
(9 new). Do-no-harm rollback trigger documented: if the off/on eval ladder shows
noise on read-only controls or a diversity drop, set FAMILIES["premortem"]="off".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
whichguy added a commit to whichguy/hermes-agent-1 that referenced this pull request Jul 2, 2026
…ix; families eval plumbing

Two independently designed same-day runs of the pre-mortem eval ladder converge:
auto-on stands, rollback trigger untripped on BOTH criteria.

- Tier-1 (14-cell projected) + tier-2 (realized all-fast): premortem TOP lens by
  realized_regret (0.416; failure-surface 0.602 vs 0.386 ≈1.6×); read-only pm
  questions correctly priced (~0.06) and pruned.
- Independent replication (deepseek judge, bucket source, 34-prompt bank-wide
  two-arm scan): premortem again TOP lens (r_change 0.984, regret 0.765); zero
  read-only bucket entries; adjudicator-diversity trigger explicitly cleared
  (0.65→0.70). Findings §"Independent replication (NousResearch#25)".
- Gate fix: bare artifact nouns (email/message/database) removed from
  _PREMORTEM_HINT (fired on retrieval tasks); word-boundary-prefix hint matching
  with exact-token set (prod/drop/db/repo); pinned in tests.
- Eval-harness families plumbing (the gap that blocked ALL lens evals: cfg from
  DEFAULTS has no 'families' key → harnesses silently ran the flat generator):
  infogain.families_cfg() + --families/--premortem on score_scan/validate_evsi/
  run_evals; lens-tagged scan rows; per_lens() attribution in analyze_evsi;
  pinned in tests/test_eval_families.py. score_scan --include-life no-op fixed.
- Same-dataset side verdicts: NousResearch#23 rel_keep_frac stays OFF (selection_policies —
  every q_value policy ≈ size-matched random within-task); graded change judge
  REJECTED (anchor-clustering; rejudge.py stays as the offline instrument
  harness, --keep-responses stores texts for it). --families-model /
  INFOGAIN_FAMILIES_MODEL override; grouped tests/run.py runner.

Known loose end: reverse-string CI case fails framing_accuracy=0.2 in both arms
(arm-independent, pre-existing).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
whichguy added a commit to whichguy/hermes-agent-1 that referenced this pull request Jul 2, 2026
…esearch#23 stays off, graded judge rejected) + eval plumbing + grouped test suites

- Eval-harness families plumbing: infogain.families_cfg(), --families/--premortem arms on
  score_scan/validate_evsi/run_evals, per_lens() + selection_policies() in analyze_evsi,
  lens-tagged rows; score_scan --include-life pool fix (default now BANK-only).
- NousResearch#25 pre-mortem lens validated at BOTH ladder tiers: tier-1 projected two-arm (14 cells) and
  tier-2 realized two-arm (6 prompts x off/on, 336 rows) — top lens by realized_regret (0.416;
  failure-surface 0.602 vs 0.386 others; forced-on read-only self-prunes). Auto-on confirmed,
  rollback untripped. Gate false positive fixed: artifact nouns removed, word-boundary hint
  matching for both vantage+premortem gates ("repo" != "report", "prod" != "product", old
  '"db "' end-of-text miss fixed).
- NousResearch#23 selection policies: analyze_evsi.selection_policies verdict — every q_value policy within
  ~0.03 of size-matched random within-task; rel_keep_frac stays off.
- Graded change judge (opt-in --graded-change-judge) + --keep-responses + evals/rejudge.py
  offline instrument A/B: REJECTED (anchor-clustering, q_value link 0.60->0.38); original judge
  stays; harness remains for future instrument tests.
- --families-model / INFOGAIN_FAMILIES_MODEL override (families layer no longer hard-pinned to
  glm for evals); SKILL.md drift fixes (stage 1 = plan_model; --value-judge-mode documented).
- Grouped test suites: tests/run.py (basic DEFAULT = mocked/offline ~1s; live opt-in via
  INFOGAIN_TEST_LIVE; all = 107 tests). Docs in evals/README.md.
- roadmap.md reconciled (NousResearch#23 status, NousResearch#24 CLOSED outcome, wrapper DONE); findings + design docs
  updated with all three verdicts. Version 1.0.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Kaladrius2trip added a commit to Kaladrius2trip/hermes-agent that referenced this pull request Jul 18, 2026
Adopt office (work/main 472c876e7, PRs NousResearch#22-NousResearch#25) hardening of the pure ACL
modules back into our canon: acl.py, acl_planner.py, acl_migration.py,
acl_recommender.py, whisper_policy.py. All changes are same-direction
security/correctness hardening, office-authored and office-reviewed:

- unwired capabilities (whisper/scheduler_user) fail closed until dispatch wiring
- glob access-definitions restricted to runtime_safe (no operator/control_plane escalation)
- durable scoped-membership revoke lineage (scoped_membership_legacy_links)
- audit-log trace IDOR fix (session-scoped visibility)
- connection FD-leak fix (_ClosingSQLiteConnection)
- planner: catalog-snapshot binding, in-txn epoch recheck (TOCTOU), session binding, durable denied-attempt audit
- migration: out-of-band approved_hash, locked-snapshot backup, writer-drift detection, reserved-all dedup plus rollback
- /acl global|guild scope, Discord scoped-write enforcement, model to bootstrap-only

Adopted office pure-module tests; kept our integration and admin-baseline tests,
adapted to the new contracts (actor_session_key, actor_can_delegate,
bind_proposal_catalog, runtime_safe recommender guard, Discord global scope).
Dropped 3 office-run.py-wiring tests (jenkins catalog curation), fork-specific.

ACL suite 231/231 green on our tree. Runtime self-contained (our run.py calls
no changed API). Pre-existing test_discord_channel_prompts failures unchanged.
ethenotethan added a commit to ethenotethan/harness that referenced this pull request Aug 4, 2026
NousResearch#25)

Record-shaped living artifacts (contributor tables, client lists,
spend entries): {"key": "login", "columns": [...], "rows": [{...}]}.
Rows union by the declared key field with incoming-wins conflicts —
the map-marker semantics generalized — so writers may set only
new/changed rows and other writers' rows survive. Keyless rows drop
(unkeyable), top-level fields carry over, malformed JSON passes
through rather than bricking.

First consumer: a scheduled job maintaining a "darkbloom-contributors"
dataset from the wiki's person entities, rendered live in
HermesNative's Artifacts pane.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Meraniya pushed a commit to Meraniya/hermes-agent that referenced this pull request Aug 6, 2026
…(Codex PR NousResearch#6, NousResearch#10) (NousResearch#25)

Addresses chatgpt-codex-connector review comments on merged PRs NousResearch#6 and NousResearch#10.

tools/lazy_deps.py (PR NousResearch#6 — lazy install pins stale after Dependabot audit):
- platform.slack: aiohttp 3.13.4 -> 3.14.1 (match [slack] extra)
- platform.discord: add aiohttp==3.14.1 (discord.py uses aiohttp
  transitively; without the pin, a first-use lazy install can leave an
  older vulnerable aiohttp while `hermes update` considers the feature
  satisfied)
- terminal.modal / terminal.vercel: add cbor2==6.1.2 (both backends pull
  cbor2 transitively; pin the audited version so existing installs
  refresh the vulnerable transitive package)
- tool.dashboard: add starlette==1.3.1 (FastAPI pulls starlette
  transitively; same rationale as above)

plugins/platforms/discord/adapter.py (PR NousResearch#10 — voice extra no longer
ships PyNaCl):
- join_voice_channel now checks for PyNaCl availability before calling
  channel.connect(). The `voice` extra was intentionally stripped of
  PyNaCl (it pinned a vulnerable version and triggered Dependabot), but
  /voice join was still registered and would crash at runtime with an
  opaque missing-module error. The guard fails fast with a logged
  warning instructing the user to install PyNaCl>=1.6.2 manually.

tests/gateway/test_discord_race_polish.py:
- Added test_join_voice_returns_false_when_pynacl_missing (verifies the
  guard fires before channel.connect())
- Added test_join_voice_proceeds_when_pynacl_available (pins the happy
  path so the guard is confirmed as the regression boundary)
- Updated test_concurrent_joins_do_not_double_connect to mock nacl via
  patch.dict (prevents sys.modules leakage into other test files)

PR NousResearch#3 website comments already fixed in main: serialize-javascript
override is at 7.0.5, all @docusaurus/* packages aligned at 3.10.1.

Verification: pytest tests/gateway/test_discord_race_polish.py
tests/tools/test_lazy_deps.py tests/test_package_json_lazy_deps.py
tests/tools/test_discord_tool.py (157 passed, 2 pre-existing warnings).
Meraniya pushed a commit to Meraniya/hermes-agent that referenced this pull request Aug 6, 2026
… activations + PyNaCl error) (NousResearch#28)

* fix(lazy_deps): use primary spec for active_features detection

active_features() was using any() over all specs, which caused false
activations when a transitive package (aiohttp, cbor2, starlette) shared
by multiple features was installed. For example:
- aiohttp from Slack -> platform.discord falsely active
- cbor2 from Modal -> terminal.vercel falsely active (and vice-versa)
- starlette from MCP -> tool.dashboard falsely active

Fix: check only specs[0] (the primary/unique package). By convention the
first tuple element is always a package exclusive to that feature. This
prevents hermes update from installing packages for features the user
never enabled. Addresses Codex PR NousResearch#25 comments.

* fix(discord): raise on missing PyNaCl instead of returning False

Returning False from join_voice_channel() when PyNaCl is absent bypassed
the PyNaCl-specific error guidance in GatewayRunner._handle_voice_channel_join
(gateway/run.py:10937). The caller only shows the install hint when the call
raises with "pynacl" in the error message; a False return fell through to the
generic "Check bot permissions" message instead. Addresses Codex PR NousResearch#25.

* test: update pynacl guard test to expect RuntimeError

The adapter now raises RuntimeError (instead of returning False) so that
GatewayRunner._handle_voice_channel_join can surface the PyNaCl-specific
install guidance. Update the test to match.

* fix(nix): update npm lockfile hashes for tui and web

* fix(nix): update npm lockfile hashes for tui and web
Meraniya pushed a commit to Meraniya/hermes-agent that referenced this pull request Aug 6, 2026
…nting (NousResearch#67)

* feat(devops): add config-integrity-watchdog skill with git-backed fingerprinting

Replaces mutable .sha256 sidecar with an append-only integrity log
committed to the dotfiles git repo. A process without git commit
credentials cannot silently forge a fingerprint entry.

- seal.py: hash config + append to log + git commit
- verify.py: compare current hash against latest seal; detect log tampering
- restore.py: revert from git + re-seal; backs up tampered config
- PLAN.md: project plan (Linear not available)
- Tests: seal->verify, seal->tamper->verify, seal->tamper->restore->verify (29 passing)

Closes the 19-day recurring config hijack pattern (Event NousResearch#25+).
Slack: https://mfc-nyc.slack.com/archives/C0BD8QBUSJF/p1782742870774319

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQKCc5mDedYAiCNyXnTezh

* fix(tests): add type ignore for pytest import and None guard for regex match

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQKCc5mDedYAiCNyXnTezh

* feat(cli): add hermes config seal/verify/restore commands

Integrates config-integrity-watchdog into the Hermes CLI so users
can seal, verify, and restore config integrity without remembering
script paths. Calls core logic shared with the standalone scripts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQKCc5mDedYAiCNyXnTezh

* fix(cli): fallback import path, move PLAN.md to .plans/, add restore caveat

- _import_core() now tries ~/.hermes/skills first, then repo-relative
  skills/ as fallback for pre-sync usage; prints clear error + exits 1
  if skill not found in either location
- Move PLAN.md to .plans/config-integrity-watchdog.md
- Append git-HEAD caveat to restore.py module docstring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQKCc5mDedYAiCNyXnTezh

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants