hermes-agent/tests
Siddharth Balyan 7ba5df0d52
feat(billing): /credits command — balance + portal top-up handoff (#44776)
* feat(billing): /usage → portal top-up browser handoff

Add the terminal side of the billing slice (phase 2a): start a top-up by
throwing the user to the portal billing page with the top-up modal open. The
terminal does not confirm, poll, or track payment — checkout completes in the
browser and the next /usage shows the new balance.

- nous_account.py: parse organisation.slug/name from /api/oauth/account into
  NousPortalAccountInfo; add nous_portal_topup_url() building the org-pinned
  {base}/orgs/{slug}/billing?topup=open with a null-slug fallback to the legacy
  {base}/billing?topup=open (never /orgs/None/...).
- portal_cli.py: 'hermes portal topup' — fresh account fetch, identity line
  (Topping up as <email> / org <name>), browser open with printed-URL fallback,
  no-wait closing copy. No polling/confirmation (deferred to 2b).
- account_usage.py: the shared /usage credits block now links the org-pinned
  top-up URL (auto-opens the modal) + points to the command.

Depends on NAS #409 (organisation.slug/name + ?topup=open). Do not merge until
that is live on the target env; until then /api/oauth/account returns
organisation: { id } only and the URL falls back to legacy.

* feat(billing): /credits command for balance + top-up handoff

Replace the standalone `hermes portal topup` subcommand with an in-session
/credits slash command — a focused money surface (balance in, top-up out) that
works in the CLI, TUI, and every messaging platform from one registry entry.

- commands.py: register /credits (Info category). Slack is at its 50-slash cap,
  so /credits is routed via /hermes credits on Slack only (new
  _SLACK_VIA_HERMES_ONLY set) to avoid clamping a canonical command off the
  native list and breaking Telegram parity; native everywhere else.
- account_usage.py: build_credits_view() — one portal fetch → balance lines +
  identity line + org-pinned top-up URL + depleted flag, consumed by all
  surfaces. Reuses the same snapshot/URL builder as /usage so numbers match.
- cli.py: _show_credits() — balance block + identity line + 3-button panel
  (Open top-up / Copy link / Cancel) via the existing prompt_toolkit modal.
  ASK, never auto-launch; headless falls back to printing the URL.
- gateway/slash_commands.py: _handle_credits_command() — renders the block +
  tappable top-up URL + no-wait copy; works on button and plain-text platforms.
- /usage credits line now points to /credits.
- Retire `hermes portal topup` (portal_cli.py back to baseline); the engine
  (slug/name parse + nous_portal_topup_url) stays as the shared core.

No polling, no payment confirmation (billing phase 2a). Depends on NAS #409.

* fix(credits): /credits works in the TUI slash-worker (non-interactive)

In the TUI, /credits runs in the slash-worker subprocess where there is no
live prompt_toolkit app and stdin is the JSON-RPC pipe. _show_credits called
the 3-button modal unconditionally, which fell back to reading stdin →
exception → slash.exec rejected → the command produced no output (only the
pre-existing 'Credit access paused' banner showed).

- _show_credits: when self._app is None (TUI worker / piped / non-interactive),
  render the text variant — balance block + tappable top-up URL + no-wait line,
  same affordance as the messaging surfaces — and skip the modal entirely. The
  3-button panel still renders in the interactive CLI.
- Depleted banner copy: 'run /usage for balance' → 'run /credits to top up'
  now that /credits is the dedicated money surface (+ tests).
- Regression tests: _show_credits with self._app=None renders text and never
  invokes the modal; logged-out path.

* feat(tui): credits.view RPC for the /credits tappable top-up button

Add a credits.view JSON-RPC method returning the structured CreditsView
(logged_in, balance_lines, identity_line, topup_url, depleted) so the TUI can
render a clickable <Link> top-up button instead of plain text. Account-
independent (portal fetch gated on a logged-in Nous account), fail-open to
{logged_in: false} on any hiccup. Mirrors session.usage's credits-block pattern.

Frontend (TUI-local /credits command + Ink component) lands separately.

* feat(tui): /credits command with keyboard-driven top-up confirm

TUI-local /credits: fetches the structured balance via the credits.view RPC,
prints the balance + identity + top-up URL, then arms the EXISTING confirm
overlay (Enter = open top-up in browser via openExternalUrl, Esc = cancel).
Reuses ConfirmReq — no new overlay component/state/input handler. Headless
(openExternalUrl returns false) falls back to printing the URL.

- gatewayTypes.ts: CreditsViewResponse.
- commands/credits.ts: the command (mirrors /status's rpc+guarded pattern).
- registry.ts: register creditsCommands.
- test: balance+overlay armed, headless fallback, no-url, logged-out (4 cases).

Matches the CLI /credits 'Enter to open' affordance. Phase 2a: no polling.
2026-06-12 08:51:10 +00:00
..
acp feat(acp): emit session provenance metadata for compression rotation (#41724) 2026-06-07 22:22:21 -07:00
acp_adapter feat(azure-foundry): add Microsoft Entra ID auth 2026-05-18 10:14:38 -07:00
agent feat(billing): /credits command — balance + portal top-up handoff (#44776) 2026-06-12 08:51:10 +00:00
cli feat(cli): persist resolved approval/clarify prompts in scrollback (#44702) 2026-06-12 01:14:35 -07:00
cron Merge pull request #44331 from NousResearch/hermes/hermes-6b48295e 2026-06-11 22:48:06 -07:00
docker fix(gateway): auto-start after container restart via planned-stop marker (#42675) (#43236) 2026-06-10 14:01:34 +10:00
e2e chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
fakes
fixtures/plugins/example-dashboard/dashboard feat(dashboard): nous-blue theme, bulk sessions, schedule picker (#37383) 2026-06-02 12:37:40 -04:00
gateway feat(billing): /credits command — balance + portal top-up handoff (#44776) 2026-06-12 08:51:10 +00:00
hermes_cli feat(billing): /credits command — balance + portal top-up handoff (#44776) 2026-06-12 08:51:10 +00:00
hermes_state fix(sessions): archive compressed conversation lineages 2026-06-11 12:31:10 +05:30
honcho_plugin fix(plugins): thread-safe lazy-singleton helpers; fix honcho TOCTOU (#24759) (#42150) 2026-06-08 09:35:22 -07:00
integration refactor(gateway): migrate Home Assistant adapter to bundled plugin 2026-06-06 11:46:24 -07:00
openviking_plugin fix(openviking): add missing /agent/{agent}/ segment to memory URI — fixes #36969 2026-06-04 17:40:33 -07:00
plugins fix(photon): stop gateway restarts from orphaning the sidecar on its port 2026-06-12 01:07:38 -07:00
providers fix(openrouter): route reasoning_effort to verbosity for adaptive Anthropic models (#43436) 2026-06-10 15:03:01 +05:30
run_agent fix(credits): suppress usage gauge when top-up funds exist + add display.credits_notices toggle (#44716) 2026-06-12 01:06:46 -07:00
scripts fix(skills-hub): stop shipping a degenerate index when GitHub taps collapse (#42347) 2026-06-08 15:21:28 -07:00
skills fix(google-workspace): fall back to uv when venv has no pip (#39516) 2026-06-05 13:30:02 +10:00
stress chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
tools feat(messaging): expose action='unreact' in send_message + react dispatch tests 2026-06-12 01:07:38 -07:00
tui_gateway fix(desktop): rebind sessions after websocket reconnect (salvage of #41740) (#43004) 2026-06-09 19:01:00 +00:00
website feat(skills): fix browse cap, add source links + copy buttons + category cleanup (#37143) 2026-06-01 19:52:28 -07:00
__init__.py
conftest.py fix: batch of small robustness/correctness fixes from @kyssta-exe 2026-06-01 19:51:03 -07:00
run_interrupt_test.py
test_account_usage.py
test_atomic_replace_symlinks.py
test_base_url_hostname.py
test_batch_runner_checkpoint.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_bitwarden_secrets.py fix(bitwarden): prevent zip-slip path traversal when extracting bws binary (#40569) 2026-06-06 18:33:44 -07:00
test_cli_file_drop.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_cli_manual_compress.py fix(tests): catch up six stale tests after compression/aux/kanban changes (#28465) 2026-05-18 21:43:59 -07:00
test_cli_skin_integration.py
test_ctx_halving_fix.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_dashboard_sidecar_close_on_disconnect.py fix(tui-gateway): reap leaked slash_worker sessions on disconnect + active_list liveness (re-scoped onto current main) 2026-06-08 10:02:05 -07:00
test_desktop_mac_entitlements.py test(desktop): assert macOS device entitlements are inherited 2026-06-03 07:32:00 +07:00
test_docker_home_override_scripts.py Repair cron ownership on container restart (#41976) 2026-06-10 15:32:34 +10:00
test_docker_stage2_browser_discovery.py fix(docker): discover Playwright headless_shell browser (#35717) 2026-06-01 16:06:44 +10:00
test_dockerfile_tini_compat_shim.py fix(docker): add /usr/bin/tini compatibility shim for legacy wrappers (#34192) (#34382) 2026-06-01 13:32:55 +10:00
test_empty_model_fallback.py test(models): guard Nous silent default against expensive-flagship escalation 2026-06-05 02:54:34 -07:00
test_empty_session_hygiene.py fix: in-memory transcript blocks empty-session prune 2026-06-10 17:37:34 -07:00
test_env_loader_secret_sources.py fix(secrets): only apply external secrets once per HERMES_HOME per process (#32271) 2026-05-25 15:18:55 -07:00
test_evidence_store.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_gateway_streaming_nested_config.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_get_tool_definitions_cache_isolation.py fix(gateway): close residual memory-leak sites under heavy scheduled workload 2026-06-08 06:32:42 -07:00
test_hermes_bootstrap.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_hermes_constants.py fix(constants): use windows native default hermes home 2026-06-03 19:37:29 -07:00
test_hermes_home_profile_warning.py
test_hermes_logging.py fix(gateway): tolerate Unicode in stderr log handlers on Windows 2026-06-06 19:57:44 -07:00
test_hermes_state.py fix(cron): bound the desktop run-history query to one job (#41088) 2026-06-07 02:41:01 -07:00
test_hermes_state_compression_locks.py fix(compression): prevent session-id fork from concurrent compressions (#34351) 2026-05-28 21:40:39 -07:00
test_hermes_state_wal_fallback.py fix(kanban): skip redundant WAL pragma on already-WAL connections 2026-05-27 14:31:55 -07:00
test_honcho_client_concurrency.py fix(plugins): thread-safe lazy-singleton helpers; fix honcho TOCTOU (#24759) (#42150) 2026-06-08 09:35:22 -07:00
test_honcho_client_config.py fix(honcho): harden self-hosted setup paths 2026-05-29 22:29:48 -07:00
test_honcho_session_context.py fix(honcho): align user context peer perspective 2026-05-27 10:49:33 -07:00
test_honcho_startup_fail_open.py fix: make Honcho startup fail open 2026-06-01 20:13:42 -07:00
test_install_no_initial_commit.py fix(install): move broken checkout aside instead of deleting it 2026-06-08 02:18:21 -07:00
test_install_sh_browser_install.py fix(install): support non-sudo service-user installs on apt distros (#25814) 2026-05-14 09:05:31 -07:00
test_install_sh_pythonpath_sanitization.py
test_install_sh_root_fhs_uv_python_path.py test(install): harden uv-python-path regression test against future drift 2026-05-27 13:55:51 -07:00
test_install_sh_setup_wizard_tty_probe.py
test_install_sh_symlink_stomp.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_install_sh_termux_network_prereqs.py
test_ipv4_preference.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_lazy_session_regressions.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_lint_config.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_live_system_guard_self_test.py chore: ruff auto-fix PLR6201 resweep — tuple → set in membership tests (#27355) 2026-05-17 02:29:41 -07:00
test_mcp_serve.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_mini_swe_runner.py
test_minimax_model_validation.py
test_minimax_oauth.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_minisweagent_path.py
test_model_forces_max_completion_tokens.py fix(params): send max_completion_tokens for newer OpenAI families on custom endpoints 2026-06-09 23:22:10 -07:00
test_model_picker_scroll.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_model_tools.py feat(middleware): add adaptive execution intercepts 2026-06-03 11:22:06 -07:00
test_model_tools_async_bridge.py fix(web): run URL SSRF checks off the event loop in async paths 2026-06-04 18:04:47 -07:00
test_ollama_num_ctx.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_output_cap_parsing.py test(agent): cover char-based output-cap overflow parsing (#42741) 2026-06-09 03:17:12 -07:00
test_package_json_lazy_deps.py fix(update): make Camofox lazy-installed instead of eager (#27055) 2026-05-16 12:15:45 -07:00
test_packaging_metadata.py fix(packaging): ship optional-mcps catalog in wheel and sdist (#39859) 2026-06-09 14:03:20 -04:00
test_plugin_skills.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_plugin_utils.py fix(plugins): thread-safe lazy-singleton helpers; fix honcho TOCTOU (#24759) (#42150) 2026-06-08 09:35:22 -07:00
test_process_loop_event_loop_warning.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_project_metadata.py fix(deps): align anthropic extra pin with lazy pin + guard whole pin surface (#42335) 2026-06-08 12:11:54 -07:00
test_retry_utils.py
test_run_tests_parallel.py fix(ci): make parallel runner's exit-4 retry robust for newly-added test files (#42994) 2026-06-09 21:39:09 -07:00
test_sanitize_tool_error.py security: sanitize tool error strings before injecting into model context (#26823) 2026-05-16 00:57:39 -07:00
test_slash_worker_watchdog.py feat(slash-worker): self-terminate on parent death via create_time watchdog 2026-06-08 07:03:12 -07:00
test_sql_injection.py
test_state_db_malformed_repair.py fix(state.db): recover from malformed sqlite_master so hidden sessions reappear (#43149) 2026-06-09 18:49:08 -05:00
test_subprocess_home_isolation.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_termux_all_extra_compat.py
test_timezone.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_toolset_distributions.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_toolsets.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_trajectory_compressor.py fix(research): keep tool_call/tool_response pairs intact when compressing trajectories 2026-06-07 05:01:27 -07:00
test_trajectory_compressor_async.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_transform_llm_output_hook.py
test_transform_tool_result_hook.py test: stub has_hook in transform_tool_result hook tests 2026-06-03 06:36:46 -07:00
test_tui_gateway_server.py fix(desktop): discover MCP tools for dashboard /api/ws backends (#44512) 2026-06-11 22:45:45 +00:00
test_tui_gateway_ws.py fix: resolve rebase conflict in _teardown_session worker cleanup 2026-06-08 10:02:05 -07:00
test_utils_truthy_values.py
test_web_server.py fix(tui-gateway): reap leaked slash_worker sessions on disconnect + active_list liveness (re-scoped onto current main) 2026-06-08 10:02:05 -07:00
test_wheel_locales_e2e.py fix(packaging): ship locales/ i18n catalogs in wheel, sdist, and Nix (#38383) 2026-06-03 12:00:27 -07:00
test_yuanbao_integration.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_yuanbao_markdown.py
test_yuanbao_pipeline.py test(yuanbao): add missing patch import to pipeline tests 2026-06-09 03:17:00 -07:00
test_yuanbao_proto.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_yuanbao_shutdown.py fix(yuanbao): bound ws.close() so an idle server can't stall shutdown ~5s (#40607) 2026-06-07 17:49:38 -07:00