Commit Graph

24 Commits

Author SHA1 Message Date
Teknium
7a33369e81 simplify(compat): interrupt — drop _ThreadAwareEventProxy/_interrupt_event legacy alias, repoint 2 test files
No runtime consumer read the proxy (terminal_tool/environments call is_interrupted()/set_interrupt()
directly); its only users were tests patching tools.interrupt._interrupt_event, which had no effect on
the code under test. tools/terminal_tool.py's own re-export of the name is owned by another worker.
2026-09-03 14:00:59 -07:00
Teknium
c93ace77c2 simplify(compat): config/runtime_provider/plugins/commands/secrets_cli/kanban — drop 96 re-exports (incl. PEP 562 facades) + 3 aliases (get_pre_tool_call_directive/_block_message, get_telegram_handler_factories), repoint 56 callers + 50 test files 2026-09-03 14:00:17 -07:00
Teknium
e83816a4d1 review-fix(comments): restore lost #NNNN rationale comments across non-test source (mechanical sweep, condensed, code unchanged)
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
2026-09-03 09:44:26 -07:00
Teknium
bcddf6a084 refactor(hermes_cli): setup cluster — drop statement-separating blanks inside function bodies (AST-neutral) 2026-09-02 22:25:24 -07:00
Teknium
17d20ae1e5 refactor(hermes_cli): setup cluster — compact send arg table, quick-setup label builders, first-time mode table by name 2026-09-02 22:21:47 -07:00
Teknium
f622145b84 refactor(hermes_cli): setup cluster — hug lone closing brackets 2026-09-02 21:53:35 -07:00
Teknium
f2b25bebe7 refactor(hermes_cli): setup cluster — print_header(gap=), yes/no answer table, bws _step helper, sync row table 2026-09-02 21:33:24 -07:00
Teknium
71e0cb0301 refactor(hermes_cli): setup cluster — join wrapped prose literals, drop blanks after local imports 2026-09-02 21:20:04 -07:00
Teknium
2e555504bf refactor(hermes_cli): setup cluster — compact docstrings/comments, fold help-line loops into _info 2026-09-02 21:12:33 -07:00
Teknium
21835bac7a refactor(hermes_cli): setup cluster — pack import/signature lists, tighten wizard picker lambdas 2026-09-02 20:49:41 -07:00
Teknium
756507554b refactor(hermes_cli): setup cluster pass 1 — unify env-var prompt/save, nous-flow, migration-step, bws setup phase helpers 2026-09-02 20:36:04 -07:00
Teknium
3371814034 refactor(hclib): auth — Nous/Copilot/portal auth, credential lifecycle, keepalive simplified 2026-09-02 14:42:18 -07:00
Halldrix
3f9150e5c4 fix(secrets_cli): defer bitwarden backend import to first attribute access
Address blocking review on #86782 (trevorgordon981, 2026-08-15): the
previous lazy closures in main.py only deferred the module-level
"import secrets_cli" statement, but were themselves invoked at parse
time — so the chain main -> secrets_cli -> bitwarden -> cryptography
still ran eagerly on every command, including `hermes update --check`.
The closure indirection was dead laziness.

Move the laziness to where the crypto payload actually lives:

1. secrets_cli.py: drop the module-top "from agent.secret_sources
   import bitwarden as bw" import. Each cmd_* handler now resolves the
   backend via a local _load_bw() helper, which imports
   agent.secret_sources.bitwarden on first use. register_cli() no
   longer touches crypto at all — it only wires argparse structure.

2. _BWS_VERSION is duplicated in secrets_cli as a plain string so the
   "install" subparser help text renders without importing the
   backend. agent.secret_sources.bitwarden._BWS_VERSION stays the
   source of truth; bump both together when pinning a new bws release.

3. Module-level PEP 562 __getattr__ resolves "secrets_cli.bw" lazily.
   Existing upstream tests (test_secrets_bitwarden_non_tty.py) that
   monkeypatch "hermes_cli.secrets_cli.bw.find_bws" keep working —
   monkeypatch resolves the string one level deep, triggering
   __getattr__, which imports the real bitwarden module and lets the
   patch land on the same cached module object the handlers import.

4. main.py: revert the closure indirection back to a direct parse-time
   _secrets_cli.register_cli() call — safe now that register_cli is
   crypto-free by construction. The argparse wiring is again visible
   at the call site (matching checkpoints.py / curator.py convention),
   which addresses the original parse-time-vs-post-parse contract
   concern from the previous review round.

Adds a decisive main()-level regression test requested by review:
test_main_update_check_crypto_absent_in_sys_modules spawns main() in a
subprocess with argv=['hermes', 'update', '--check'], patches
hermes_cli.main._cmd_update_check to short-circuit before any network,
and asserts cryptography.hazmat.bindings._rust stays out of
sys.modules both at dispatch time and after main() returns. This is
the exact invariant the Windows self-lock depends on; the previous
import-only tests could not observe the failure because parser
construction runs inside main().

Verification:
- scripts/run_tests.sh tests/test_lazy_secrets_import.py
  tests/test_lazy_secrets_dispatch.py
  tests/hermes_cli/test_secrets_bitwarden_non_tty.py
  -> 13/13 passed (includes the new decisive test + the 2 upstream
     tests that broke under the earlier _LazyBitwarden proxy).
- Sabotage run: same suite against the pre-fix main.py + secrets_cli.py
  fails the new decisive test with "cryptography._rust loaded by main()
  before update dispatch" — confirming the test guards the bug.
- Manual trace: at _cmd_update_check dispatch time, sys.modules
  contains hermes_cli.secrets_cli (parse-time structure only) but NOT
  agent.secret_sources.bitwarden and NOT cryptography._rust.

Refs: #86781
Refs: #83569
2026-08-15 01:55:22 -07:00
Halldrix
230e5ff048 fix(main): revert secrets_cli to upstream, wrap registration in lazy closures
secrets_cli.py: revert to upstream eager import (tests rely on  as
module attribute for monkeypatch; the previous _LazyBitwarden proxy
broke 2 existing tests because agent.secret_sources.bitwarden lacks
BwsClient in the upstream codebase).

main.py: wrap secrets_cli/onepassword_secrets_cli imports in
_register_bitwarden/_register_onepassword closures.  The parser tree is
created at parse-time (register_cli attaches subparsers), but the
module import itself defers to first use — argparse only calls the
closure when it encounters the subcommand, so importing main() no
longer loads bitwarden/cryptography eagerly.

env_loader.py: keep the known-source-names gate (any dict with a
known source name and enabled: true).

Verification: 12/12 tests pass (3 sys.modules + 7 E2E subprocess +
2 upstream test_secrets_bitwarden_non_tty).
2026-08-15 01:55:22 -07:00
Halldrix
5a76c8a978 fix(update): lazy-import secrets backends + defer registry import — break Windows self-lock loop
Address review feedback from trevorgordon981 on PR #86782:

1. **Pre-register parsers at parse-time, lazy-import backends only**
   - secrets_cli.register_cli() and onepassword_secrets_cli.register_cli()
     now run eager at parser-build time (no deferral past parse_args)
   - Only the agent.secret_sources.bitwarden/onepassword imports are lazy
     (inside each cmd_* handler via _load_bitwarden()/_load_onepassword())
   - This eliminates the 'invalid choice' and infinite-recursion risks

2. **Known-source-names gate for env_loader registry**
   - Only keys in {bitwarden, onepassword, op, 1password, bw} trigger the
     registry import; a generic dict entry no longer forces crypto load
   - Prevents unrelated config dicts from paying crypto cost

3. **End-to-end tests for the real dispatch paths**
   - test_bitwarden_setup_help: runs real CLI subprocess with --help
   - test_bitwarden_status/disable/onepassword_status: run real handlers
   - test_update_check_clean/no_self_lock: run real update --check
   - test_update_check_no_cryptography: sys.modules inspection (backup)

4. **Fix flaky test_turn_lease.py** (unrelated pre-existing failure)

The architecture guarantees:
- parse-time: zero cryptography load (all backends lazy)
- dispatch-time: crypto loads exactly once per secrets command
- update path: completely clean of cryptography._rust mapping

Refs #86781, #86782
2026-08-15 01:55:22 -07:00
teknium1
3d48f893da refactor: single build_subprocess_env() factory for all child-process spawns (profile + secret-scrub single owner) 2026-07-29 10:14:11 -07:00
Stoltemberg
c89481db5e fix: add explicit UTF-8 encoding to all subprocess text=True calls (#53428)
On Windows with Chinese locale (GBK), subprocess.run(text=True) without
explicit encoding causes UnicodeDecodeError crashes. This fix adds
encoding='utf-8', errors='replace' to all subprocess.run() and
subprocess.Popen() calls that use text=True across 76 non-test Python files.

Fixes #53428 (master tracker for Windows GBK locale crash).

Note: credential_pool.py and electron changes excluded per reviewer request —
those will be submitted as separate focused PRs.
2026-07-24 11:45:57 -07:00
izumi0uu
8e089db689 fix(secrets): validate bitwarden status token
Keep the env-presence row, but add a real Bitwarden probe so revoked or malformed tokens no longer look healthy in hermes secrets bitwarden status.

Also document the new status behavior and lock it in with a dedicated regression test.

Refs: NousResearch/hermes-agent#40275
Tested: ./scripts/run_tests.sh tests/hermes_cli/test_bitwarden_status.py tests/test_bitwarden_secrets.py
Tested: .venv/bin/python -m ruff check hermes_cli/secrets_cli.py tests/hermes_cli/test_bitwarden_status.py
2026-07-22 03:20:09 -07:00
Teknium
d3b0e61429 feat(secrets): one-command token rotation + actionable startup errors for all secret sources (#68605)
* feat(secrets): one-command token rotation + actionable startup errors for all secret sources

When a Bitwarden machine-account token expired, users saw a raw Rust
error dump (invalid_client + Location: + backtrace hints) and the only
fix was manually editing .env or re-running the whole setup wizard.

- New `hermes secrets bitwarden token` / `hermes secrets onepassword
  token`: paste a new token (masked prompt or flag), the command probes
  the backend BEFORE persisting — a rejected token changes nothing; a
  good one is written to .env and the fetch caches are cleared.
- New optional SecretSource.remediation(kind, cfg) hook: startup
  warnings now print a '→ Run `hermes secrets <name> token`…' fix-it
  line after any fetch error, for bundled AND plugin sources (generic
  per-ErrorKind defaults in the ABC).
- bws stderr is summarized to its cause line (Location:/backtrace noise
  dropped) and invalid_client/invalid_grant/400 identity rejects are
  now classified AUTH_FAILED (was INTERNAL) with a plain-English
  explanation naming the token env var.
- op whoami probe accepts a candidate token so rotation validates the
  NEW credential, not the ambient one.

Additive hook with defaults — no SECRET_SOURCE_API_VERSION bump.

* docs: fix MDX parse error in secret-source-plugin hook table

Escaped backticks around a <name> placeholder made MDX parse it as an
unclosed JSX tag, breaking the docs-site build.  Use a plain code span
instead.
2026-07-21 06:41:04 -07:00
Teknium
89040e0db3 fix(secrets): fail early with clear error when bitwarden setup runs without TTY (#40571)
Salvaged from #40280; cleaned up, re-verified against main, tests added.

Co-authored-by: liuhao1024 <liuhao1024@users.noreply.github.com>
2026-06-06 18:36:40 -07:00
kshitijk4poor
66827f8947 chore: prune unused imports and duplicate import redefinitions
Remove unused imports (F401) and duplicate/shadowed import
redefinitions (F811) across the codebase using ruff's safe
autofixes. No behavioral changes -- imports only.

- ~1400 safe autofixes applied across 644 files (net -1072 lines)
- __init__.py re-exports preserved (excluded from F401 removal so
  public re-export surfaces stay intact)
- Re-exports that are imported or monkeypatched by tests but look
  unused in their defining module are kept with explicit # noqa:
  F401 (gateway/run.py load_dotenv; run_agent re-exports from
  agent.message_sanitization, agent.context_compressor,
  agent.retry_utils, agent.prompt_builder, agent.process_bootstrap,
  agent.codex_responses_adapter)
- Unsafe F841 (unused-variable) fixes deliberately skipped -- those
  can change behavior when the RHS has side effects
- ruff lints remain disabled in pyproject.toml (only PLW1514 is
  selected); this is a one-time cleanup, not a config change

Verification:
- python -m compileall: clean
- pytest --collect-only: all 27161 tests collect (zero import errors)
- core entry points import clean (run_agent, model_tools, cli,
  toolsets, hermes_state, batch_runner, gateway)
- static scan: every name any test imports directly from an edited
  module still resolves
2026-05-28 22:26:25 -07:00
helix4u
ec4d6f1823 fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
Teknium
bc3f1f4f34 feat(secrets/bitwarden): EU Cloud + self-hosted server URL support (#31378)
Closes #31370.

bws defaults to the US identity endpoint, so EU Cloud and self-hosted
machine-account tokens fail with [400 Bad Request] {"error":"invalid_client"}
during 'hermes secrets bitwarden setup'. The token is valid — it's just
being checked against the wrong region.

Add a Bitwarden region step to the wizard between the access-token and
project-list steps:

  Step 1  Install bws
  Step 2  Provide access token
  Step 3  Pick region   <-- new (US / EU / self-hosted-custom-URL)
  Step 4  Pick project  (now talks to the right endpoint)
  Step 5  Test fetch

Region is stored in config.yaml as secrets.bitwarden.server_url and
plumbed into every bws subprocess as BWS_SERVER_URL (project list,
secret list, test fetch, and the env_loader startup pull).

Also:
- Non-interactive: 'hermes secrets bitwarden setup --server-url ...'
- Pre-existing BWS_SERVER_URL in the shell is detected and reused
- Cache key includes server_url so EU/US fetches don't collide
- 'hermes secrets bitwarden status' shows the configured region
- 'invalid_client' / '400 Bad Request' from bws now triggers a hint
  pointing at the region setting instead of looking like a bad token
2026-05-24 02:19:57 -07:00
Teknium
552e9c7881 feat(secrets): Bitwarden Secrets Manager integration with lazy bws install (#30035)
* feat(secrets): Bitwarden Secrets Manager integration with lazy bws install

Pull API keys from Bitwarden Secrets Manager at process startup
instead of storing them all in plaintext in ~/.hermes/.env.  One
bootstrap token (BWS_ACCESS_TOKEN) replaces N per-provider keys, and
rotating a credential becomes a single change in the Bitwarden web
app.

Bitwarden defaults to source of truth: secrets pulled from BSM
overwrite any matching env vars on startup so rotations actually
take effect.  Set secrets.bitwarden.override_existing: false in
config.yaml to invert.

The bws binary is auto-downloaded into ~/.hermes/bin/bws on first
use (pinned to v2.0.0, SHA-256 verified against the GitHub release
checksum file).  No apt, brew, or sudo required.

New surfaces:
  hermes secrets bitwarden setup    — interactive wizard
  hermes secrets bitwarden status   — config + binary + token state
  hermes secrets bitwarden sync     — dry-run fetch / --apply exports
  hermes secrets bitwarden disable  — flip enabled: false
  hermes secrets bitwarden install  — just download the binary

Failures (missing binary, bad token, no network) never block Hermes
startup — they emit a one-line warning to stderr and continue with
whatever credentials .env already had.

Docs: website/docs/user-guide/secrets/{index,bitwarden}.md
Tests: tests/test_bitwarden_secrets.py (26 tests, hermetic — bws
       subprocess and HTTP downloads fully mocked)

* chore(infographic): add bitwarden-secrets-manager bento-grid retro-pop-grid

Generated for PR #30035 — Bitwarden Secrets Manager integration.
Style picked via pick_pr_infographic_style.py rotation:
  layout: bento-grid
  style:  retro-pop-grid
  aspect: 1:1 square

Saved at infographic/bitwarden-secrets-manager/infographic.png
2026-05-21 14:10:34 -07:00