hermes-agent/tests/hermes_cli
Siddharth Balyan b51fbc738b
feat(tui+cli): change your Nous plan from the terminal (/subscription, /topup, terminal-billing UX) (#51639)
* feat(tui): rename /billing slash command to /topup

Behavior-preserving rename of the /billing command surface to /topup.
Changes: billing.ts → topup.ts (export topupCommands, name 'topup', new
help string), registry.ts import+spread updated, billingOverlay.tsx
overview header 'Usage credits' → 'Top up credits', billingCommand.test.ts
→ topupCommand.test.ts with import/lookup/call updated. RPC method names
(billing.state, billing.charge, etc.) and component/symbol names unchanged.

* refactor(tui): extract overlay primitives to shared module

Lift MenuRow, ActionRow, footer, and barCells() out of billingOverlay.tsx
into overlayPrimitives.tsx so the upcoming subscriptionOverlay.tsx can
import them instead of duplicating. spendBar now calls barCells() —
output is byte-identical. Pure behavior-preserving refactor.

* feat(tui): add /subscription + /topup CTAs to /usage output

Every /usage render now ends with 'Run /subscription to change plan
· /topup to add credits' — both the healthy (with-calls) and depleted
(no-calls) paths. Strings-only change, no WS1 dependency.

* feat(tui): add subscription wire types

Add SubscriptionTierOption, SubscriptionStateResponse, and
SubscriptionManageLinkResponse to gatewayTypes.ts. Type-only — no
usages yet. Mirrors the BillingStateResponse conventions (snake_case,
Decimals as strings) and reuses BillingErrorPayload for error mapping.

* feat(gateway): add subscription.state + subscription.manage_link RPCs

- agent/subscription_view.py: SubscriptionState dataclass + fail-open
  build_subscription_state() (mirrors billing_view pattern) +
  get_subscription_manage_link() for the Stripe deep-link.
- hermes_cli/nous_billing.py: get_subscription_state() +
  post_subscription_manage_link() HTTP helpers for the two NAS endpoints
  (WS1 Phase A/C). The manage-link endpoint raises BillingScopeRequired
  when Remote-Spending is missing (Phase 4 step-up trigger).
- tui_gateway/server.py: _serialize_subscription_state() +
  subscription.state RPC (fail-open) + subscription.manage_link RPC
  (returns {ok,kind,url} or typed error envelope via
  _serialize_billing_error). NOT added to _LONG_HANDLERS — synchronous
  HTTP round-trip, not a device flow.

* feat(tui): add subscription overlay state types + store slot

Add SubscriptionScreen, SubscriptionOverlayCtx, SubscriptionOverlayState
to interfaces.ts and a 'subscription' slot to OverlayState. Wire it into
overlayStore.ts (buildOverlayState + $isBlocked). NOT added to
resetFlowOverlays preserve list — flow-scoped like billing, drops on
turn end.

* feat(tui): build SubscriptionOverlay — overview + confirm + handoff

Pure-render Ink component mirroring billingOverlay.tsx's structure.
Overview screen covers all 5 states (free-upgradeable, mid-tier,
top-tier, not-admin, downgrade-pending) + dunning. Confirm screen is
y/n deep-link to Stripe (NO in-terminal charge). Handoff is the
transient 'Opening Stripe' screen. Imports shared primitives from
overlayPrimitives.tsx. 8 render tests via renderSync covering every
state.

* feat(tui): add /subscription command + overlay wiring

- subscription.ts: SubscriptionOverlayCtx closure (openManageLink,
  refreshState, requestRemoteSpending) + run handler that fetches
  subscription.state and opens the overlay. Alias /upgrade.
- registry.ts: spread subscriptionCommands into SLASH_COMMANDS.
- appOverlays.tsx: render SubscriptionOverlay when overlay.subscription set.
- useInputHandlers.ts: Esc closes subscription overlay; promptOverlay OR
  includes subscription so input is intercepted while open.
- subscriptionCommand.test.ts: 4 tests (fetch+open, logged-out sys line,
  /upgrade alias, /subscription resolves).

* fix(tui/subscription): stop saying Stripe in deep-link copy + fix manage link kind type

Replace all user-facing 'Stripe' mentions in the /subscription overlay and
sys messages with 'your subscription page' — the deep-link target is NAS's
own /manage-subscription page, not the Stripe hosted portal. Stripe only
legitimately appears later at actual Checkout. Also add 'manage' to the
SubscriptionManageLinkResponse.kind union (NAS emits kind:'manage'; was
previously missing from the TypeScript type causing silent narrowing errors).

* feat(tui/subscription): render cancellation-scheduled note with headline precedence

Parse cancelAtPeriodEnd + cancellationEffectiveAt from the NAS contract
(camelCase) in the agent parser (_parse_current), emit cancel_at_period_end
+ cancellation_effective_at from the gateway serializer, extend the
SubscriptionStateResponse type, and render a warn note in OverviewScreen:
'Cancels on {date} — your plan stays active until then.'

Headline precedence when multiple flags co-occur:
  past-due > cancel-scheduled > downgrade-pending > active
The downgradeNote guard is tightened to suppress when cancel is scheduled,
so at most one status line renders at a time.

* feat(tui/subscription): team-context screen — redirect to /topup for team orgs

Parse the NAS context:'personal'|'team' field (defaults to 'personal' for
unknown/missing values), emit it on the gateway wire, add it to
SubscriptionStateResponse. When context is 'team', SubscriptionOverlay
renders a dedicated read-only screen instead of the tier picker:

  'This terminal is connected to {org_name}. Teams run on shared
   credits — use /topup to add funds. Personal subscriptions live
   on your personal account.'

The screen closes on Enter or Esc. The personal/tier-picker path is
unchanged.

* fix(subscription): drop manage-link gateway RPC, build URL locally

The NAS POST /api/billing/subscription/manage-link endpoint was dropped
(it added no server work — the target is the static /manage-subscription
page, not a Stripe-minted secret). Build the URL client-side instead:
{portal_base}/manage-subscription?org_id=<org.id>.

- Remove subscription.manage_link gateway RPC (server.py)
- Remove get_subscription_manage_link helper (subscription_view.py)
- Remove post_subscription_manage_link (nous_billing.py)
- Remove SubscriptionManageLinkResponse type (gatewayTypes.ts)
- Add org_id to SubscriptionState + wire through serializer + TS type
- openManageLink() builds the URL locally via buildManageUrl(), opens
  it with the existing openExternalUrl(), no gateway round-trip
- Drop targetTierId param from openManageLink (v1 sends everyone to
  /manage-subscription; no tier deep-link needed)
- Fix stale test expectations (Stripe copy → subscription page copy)

* chore(subscription): drop unused format_money import

* feat(cli): /subscription + /upgrade, /billing→/topup rename, /usage CTAs

Add the classic-CLI half of the terminal billing surface to match the TUI:
- /subscription (alias /upgrade) command + /topup (renamed /billing, keeps
  'billing' as a back-compat alias) in the command registry.
- Drop the stale 'billing' entry from _SLACK_VIA_HERMES_ONLY (now cli_only).

* feat(subscription): CLI /subscription handler, drop dunning, current:null no-plan

- CLI _show_subscription mirrors the TUI overlay (plan read + tier list + usage
  bar + browser deep-link via subscription_manage_url); credits render as counts.
- Adapt to the updated NAS read contract: remove is_past_due/dunning everywhere
  (a card-failing subscriber returns as a normal plan now), and treat no-plan as
  current:null (parser returns None) rather than an all-null object.
- HERMES_DEV_SUBSCRIPTION_FIXTURE env-driven fixtures + ui-tui fixture harness
  drive every state (CLI + live TUI) with no portal.

Verified against handoff 2026-06-24_subscription-tui-handoff.md.

* feat(billing): CF-4 Remote-Spending revoked-terminal UX (NAS PR #481)

Wire the Remote-Spending gate denial contract end to end:
- nous_billing: BillingRemoteSpendingRevoked (403 remote_spending_revoked →
  reconnect) + BillingSessionRevoked (401 session_revoked → re-login), distinct
  from insufficient_scope; capture actor/code/recovery; 503 stays transient.
- gateway _serialize_billing_error threads the new typed kinds + actor/code/
  recovery to the TUI.
- TUI renderBillingError: actor-aware revoke copy, kills the spend overlay
  immediately (no 15-min zombie button), handles session_revoked, the dual-
  emitted cli_billing_disabled/remote_spending_disabled, role_required,
  idempotency_conflict; poll treats a mid-poll revoke as ambiguous (check
  balance before retry), not a failure.
- CLI _billing_render_charge_error: same denial matrix, actor-aware copy.

Tests: gate-contract mapping + envelope (py) and revoke/session/disabled (TUI).
Per handoff 2026-06-24_remote-spending-TUI-contract-handoff.md.

* refactor(subscription): remove dead step-up scaffolding from /subscription

/subscription only opens a browser deep-link to manage-subscription — that needs
no billing scope, so it can never hit insufficient_scope. Drop the never-fired
'stepup' screen type, requestRemoteSpending ctx fn, and resumeScreen bookkeeping
(leftovers from a superseded plan). The resumable step-up lives on /topup, where
the charge actually gets gated.

* feat(tui/topup): resumable 'Allow Remote Spending' step-up on the charge path

Phase 4: when a charge returns insufficient_scope, the /topup modal no longer
tears down with a 'run /billing again' ConfirmReq. Instead it stays MOUNTED and
switches to a step-up screen:
- charge() is now awaitable, returning a discriminated outcome (submitted |
  needs_remote_spending | error) so the overlay can route without closing.
- StepUpScreen: 'Allow Remote Spending' → await the device-flow grant (browser
  opens via the existing out-of-band billing.step_up.verification event) →
  replay the held charge (pendingCharge.amount) and settle, with no command
  re-run. Never surfaces the raw billing:manage scope.
- armStepUp's fire-and-forget ConfirmReq replaced by requestRemoteSpending();
  the leaky 'billing:manage' / 'Re-authorize' / 'run /billing again' copy is gone.

Tests: charge-outcome routing, step-up grant/deny, and a render test asserting
the step-up copy holds the amount and never leaks billing:manage.
Per handoff 2026-06-24_remote-spending-TUI-contract-handoff.md §2 (Grady #6).

* feat(billing): shared dollar usage model + two-bar view (drop "credits")

Single source of truth for the /usage and /subscription usage bars across
TUI + CLI. Reads the NAS account-info dollar fields (subscription/top-up/total
remaining, monthly allowance, renewal) and produces a surface-agnostic model:
two full-resolution bars (plan allowance + purchased top-up), a status
classification (free | healthy | low | depleted), and a human renewal date.

- agent/billing_usage.py: UsageModel/UsageBar, usage_model_from_account
  (fail-open), build_usage_model (HERMES_DEV_CREDITS_FIXTURE-aware),
  format_renews (ISO -> "Jul 24, 2026", Windows-safe), $5 low-balance threshold.
- tui_gateway/server.py: _serialize_usage_model/_serialize_usage_bar, a
  usage.bars RPC, and the model embedded into subscription.state so the overlay
  renders the same bars from its single fetch.
- Dollars only, never "credits"; two separate bars (not a crammed
  three-segment one) for legibility at terminal widths.
- tests/agent/test_billing_usage.py: status classification, bar math
  (clamp/over-cap), NaN/Inf rejection, fail-open invariants.

* feat(tui): dollar usage bars on /usage + /subscription, drop tier picker

Render the shared two-bar dollar model in both overlays; strip "credits" and
the in-terminal tier selection per UX feedback.

- overlayPrimitives.tsx: UsageBars (themed plan/top-up bars — gold allowance,
  green top-up) + usageBarsText for the /usage panel. Plan name labels the
  bar; "$X left of $Y · N% used" (disambiguated so the % matches); top-up
  "never expires".
- subscriptionOverlay.tsx: status line dedupes ($X left once; bar carries the
  breakdown), human renewal date, state-matched nudges (free upsell / <$5
  low alert) with box-safe ASCII markers (! / >) instead of the width-unstable
  emoji that broke the border. Tier picker removed — overview shows usage +
  plan, then "Manage on portal" / "Close" (free users get "Start a
  subscription"). No "credits" anywhere.
- session.ts: /usage renders the dollar bars + balance summary, falling back
  to the legacy credits lines only when the model is unavailable; CTA reworded.
- gatewayTypes.ts: UsageModelData/UsageBarData wire types + usage on
  SessionUsageResponse/SubscriptionStateResponse.
- Tests updated to the new contract (no "credits", "left of", dedup, markers).

* feat(cli): mirror dollar usage bars on /usage + /subscription

CLI parity with the TUI billing rework, from the same shared usage model.

- _print_nous_credits_block (/usage) and _subscription_overview render the
  two-bar dollar view (plan name on the bar, "$X left of $Y · N% used",
  top-up "never expires", total spendable) instead of the credits-worded block.
- Dollars only — dropped the tier catalog (no more "$N/mo (… credits)") and
  every user-facing "credits"; team copy says "shared balance".
- Human renewal date via the shared format_renews; status line dedupes the
  "$X left"; free upsell + <$5 low alert with ASCII markers.
- /subscription manage modal no longer dumps the raw manage-subscription URL
  in its detail — the [1] Open / [2] Copy link / [3] Cancel options carry it.
  Title is "Manage your subscription" (no in-terminal plan change). The raw URL
  stays only in the non-interactive / not-admin fallbacks, which have no menu.
- /usage token-usage panel (model, tokens, cost, context) left untouched.

* feat(billing): embed dollar usage model into billing.state for /topup

The /topup overview renders the same two-bar dollar usage (plan + top-up) as
/usage and /subscription. Embed the shared usage model into the billing.state
RPC payload (mirrors subscription.state) so the overlay gets the bars from its
single fetch, and add the `usage` field to BillingStateResponse.

* feat(tui/topup): reorder overview + in-flight reauth with press-Enter resume

Reworks the /topup overlay per the Jun 19 review and the no-preflight decision.

Overview:
- Balance leads in the title ("Top up · balance $X"); the shared two-bar dollar
  usage (plan + top-up) renders below. Dropped the old monthly-cap spend bar.
- "Add funds" is the first action (was "Buy credits"); auto-reload / monthly
  limit / manage-on-portal follow. Dollars only — no "credits" anywhere.
- No "Enable terminal billing" menu item and NO scope preflight: whether the
  terminal can charge is discovered reactively at pay time. (We deliberately do
  not read/refresh the OAuth token to gate UI.)

Step-up (reached only on a charge's insufficient_scope 403):
- New 4-phase flow that keeps the modal mounted: prompt (one-time-setup
  heads-up) → waiting (browser authorize) → granted (explicit "Press Enter to
  resume") → replay the held charge → settle. The press-Enter beat is the
  reassuring "you're back, finish your purchase" moment.
- Renamed user copy "Allow Remote Spending" → "Enable terminal billing"; never
  leaks the raw billing:manage scope (guarded by the render test).
- topup.ts error copy de-crufted to terminal-billing wording, emoji removed.

Tests: step-up prompt copy, the no-raw-scope invariant, and new overview tests
(balance-in-title, Add-funds-first, two-bar usage, no "credits").

* feat(cli/topup): mirror overview reorder + in-flight reauth resume

CLI parity with the TUI /topup rehaul, from the same shared usage model.

- _billing_overview: balance in the title, the two-bar dollar usage (plan name
  on the plan bar, top-up "never expires") in place of the old cap spend bar,
  "Add funds" first, dollars throughout — no "credits", no scope preflight.
- _billing_handle_scope_required: now takes the held amount + idempotency key
  and runs the in-flight flow — "Enable terminal billing" → browser device-flow
  → re-check the org kill-switch → press-Enter to resume → replay the held
  charge (reusing the key so a double-submit collapses to one). Stops leaking
  the raw billing:manage scope.
- Charge-error + buy/auto-reload copy de-crufted to terminal-billing/dollars.
- Tests updated to the new overview + buy copy.

* fix(billing): guard non-JSON 2xx responses in the billing HTTP client

A 2xx response with a non-JSON body — e.g. a reverse-proxy / SPA fallback HTML
page served when a billing route isn't actually mounted on a deployment — hit
json.loads() on the success path of _request() and raised a raw
json.JSONDecodeError. That escaped the typed-BillingError contract, so callers'
`except BillingError` missed it and fell through to a generic fail-open that
rendered as a misleading "not logged in" (observed when /api/billing/subscription
was briefly unshipped on staging: 200 text/html, x-matched-path /[...notFound]).

Now a non-JSON 2xx body raises a typed BillingError(error="endpoint_unavailable")
so surfaces degrade gracefully ("could not load …") instead of crashing or
mislabeling a valid session as logged-out. The 4xx/5xx path already guarded its
.json(); this closes the same hole on the success path.

Test: tests/hermes_cli/test_nous_billing_request.py — non-JSON 2xx → typed
error (not JSONDecodeError, not BillingAuthError), empty body → {}, valid JSON
parses.

* feat(billing/dev): add HERMES_DEV_BILLING_FIXTURE for offline card/scope testing

build_billing_state short-circuits to a fixture when HERMES_DEV_BILLING_FIXTURE
is set (mirrors HERMES_DEV_CREDITS_FIXTURE for the usage model). States:
nocard | card | card-autoreload | notadmin | billing-off | logged-out — so the
card-on-file gate, admin role, and kill-switch paths are exercisable offline
without a live portal. Env-var gated; returns None when unset (no prod leak).

Adds 8 behavior tests asserting the card/admin/billing-on contract per state.

* refactor(billing): fold /credits into /topup

/credits is redundant now that /topup shows the dollar balance + portal handoff.
Make 'credits' (and 'billing') aliases of /topup so typing /credits still works,
resolving to topup everywhere (CLI, gateway, Slack, TUI, autocomplete, help).

Remove the standalone /credits surface across 6 places:
- CLI _show_credits handler + dispatch
- gateway _handle_credits_command -> renamed _handle_topup_command, copy softened
  to 'Manage billing on the portal' (the messaging billing surface; /topup is now
  gateway-available so messaging keeps billing — credits was the only one before)
- TUI commands/credits.ts + creditsCommand.test.ts (deleted), registry entry
- tui_gateway credits.view RPC + the CreditsViewResponse type
- Slack _SLACK_VIA_HERMES_ONLY: credits -> topup

Sweep user-facing /credits -> /topup (usage-block hint, depletion notice) and
stale doc-comments. OpenRouter's /credits endpoint URL left untouched. Tests
updated (test_credits_folds_into_topup) or pruned for the removed symbols.

* fix(billing): card-on-file heads-up, no-card portal gate, /usage bar ordering, modal glyph

In-terminal charge (POST /charge against the org's server-held card, no card ref
leaves the client):
- card present: confirm screen shows 'Your card saved on the portal will be
  charged' + a 'Manage on portal' escape option (CLI); heads-up line (TUI)
- no card on file: /topup overview + buy flow detect it and route to the portal
  to add a card, instead of offering a charge that 403s no_payment_method

/usage bar ordering: route the dollar block through _cprint consistently. The
Plan: line (_cprint) and the bar (raw print) flushed to different buffers under
patch_stdout and interleaved nondeterministically; now Plan: -> bar -> status/CTA
is stable across all states.

Modal glyph: strip the leading emoji from bordered _prompt_text_input_modal
titles — it measures 1 char but renders 2 columns, shifting the box's right
border (the stray '|'). Includes the f-string 'Pay $X?' title.

Small /credits -> /topup string bits in cli.py ride along with the surrounding
charge edits (the fold lives in the sibling refactor commit).

* refactor(billing): apply safe simplify-pass fixes

Three low-risk cleanups from a parallel simplify review (reuse/quality/efficiency):
- dev fixture portal URL: reuse the prod host (was drifted to staging-* — a real
  mismatch vs subscription_view's _DEV_FIXTURE_PORTAL)
- TUI billingOverlay choose(): collapse two byte-identical branches (needsCard +
  the not-full else both = portal-or-close at index 0) into one tail; the only
  divergent path (full && !needsCard → buy/auto/limit) stays explicit
- /topup overview comment: correct the stale 'buy_flow detects no_payment_method'
  note (the overview's no-card gate fires first, so reaching Add funds implies a
  card on file)

Skipped (judgment): the orphaned CreditsView.depleted field (harmless, on a live
dataclass), the defensive card gates in _billing_buy_flow/_confirm_and_charge
(cheap correct defense on the money path), and folding the no-card handoff into a
shared helper (touches 4 money-path sites for tidiness — not worth the risk here).

* fix(billing): reactive charge gating — drop card preflight, react to 403 (scope→reauth, no-card→portal)

* refactor(billing): drop the /credits alias entirely

The /credits fold made it an alias of /topup; now remove that too. Typing
/credits is an unknown command, not a silent redirect — billing lives only on
/topup (with /billing kept as the old command's back-compat name). Dropped the
alias from the registry CommandDef and the TUI topup.ts; updated the test to
assert /credits resolves to nothing (no command, no alias).

* docs(billing): fix stale comment in _billing_overview — describe reactive no-card path

The comment still described the removed overview-level card gate ('no-card case
handled above'). Corrected to: the buy flow reacts to the server's
no_payment_method 403 and hands off to the portal at charge time (no preflight).

* refactor(billing): simplify-pass — share usage-payload helper, drop dead bar wire fields + redundant admin gate

* refactor(billing): drop the /billing alias too — /topup is the only billing command

Following /credits removal, retire the old /billing name as well. /topup now has
NO aliases — both /credits and /billing are unknown commands. Dropped the alias
from the registry CommandDef and TUI topup.ts; fixed the one live user-facing
straggler (the not-logged-in message said 'then /billing' → /topup) and the
_show_billing docstring/default-arg references. Test asserts /topup carries no
aliases and neither old name resolves.

* fix(billing): code-review fixes — money-path + parity bugs

Money path (TUI):
- auto-reload "Turn off" now echoes current threshold/top_up_amount so the
  PATCH succeeds (was sending {enabled:false} → invalid_request → stayed ON)
- charge poll honors the 5-min cap on the 429/503 throttle branch too (was
  rescheduling forever); cap folded into one timedOut() helper
- step-up resume reacts to the replay outcome instead of unconditionally
  closing on a reassuring line with no charge made
- synchronous submit guard on Confirm so two key events can't double-charge

Gateway:
- billing.step_up routes typed errors through _serialize_billing_error (was a
  raw {error:'error'} dict → generic copy for session_revoked)
- billing.state / subscription.state / usage.bars / session.usage moved to
  _LONG_HANDLERS (blocking portal HTTP no longer stalls the main stdin loop)

CLI:
- _billing_render_charge_error handles insufficient_scope without leaking the
  raw billing:manage scope name on a post-grant replay re-raise

Python model:
- subscription_view tier parse None-coalesces tierOrder/dollarsPerMonth so a
  free tier's 0 survives ($0, not "—"; correct sort order)

TUI parity/robustness:
- /usage shows formatted renews_display, not raw ISO renews_at
- subscription overview guards a null pending_downgrade_at (was "on null.")
- subscription overview surfaces a message instead of silently closing when
  portal_url is missing
- buildManageUrl wraps new URL() so a malformed portal_url can't throw out of
  the Ink key handler

* fix(billing): cross-surface bar direction, formatted cancel/downgrade dates, Slack alias gating

- CLI plan bar now fills by REMAINING (fuel-gauge), matching the shared model's
  fill_fraction, the top-up bar, and the TUI — same account renders identically
  on both surfaces (#8)
- subscription serializer emits cancellation_effective_display /
  pending_downgrade_display (format_renews); TUI shows 'Jul 1, 2026' not raw ISO (#14b)
- _SLACK_VIA_HERMES_ONLY now includes the 'billing' alias so it follows its
  canonical /topup via /hermes instead of leaking a native Slack slot (#9)

* fix(billing): thread idempotency key through the TUI step-up replay (#2)

Mint a stable idempotency key when the purchase amount is chosen; it rides
pendingCharge into both the Confirm charge and the post-grant step-up replay,
so a retried charge dedups server-side (the gateway already echoes the key).
A fresh amount selection gets a fresh key. Combined with the sync submit guard,
a double-submit now collapses to one charge.

* refactor(billing): remove dead /subscription tier-picker scaffolding (#18)

The in-terminal plan picker was cut (deep-link only), leaving a whole unreached
state machine. Removed end-to-end:
- TUI: ConfirmScreen, HandoffScreen, the 'confirm'/'handoff' screen types,
  pendingTargetTierId, and the now-dead onPatch threading (collapsed the dispatch
  to a single overview screen + folded the duplicate Box wrapper)
- gateway: the tiers serialization + SubscriptionTierOption wire type
- model: SubscriptionTier, _parse_tier, _coalesce, _dev_tiers and the tiers field
  (never displayed on either surface, so this supersedes the tier-parse fix)
- tests: dropped the confirm/handoff/tier-passthrough tests; slimmed the overview
  render tests

Net: a large dead-code cull (no behavior change — the picker never ran).

* test(billing): parametrize usage-model tests; drop dead is_low/is_free props

Collapse the fail-open + status-classification cases into parametrized tables
(same coverage, ~80 fewer lines) and remove the now-unused UsageModel.is_low /
is_free properties (only a test pinned them).

* fix(billing): revert dead 'billing' Slack-via-hermes entry — the alias was dropped

#9 was based on a stale review diff: /billing is no longer an alias of /topup
(dropped earlier), so routing it via /hermes filtered a name that doesn't exist.

* test(billing): cull redundant TUI billing tests (parametrize, merge dupes)

usageCommand: collapse 3 CTA tests into one + a panel helper.
billingStepUp: merge the two step-up render asserts.
topupCommand: parametrize requestRemoteSpending + the revoked-actor pair, drop
the redundant happy-path-submitted test. Money-path + error-mapping coverage
preserved.

* refactor(billing): extract _usage_bar_lines — one source of truth for the CLI bars

The plan + top-up bar format was copy-pasted across _print_nous_credits_block,
_subscription_overview, and _billing_overview. Extract a helper returning the
ready-to-print lines; each caller keeps its own print fn (the _cprint-ordering
constraint stays) and resolves its plan-name label. Centralizes the format so
the three surfaces can't drift.

* feat(billing): NAS V3 subscription-change HTTP client wrappers

Add the four write-side wrappers for the V3 subscription contract to nous_billing,
each a thin _request() call (reusing auth, JSON, 401-retry, typed errors):
- post_subscription_preview      → POST  /subscription/preview      (chargeless quote)
- put_subscription_pending_change→ PUT   /subscription/pending-change (downgrade/cancel)
- delete_subscription_pending_change → DELETE .../pending-change      (resume/undo)
- post_subscription_upgrade      → POST  /subscription/upgrade        (the money route)

pending-change takes a discriminated body (tier_change | cancellation); upgrade
requires an Idempotency-Key (mandatory, validated client-side before any I/O).
Tests assert the exact method/path/body/header each wrapper puts on the wire.

* feat(billing): subscription tier catalog + change-preview models

Reinstate the catalog the in-terminal picker needs (was culled when /subscription
was deep-link-only): SubscriptionTier + SubscriptionState.tiers + _parse_tier, with
_coalesce so the free tier's 0 tierOrder/price survives a falsy-or. Parse the
catalog from GET /subscription's tiers and seed _dev_tiers into every fixture.

Add SubscriptionChangePreview + subscription_change_preview_from_payload for the
POST /preview quote (effect/amountDueNowCents/effectiveAt/reason + tier delta); a
malformed/missing effect fails safe to 'blocked' so a bad quote never reads as a
charge. Module docstring updated: the overlay is no longer deep-link-only.

* feat(billing): gateway RPCs for the V3 subscription change flow

Add subscription.preview / .change / .resume / .upgrade RPCs, each wrapping its
nous_billing call and reusing _serialize_billing_error for the typed envelope
(so a 403 still drives the device step-up). upgrade mints + echoes the
idempotency key and surfaces status + recovery_url so the TUI can route an
SCA/decline to the portal. Re-add the tier catalog to _serialize_subscription_state
(price pre-formatted) for the picker. All four are pool-routed (_LONG_HANDLERS) —
preview + upgrade hit Stripe and must not stall the main stdin loop.

* feat(billing): in-terminal subscription change flow (TUI)

/subscription is no longer deep-link-only: it drives the change in-terminal
against the V3 contract via the new gateway RPCs. The overlay is a state machine
overview → picker → confirm → result:
- picker lists the tier catalog with upgrade/downgrade hints (current + free
  excluded; free=cancel, on the overview);
- confirm shows the previewed effect — pay $X now (upgrade) / scheduled at date
  (downgrade) / cancel at period end / blocked-with-reason — then applies it;
- an upgrade's SCA/decline routes to the portal via the result screen's recovery
  link; resume/cancel/downgrade are chargeless.

Starting a NEW subscription still deep-links (needs a fresh card). insufficient_scope
points to /topup (the step-up stays there, not duplicated here). Adds the wire
types (tiers + preview/upgrade responses), widens the overlay ctx + screen state,
and threads onPatch. Render tests cover every screen.

* feat(billing): in-terminal step-up + clearer scheduled-change UX (TUI)

Two improvements to the /subscription overlay:

Step-up re-auth in place. When a mutation (preview/change/upgrade/resume) returns
insufficient_scope, route to a new 'stepup' screen that grants terminal billing
via billing.step_up and AUTO-REPLAYS the held action on grant — no bounce to
/topup. Scope routing is centralized in previewAndRoute/applyPendingAndRoute/
resumeAndRoute (shared by the picker, confirm, overview + the step-up replay). The
browser opens via the shared global verification handler; copy never leaks the raw
billing:manage scope.

Make a scheduled change unmissable. A downgrade/cancel was one buried warn line
that read as 'nothing happened'. Now the overview leads with a banner
( Scheduled change · Ultra ──▶ Plus · <date> · you keep Ultra until then), the
status line echoes the transition (Plan: Ultra → Plus), 'Keep <tier> (undo)' is
promoted to the first olive action, the result screen says 'your plan doesn't
change today', and confirm gets a charged-now / scheduled chip.

* feat(billing): full in-terminal subscription change flow in the classic CLI

Bring the CLI to parity with the TUI overlay — /subscription is no longer
deep-link-only. A paid admin/owner gets picker → preview → confirm → apply,
mirroring the /topup buy flow's modal idioms:
- _subscription_change_menu (change / undo-or-cancel / manage-on-portal),
- _subscription_pick_tier (catalog with upgrade/downgrade hints),
- _subscription_preview_and_confirm (POST /preview → effect-aware confirm),
- _subscription_apply (schedule / cancel / resume chargeless; upgrade charges
  the sub's card, SCA/decline → portal),
- _subscription_handle_scope_required (insufficient_scope → step_up_nous_billing_scope
  inline, then replays the held preview/mutation — reusing the upgrade idempotency key).

Also the scheduled-change UX fix: the overview leads with a prominent banner
( Scheduled change · Super ──▶ Plus · <date> · you keep Super until then) and the
status line echoes the transition, matching the TUI. Members / non-interactive /
free still deep-link. Tests drive every branch via a mocked modal + nous_billing.

* fix(billing): close TUI subscription money-path holes (ultracode review)

- Un-consented charge (P1): the step-up now HOLDS at a 'granted' phase requiring
  an explicit Continue, and an abortedRef gates the grant's late .then — a cancel
  during the browser flow can no longer replay the held upgrade + charge.
- Missing idempotency key (P2): mint it when building an upgrade 'pending' so it
  rides into confirm AND the step-up replay (was always undefined → gateway minted
  a fresh key per call, defeating dedup).
- Navigate-away re-charge (P2): confirm 'back' is guarded by submittingRef while an
  apply is in flight.
- Ambiguous charge (P2): a transport-null upgrade is reported as 'may or may not
  have charged — re-check', never a flat failure that invites a blind retry.
- Typed step-up denial (P2): requestRemoteSpending returns {granted,error,message};
  the screen maps session_revoked / remote_spending_revoked / rate_limited to the
  right recovery instead of always 'an admin must allow it'.

* fix(billing): close CLI subscription money-path holes (ultracode review)

- Bounded step-up (P2): bust the 30s token cache after a grant (it held the
  pre-grant unscoped token; _request only busts on 401, not 403) and replay ONCE
  with allow_stepup=False so a still-denied scope can't re-prompt/re-open in a loop.
- Stray-keystroke charge (P3→near-P2): the upgrade confirm defaults to 'Go back',
  not 'Pay ' — a bare Enter can't move money.
- Fail-open on unknown effect (P3→near-P2): an unrecognized preview effect now
  fails SAFE (portal hand-off) instead of scheduling a real PUT.
- 'cancel' word collision (P3): the Close row uses value 'close' so typing 'cancel'
  can't hit it and falsely report 'Cancelled'.
- blocked effect re-offers the portal; undo is promoted to the first row when a
  change is pending (TUI parity).

* fix(billing): guard the step-up resume against double-fire (2nd ultracode pass, BUG A)

The P1 fix split the auto-replay into a user-triggered resume() on the granted
screen, where the default row is the charging action — but resume() had no
re-entrancy guard, so a double-Enter fired two replays (the upgrade dedups on the
shared key, but schedule/cancel/resume replays carry none → duplicate PUT/DELETEs).
Mirror billingOverlay.resume(): flip to a 'resuming' phase + a resumingRef so it
fires at most once, and block 'back' once resuming (no re-mount → no second submit).

* fix(billing): CLI charge-route ambiguous-charge caveat (2nd ultracode pass, BUG B)

The TUI hardened upgradeResult(null) but the CLI charging route did not: a
transport/timeout/500 (or unknown 2xx status) on post_subscription_upgrade — after
NAS may have already prorated + charged — printed a flat failure, and a manual
re-run mints a FRESH idempotency key the server can't dedup → a real second charge.
Now the charge route reports 'your card may or may not have been charged — re-run
/subscription to check before trying again' and steers away from a blind retry
(the CLI can't persist the key across a command re-run). Also thread allow_stepup
through the preview→apply replay (BUG C.1) and route the requires_action/
payment_failed portal lines through _cprint for deterministic ordering.

* fix(billing): cap the TUI step-up replay to avoid a resume-deadlock (final pass, R1)

The round-2 resume guard ('resuming' phase + resumingRef) could deadlock: on a
REPEAT insufficient_scope during the post-grant replay, the route helpers did
onPatch({screen:'stepup'}) — a no-op since we're already mounted on stepup (no key
→ no remount) — leaving phase='resuming'/resumingRef=true frozen on 'Applying your
change…'. Thread allowStepUp through previewAndRoute/applyPendingAndRoute/
resumeAndRoute; the resume() replay passes false, so a repeat scope denial surfaces
a 'still isn't enabled' result instead (mirrors the CLI's allow_stepup=False cap).
Also: applyPendingAndRoute(pending=null) now routes to overview, not a stranded
Promise.resolve().

* fix(billing): narrow the CLI ambiguous-charge catch to indeterminate outcomes (final pass, R2)

The round-2 fix caught EVERY non-scope BillingError as 'may or may not have been
charged' — but typed pre-charge rejections (BillingRateLimited 429, BillingSessionRevoked
401, BillingRemoteSpendingRevoked 403, role_required/no_payment_method 4xx) never
reached Stripe, so the ambiguity copy was wrong and dropped their real recovery hints.
Now route those to _subscription_render_error, and reserve the ambiguous copy for
genuinely indeterminate outcomes (network_error / endpoint_unavailable / status None /
5xx). Tests: rate-limit stays deterministic; a real transport failure stays ambiguous.

* feat(billing): card visibility + guided add-card path in /topup and /subscription

Consume the NAS card-resolver contract (card.resolvedVia + chargeability) across
both surfaces, degrading cleanly on today's NAS (fields absent → prior behavior):

- WHICH card: the payment lines render provenance — 'Visa ····4242 — the card on
  your subscription' (resolvedVia → label; unknown rung/older NAS → masked card +
  the old generic line). Link payment methods render the brand alone (last4 is
  empty — never 'Link ····').
- Presence at a glance: the /topup overview now shows 'Card: …' or 'No saved
  card on file' for the full-menu case, plus a warning when the resolver marks
  the card needs_repair (failing auto-reloads) on overview/buy/confirm.
- Add-card path: with no card on file, 'Add funds' becomes a guided screen —
  open the portal billing page, then 'I've added it — check again' re-fetches
  billing state and continues straight into the purchase (also recovers a
  transient display miss). Cards are never entered in-terminal.
- /subscription upgrade confirm names the exact card ('Visa ····4242 — the card
  on your subscription — will be charged'), best-effort via billing.state and
  only when the resolution rung matches what a subscription charge actually
  uses (subPin/customerDefault, mirroring Stripe's precedence); otherwise the
  generic line stands. Fail-soft: any lookup error keeps the generic line.
- Gateway serializes display/resolved_via/needs_repair; TUI ctx gains
  refreshState (topup) + fetchCard (subscription); new offline fixtures
  card-sub / card-repair.

Tests: TUI ctx mocks extended; CLI suites cover provenance + repair-warning
render, the Link guard, the add-card path (continue-after-recheck + abandon),
the sub-confirm card line, and keep the confirm-time lookup offline in tests.

* fix(billing): consume server canChangePlan, preserve distinct refusal codes, drop dead chargeability

- Parse canChangePlan verbatim from NAS payloads into BillingState and
  SubscriptionState; fall back to the legacy OWNER/ADMIN check only when the
  server omits the field (FINANCE_ADMIN stops being locked out where NAS
  authorizes it). Role model updated to the 5-role enum.
- Add the autoReload.card union (canonical | distinct | none) end-to-end:
  parse + gateway serialization, distinct carries payment_method_id/brand/last4
  with nullable display fields.
- stripe_unavailable (503, transient) and upgrade_cap_exceeded (429, daily cap)
  now survive to the wire as their own codes instead of collapsing into
  rate_limited; new exception types subclass BillingRateLimited so existing
  backoff call sites keep working.
- Remove card.chargeability / needs_repair parsing, serialization, fixtures and
  the cli warning blocks: NAS #670 removed the field, so the repair path was
  permanently dead. The future card-health signal belongs to the NAS W1/W3 work.
- Tests: five-role fixtures, canChangePlan override/fallback, all three
  auto-reload card variants, 429-vs-503 code preservation end-to-end.

* feat(tui): render the full NAS billing refusal surface

- billingOverlay: divergence notice when auto-refill charges a distinct card
  (portal deep-link to reconcile); needs_repair warnings removed with the field.
- topup: explicit copy for consent_required, org_access_denied,
  upgrade_cap_exceeded, auto_top_up_disabled_failures and stripe_unavailable
  (honors retry_after); processing_error is an explicit charge-failure case;
  transport loss during charge polling now reads as an unconfirmed outcome
  (check balance before retrying), matching the revocation path.
- subscriptionOverlay: branch on upgrade reason, not status, so an SCA-needing
  upgrade routes to portal verification even while NAS pre-#711 labels it
  payment_failed; after an upgrade, poll subscription state until the tier
  flips (bounded), rendering applying/still-applying rather than assuming
  immediacy.
- Capability-neutral refusal copy (owner, admin, or finance admin) replaces
  the stale org admin/owner wording.
- gatewayTypes: BillingAutoReload.card union added, needs_repair removed.

* docs(billing): client-side billing state and refusal lifecycle table

Enumerates, from the code, every billing.state shape and typed refusal the
gateway serves and the exact TUI copy + recovery each renders. Acceptance from
the billing-integration handoff: no NAS billing state or typed refusal falls
through to a generic toast; unknown codes still degrade to the default branch
that surfaces the server message.
2026-07-18 14:30:24 +05:30
..
__init__.py
conftest.py test(hermes_cli): harden concurrent-gate fixture against partial-import race (#42626) 2026-06-08 22:54:25 -07:00
conftest_dashboard_auth.py fix(dashboard-auth): use fixed-length sig suffix in stub token framing 2026-05-27 02:12:27 -07:00
test_25106_global_switch_persists_base_url_api_mode.py test(cli): cover picker-path persist_global=True in #25106 regression tests 2026-07-17 15:47:08 -07:00
test_active_sessions.py test: make active session cross-process race deterministic (#54248) 2026-06-28 05:49:21 -07:00
test_anthropic_model_flow_stale_oauth.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_anthropic_oauth_flow.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
test_anthropic_oauth_routes_to_messages_api.py test(runtime): pin Anthropic OAuth → /v1/messages routing across runtime branches 2026-07-01 02:18:56 -07:00
test_anthropic_picker_curated.py fix(models): keep curated Anthropic aliases in /model picker (#43103) 2026-06-09 14:45:19 -07:00
test_anthropic_provider_persistence.py
test_api_key_providers.py fix(ci): make tests, workflows, and attribution reliable under load (#66373) 2026-07-17 20:55:24 +00:00
test_apply_model_switch_result_context.py
test_apply_profile_override.py fix(s6): reserved default gateway must not follow sticky active_profile (#46483) 2026-06-15 05:36:20 +00:00
test_arcee_provider.py remove Vercel AI Gateway and Vercel Sandbox (#33067) 2026-05-27 00:43:32 -07:00
test_argparse_flag_propagation.py fix(cli): preserve -t/-m/--provider/--tui/--dev before chat subcommand 2026-07-11 03:51:43 -07:00
test_at_context_completion_filter.py
test_atomic_json_write.py fix(utils): guard os.fchmod for Windows in atomic_json_write 2026-06-01 09:57:10 -07:00
test_atomic_yaml_write.py fix(config): write config.yaml as UTF-8 to stop emoji/personality corruption (#51676) 2026-06-23 23:28:21 -07:00
test_auth_codex_provider.py fix(auth): retry Codex device-code login on 429 with clear rate-limit message (#47860) 2026-06-17 05:48:35 -07:00
test_auth_codex_self_heal.py fix(auth): self-heal missing Codex access tokens 2026-06-13 05:15:26 -07:00
test_auth_commands.py fix(auth): make xAI OAuth pools multi-account resilient 2026-07-17 11:37:25 +05:30
test_auth_loopback_ssh_hint.py fix(auth): remove stale loopback_pkce reference in xAI quarantine removal list 2026-07-02 13:17:41 -07:00
test_auth_nous_provider.py test(auth): pin runtime routing persistence on failure 2026-07-10 17:50:43 +05:30
test_auth_profile_fallback.py fix(auth): key reentrancy by auth store path 2026-07-16 06:14:56 -07:00
test_auth_provider_gate.py fix(auth): detect configured providers absent from registry (#66017) 2026-07-16 22:56:45 -04:00
test_auth_qwen_provider.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_auth_ssl_macos.py
test_auth_toctou_file_modes.py
test_auth_usable_secret.py security: harden API server key placeholder handling (#30738) 2026-05-24 04:25:32 -07:00
test_auth_xai_oauth_provider.py feat(auth): make xAI Grok OAuth device-code-only, drop loopback login 2026-07-02 13:17:41 -07:00
test_authenticated_providers_exhausted_pool.py fix(model-picker): show exhausted-pool providers in interactive /model picker (#66584) 2026-07-17 19:16:48 -04:00
test_aux_config.py fix: honor disabled title generation config 2026-07-16 22:24:45 -07:00
test_azure_detect.py fix(security): secure Azure catalog probes 2026-07-11 12:28:55 +05:30
test_azure_foundry_entra.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_backup.py fix(backup): fail closed on sqlite snapshot errors 2026-07-17 04:55:19 -07:00
test_banner.py fix(model): show MoA preset picker on selection and label MoA in the banner 2026-06-27 11:45:07 -07:00
test_banner_git_state.py fix(docker): bake build-time git SHA into the image 2026-05-28 15:14:05 +10:00
test_banner_pip_update.py refactor: DRY cleanup from code review 2026-05-15 14:45:43 -07:00
test_banner_skills.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_banner_skills_width.py feat(banner): size skills display to terminal width instead of fixed 8/47 2026-07-06 11:17:41 -07:00
test_bedrock_model_picker.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_bedrock_region_scoped_picker.py fix(bedrock): region-scoped model picker + geo-aware recommendations (#28156) 2026-07-15 09:59:38 -07:00
test_billing_cli.py feat(tui+cli): change your Nous plan from the terminal (/subscription, /topup, terminal-billing UX) (#51639) 2026-07-18 14:30:24 +05:30
test_billing_portal_url.py feat(billing): /billing terminal billing — interactive TUI + CLI client (#45449) 2026-06-19 01:53:32 +05:30
test_billing_scope_stepup.py feat(billing): /billing terminal billing — interactive TUI + CLI client (#45449) 2026-06-19 01:53:32 +05:30
test_build_info.py fix(docker): bake build-time git SHA into the image 2026-05-28 15:14:05 +10:00
test_bundles.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_chat_skills_flag.py
test_claw.py revert(windows): roll back terminal-popup PRs #53791 #53810 #53829 (#53853) 2026-06-27 15:59:00 -07:00
test_clear_stale_base_url.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_cli_active_session_limit.py feat(sessions): add optional max session cap 2026-06-08 15:12:12 -07:00
test_cli_output.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
test_cmd_update.py fix(desktop): force npm --include=dev so self-update rebuild can't be broken by NODE_ENV=production (#38416) 2026-07-16 15:45:07 +00:00
test_cmd_update_docker.py fix(ci): remove pytest-timeout, use per-file timeout only 2026-06-12 13:42:42 -04:00
test_coalesce_session_args.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_codex_cli_model_picker.py test(codex-picker): raise max_models so count invariant survives catalog growth 2026-07-10 00:47:51 +05:30
test_codex_models.py fix(model): require confirmation for expensive model selections 2026-06-10 00:24:06 -07:00
test_codex_runtime_plugin_migration.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_codex_runtime_switch.py fix(codex-runtime): re-running /codex-runtime codex_app_server when already enabled now triggers migration 2026-07-01 23:51:54 +05:30
test_commands.py feat(reasoning): add max and ultra effort levels (#62650) 2026-07-12 00:26:49 -07:00
test_completion.py test(cli): strengthen zsh completion regression coverage 2026-05-13 09:34:15 -07:00
test_config.py test(config): regression for merge_existing partial saves (#62723) 2026-07-15 04:26:20 -07:00
test_config_drift.py
test_config_env_expansion.py fix(cli): ignore empty config sections 2026-07-09 18:23:18 -07:00
test_config_env_refs.py
test_config_validation.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_configured_builtin_models.py fix(model): merge configured models into picker rows (#63055) 2026-07-12 03:05:12 -07:00
test_console_engine.py refactor(console): remove hosted-context command blocking from Hermes Console (#66144) 2026-07-17 04:33:34 -07:00
test_container_aware_cli.py
test_container_boot.py fix(container): keep named multiplex gateway slots down (#65368) 2026-07-16 14:30:05 +10:00
test_context_switch_guard.py fix(cli): log instead of swallow preflight-warning errors; consistent TUI warning field 2026-06-21 16:31:56 +05:30
test_copilot_auth.py fix(copilot): set x-initiator per turn so user prompts bill as premium requests (salvage #4097) (#58544) 2026-07-05 00:44:15 -07:00
test_copilot_catalog_oauth_fallback.py
test_copilot_context.py
test_copilot_in_model_list.py test: remove low-value model-catalog mirror tests 2026-05-29 23:45:05 -07:00
test_copilot_token_exchange.py fix(copilot): switch to VS Code client ID and derive enterprise base URL 2026-06-30 03:27:41 -07:00
test_cron.py test(cron): cover cron_list/status/tick/create CLI helpers 2026-07-06 02:21:32 -07:00
test_cron_dashboard_off_loop.py test(dashboard): pin cron fire + blueprint handlers off the event loop 2026-07-09 14:30:16 +05:30
test_cron_fire_dashboard.py fix(cron): complete plugins.cron_providers rename in 2 missed test files 2026-06-23 23:39:22 -07:00
test_cron_parser_builder.py fix(cron): harden execution attempt ledger 2026-07-17 04:58:35 -07:00
test_ctrlg_editor_submit.py feat(cli): Ctrl+G submits the edited draft on save (TUI parity) (#50560) 2026-06-21 22:43:55 -07:00
test_curator_archive_prune.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_curator_recent_run_notice.py feat(curator): show rename map in user-visible summary (#22910) 2026-05-09 18:43:40 -07:00
test_curator_run.py
test_curator_status.py
test_curator_usage.py feat(curator): add hermes curator usage — all-skills usage view 2026-07-06 12:26:21 -07:00
test_curses_arrow_keys.py test: skip curses tests on Windows where _curses is unavailable (#40611) 2026-06-07 18:21:03 -07:00
test_curses_color_compat.py test: skip curses tests on Windows where _curses is unavailable (#40611) 2026-06-07 18:21:03 -07:00
test_curses_ui_fuzzy_rank.py feat(cli): ranked fuzzy search in the curses model picker 2026-06-01 16:58:58 -07:00
test_curses_ui_search.py feat(cli): add fuzzy search helpers for curses pickers 2026-06-01 16:58:58 -07:00
test_custom_provider_context_length.py
test_custom_provider_extra_headers.py test(models): patch the secured request seam 2026-07-11 12:28:55 +05:30
test_custom_provider_identity.py Fix custom provider identity loss in session persistence 2026-06-13 05:51:05 -07:00
test_custom_provider_model_switch.py fix(auth): prune stale custom model credentials 2026-07-05 00:55:51 -07:00
test_custom_provider_tls.py fix(agent): honor custom CA certs on aux client + harden TLS resolution 2026-07-02 04:51:56 +05:30
test_dashboard_admin_endpoints.py fix(dashboard): drop _HERMES_GATEWAY when spawning hermes actions (#52482) 2026-07-16 19:52:53 -04:00
test_dashboard_auth_401_reauth.py fix(auth): preserve provider fallback during refresh 2026-07-14 07:02:05 -07:00
test_dashboard_auth_audit.py feat(dashboard-auth): json-lines audit log at $HERMES_HOME/logs/dashboard-auth.log 2026-05-27 02:12:27 -07:00
test_dashboard_auth_cookies.py fix(auth): preserve provider fallback during refresh 2026-07-14 07:02:05 -07:00
test_dashboard_auth_gate.py fix(ci): rip out some xdist legacy stuff... how did these ever work?? 2026-06-26 19:15:18 -07:00
test_dashboard_auth_middleware.py feat(dashboard): auto-initiate portal SSO redirect on unauthenticated load 2026-06-29 04:25:18 -07:00
test_dashboard_auth_password_login.py fix(dashboard): support mobile OAuth login 2026-07-09 12:21:16 +05:30
test_dashboard_auth_plugin_hook.py feat(plugins): add register_dashboard_auth_provider hook on PluginContext 2026-05-27 02:12:27 -07:00
test_dashboard_auth_prefix.py fix(dashboard): accept HA ingress prefix paths 2026-07-06 03:18:02 -07:00
test_dashboard_auth_provider_base.py test(dashboard-auth): cover registry register/get/list/clear semantics 2026-05-27 02:12:27 -07:00
test_dashboard_auth_status_endpoint.py fix(ci): rip out some xdist legacy stuff... how did these ever work?? 2026-06-26 19:15:18 -07:00
test_dashboard_auth_stub_provider.py test(dashboard-auth): stub auth provider for E2E gate testing 2026-05-27 02:12:27 -07:00
test_dashboard_auth_ws_auth.py Add dashboard Hermes console websocket 2026-07-03 20:18:00 +05:30
test_dashboard_auth_ws_tickets.py test(dashboard): direct unit coverage for internal WS credential + docstring fix 2026-06-02 23:43:27 -07:00
test_dashboard_basic_auth_plugin_enable.py fix(dashboard): unblock basic auth plugin when setting password interactively (#54489) (#63786) 2026-07-16 19:49:39 -04:00
test_dashboard_browser_safe_imports.py
test_dashboard_lifecycle_flags.py feat(dashboard): always enable embedded chat; remove dashboard --tui flag 2026-06-04 03:03:35 -07:00
test_dashboard_oauth_endpoints_server_gate.py test(dashboard): assert server still gates OAuth endpoints without cookie 2026-07-09 12:21:16 +05:30
test_dashboard_profiles_nav_label.py fix(dashboard): UI polish — modals, layout, consistency, test fixes 2026-05-12 13:59:22 -04:00
test_dashboard_register.py fix(cli): persist custom --portal-url to .env on dashboard register (#42435) 2026-06-09 13:56:33 +10:00
test_dashboard_token_auth.py fix(dashboard-auth): exclude non-interactive providers from interactive login surfaces (#53239) 2026-06-27 10:08:13 +10:00
test_dashboard_tui_backcompat.py fix(ci): map @TheGardenGallery email + drop unused pytest import 2026-06-06 12:43:28 -05:00
test_dashboard_unified_launch.py fix(dashboard): pin machine-dashboard reroute to the machine root, not $HOME/.hermes (#46487) 2026-06-15 15:33:15 +10:00
test_dashboard_web_dist_validation.py fix: write expanded HERMES_WEB_DIST back for web_server's raw read 2026-07-09 14:48:50 +05:30
test_debug.py fix(security): require explicit consent before uploading debug logs 2026-07-01 00:38:17 -07:00
test_default_interface_resolution.py fix(kanban): no-TTY gate in _wants_tui_early — the actual worker-crash fix 2026-07-09 16:13:59 -05:00
test_dep_ensure.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_deprecated_cwd_warning.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_destructive_slash_confirm_gate.py
test_detect_api_mode_for_url.py test(provider): pin api.anthropic.com → anthropic_messages URL detection 2026-07-01 02:18:56 -07:00
test_determine_api_mode_hostname.py
test_diagnostics_upload.py feat(debug): drop dead confirm step from --nous upload (stateless NAS) 2026-06-30 17:29:23 -07:00
test_dingtalk_auth.py
test_discord_skill_clamp_warning.py
test_doctor.py test: deflake CI and dev-machine flaky tests in bulk (11 tests, 10 files) (#61816) 2026-07-09 20:03:11 -07:00
test_doctor_command_install.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_doctor_dedicated_provider_skip.py fix(doctor): skip pluggable provider profiles when a dedicated check exists (#22346) 2026-05-09 13:36:33 -07:00
test_dump_env_visibility.py fix(dump): flag API keys visible only to the shell, not the managed backend 2026-07-02 19:52:18 -05:00
test_dump_git_commit.py fix(dump): show commit date instead of release date in hermes debug (#48104) 2026-06-17 16:53:42 -07:00
test_dump_terminal_backend.py test(dump): cover terminal backend override reporting 2026-06-15 12:31:23 -07:00
test_ensure_hermes_home_memo.py perf: fast model picker + dialogs — config-load hot path, model.options off the reader thread, off-screen turns skip rendering 2026-07-17 15:01:54 -04:00
test_ensure_hermes_home_uid_34107.py fix(config): chown ensure_hermes_home dirs to HERMES_UID/GID in Docker (#34107) (#34268) 2026-06-01 13:27:30 +10:00
test_ensure_utf8_locale.py fix(cli): repair non-UTF-8 stdout/stderr on all platforms, not just Windows (#43439) 2026-06-10 02:21:00 -07:00
test_env_custom_keys.py feat(dashboard): list & add arbitrary custom .env keys on the Keys page 2026-06-28 22:53:56 -07:00
test_env_export_prefix.py test: cover export-prefix stripping in .env parsers (PR #6659) 2026-06-28 18:53:00 -07:00
test_env_load_cache.py perf(tools): cache get_nous_auth_status() and load_env() to fix slow hermes tools menus (#25341) 2026-05-13 18:40:14 -07:00
test_env_loader.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_env_sanitize_on_load.py
test_fallback_cmd.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_fallback_config.py fix(auth): honor per-entry key_env when resolving fallback providers 2026-07-16 07:19:36 -07:00
test_fireworks_provider.py fix(providers): align Fireworks integration with project policy 2026-07-11 05:43:35 -07:00
test_gateway.py fix(gateway): bypass legacy-unit prompt in non-TTY systemd install 2026-06-28 04:09:54 -07:00
test_gateway_external_supervisor.py fix(gateway): preserve external supervisor ownership 2026-07-16 05:08:56 -07:00
test_gateway_linger.py fix(gateway): refuse to write service definitions with a temp-dir HERMES_HOME (#44267) 2026-06-11 06:10:08 -07:00
test_gateway_platform_gating.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_gateway_proc_fallback.py fix(gateway): track no-systemd restart runtimes 2026-06-23 23:29:28 -07:00
test_gateway_restart_loop.py fix(gateway,cron): guard cron model-tool path + add auto-resume loop breaker (#30719) 2026-07-01 02:48:36 -07:00
test_gateway_runtime_health.py Fix dashboard gateway profile scoping 2026-06-17 05:40:57 -07:00
test_gateway_s6_dispatch.py fix(gateway): fall back to in-process heartbeat when s6 sleep is missing (#36208) (#37120) 2026-06-02 11:59:27 +10:00
test_gateway_service.py fix(gateway): stop systemd retries on fatal config 2026-07-16 01:19:29 -07:00
test_gateway_service_paths.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_gateway_windows.py revert(windows): roll back terminal-popup PRs #53791 #53810 #53829 (#53853) 2026-06-27 15:59:00 -07:00
test_gateway_wsl.py fix(gateway,windows): reliability — JOB breakaway + status --deep probes + test-leak fix (#40909) 2026-06-06 19:53:58 -07:00
test_gemini_free_tier_setup_block.py
test_gemini_provider.py fix(gemini): strip native self prefixes before generateContent (#36141) 2026-06-13 13:47:08 -07:00
test_gmi_provider.py test(gmi): stub profile fetch_models in static-fallback test 2026-06-28 18:05:28 -05:00
test_goals.py fix(auxiliary): route direct-create aux callers through call_llm (#65029) 2026-07-15 07:39:17 -07:00
test_gpt56_registration.py feat(openai): complete gpt-5.6 E2E — codex catalog + 272K compaction auto-raise 2026-07-10 00:47:51 +05:30
test_graphical_browser_detection.py fix(auth): don't launch a text-mode browser inside the terminal for OAuth (#34479) 2026-05-29 01:23:06 -07:00
test_gui_command.py fix(desktop): put Hermes-managed Node on PATH for install/rebuild (#66002) 2026-07-16 20:45:05 -04:00
test_gui_uninstall.py feat: uninstall the Chat GUI without removing the agent (CLI + desktop UI) (#40355) 2026-06-06 18:22:38 -07:00
test_hooks_cli.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_ignore_user_config_flags.py test: deflake CI and dev-machine flaky tests in bulk (11 tests, 10 files) (#61816) 2026-07-09 20:03:11 -07:00
test_image_gen_picker.py fix(auth): refresh Nous entitlement in tool menus 2026-05-28 00:19:31 -07:00
test_input_sanitize.py test(input): preservation regressions and prompt.submit boundary (#62557) 2026-07-15 07:39:42 -07:00
test_install_cua_driver.py fix(cli): drop shell=True from cua-driver installer — download to mkstemp, exec as argv (#58796) 2026-07-05 05:35:44 -07:00
test_inventory.py fix(model): keep configured provider authoritative 2026-07-11 05:43:07 -07:00
test_inventory_pricing.py Add Hermes desktop app (#20059) 2026-05-31 17:46:56 -05:00
test_journey_render.py fix(cli): render /journey color instead of leaking raw ANSI 2026-07-01 16:25:48 -05:00
test_kanban_block_kinds.py feat(kanban): typed block reasons + unblock-loop breaker (#52848) 2026-06-25 21:46:58 -07:00
test_kanban_blocked_sticky.py fix(kanban): align recompute_ready guard with breaker's configured failure_limit 2026-05-30 01:40:57 -07:00
test_kanban_boards.py fix(kanban): align board_exists with board discovery rules 2026-05-18 20:17:10 -07:00
test_kanban_cli.py fix(auxiliary): route direct-create aux callers through call_llm (#65029) 2026-07-15 07:39:17 -07:00
test_kanban_cli_dispatch_passthrough.py fix(kanban): CLI dispatch honors max_in_progress/max_spawn from config; swap missing 'avoid-ai-writing' skill for bundled humanizer (#33488, #29415) (#34337) 2026-05-28 21:00:46 -07:00
test_kanban_core_functionality.py feat(kanban): attachment toolset + CLI to match the dashboard surface 2026-07-16 07:33:14 -07:00
test_kanban_db.py fix(kanban): harden durable artifact handoff 2026-07-12 23:09:41 -07:00
test_kanban_db_init.py fix(kanban): rebuild legacy TEXT-PK tables to INTEGER AUTOINCREMENT on open 2026-05-30 01:40:49 -07:00
test_kanban_decompose.py fix(auxiliary): route direct-create aux callers through call_llm (#65029) 2026-07-15 07:39:17 -07:00
test_kanban_decompose_db.py fix(kanban): decompose children inherit root workspace instead of forcing scratch (#37172) 2026-06-01 20:26:57 -07:00
test_kanban_default_assignee.py feat(kanban): default_assignee fallback + per-profile concurrency cap (#27145, #21582) (#34244) 2026-05-28 19:02:55 -07:00
test_kanban_diagnostics.py fix(kanban): clear failure/crash diagnostics while a retry is in flight 2026-07-09 16:13:59 -05:00
test_kanban_dispatch_lock.py fix(kanban): single-writer dispatch lock to prevent orphan-dispatcher DB corruption (#50331) 2026-06-21 12:06:24 -07:00
test_kanban_goal_mode.py feat(goals): /goal wait <pid> — park the loop on a background process (#50503) 2026-06-22 06:27:29 -07:00
test_kanban_init_lock_bounded.py fix(kanban): bound the cross-process init lock so connect() can't hang forever (#50353) 2026-06-21 12:43:41 -07:00
test_kanban_lifecycle_hooks.py feat(kanban): add task lifecycle plugin hooks (claimed/completed/blocked) (#50349) 2026-06-21 12:38:14 -07:00
test_kanban_notify.py feat(kanban): typed block reasons + unblock-loop breaker (#52848) 2026-06-25 21:46:58 -07:00
test_kanban_per_profile_cap.py feat(kanban): default_assignee fallback + per-profile concurrency cap (#27145, #21582) (#34244) 2026-05-28 19:02:55 -07:00
test_kanban_project_link.py feat(kanban): link tasks to project worktrees 2026-06-25 16:40:26 -05:00
test_kanban_promote.py feat(kanban): --ids bulk promote + AUTHOR_MAP entry for #29464 2026-05-23 23:10:36 -07:00
test_kanban_reclaim_claim_lock_guard.py fix(kanban): make reclaim claim-lock-aware to stop task/run status desync (#50366) 2026-06-21 12:49:07 -07:00
test_kanban_specify.py fix(auxiliary): route direct-create aux callers through call_llm (#65029) 2026-07-15 07:39:17 -07:00
test_kanban_specify_db.py
test_kanban_swarm.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_kanban_worker_image_extraction.py feat(kanban): attach images referenced in task bodies to worker vision (#34210) 2026-05-28 17:50:42 -07:00
test_kanban_worker_spawn_toolsets.py fix(cli): preserve -t/-m/--provider/--tui/--dev before chat subcommand 2026-07-11 03:51:43 -07:00
test_kanban_worker_terminal_cwd.py fix(kanban): pin worker TERMINAL_CWD to the task workspace (#50348) 2026-06-21 12:43:37 -07:00
test_kanban_write_txn_busy_retry.py fix(kanban): retry write_txn on transient SQLITE_BUSY 2026-06-28 02:44:04 -07:00
test_launcher.py
test_list_picker_providers.py fix(gateway): show MoA presets in model picker 2026-06-27 03:43:38 -07:00
test_logs.py refactor(gateway): migrate slack/dingtalk/whatsapp/matrix/feishu/telegram/wecom/email/sms adapters to bundled plugins 2026-06-20 10:26:45 -07:00
test_main_model_custom_provider_normalization.py fix(web): preserve declared providers in model writes (#63058) 2026-07-12 03:08:08 -07:00
test_managed_installs.py fix(tests): catch up 25 stale tests after recent merges (#28626) 2026-05-19 01:28:32 -07:00
test_managed_scope.py feat(managed-scope): add managed_scope module (resolver, loaders, key helpers) 2026-06-19 07:46:33 -07:00
test_managed_scope_cli_config.py fix(managed-scope): apply managed layer in cli.py's standalone config loader 2026-06-19 07:46:33 -07:00
test_managed_scope_config.py feat(managed-scope): managed config layer wins over user config 2026-06-19 07:46:33 -07:00
test_managed_scope_env.py feat(managed-scope): apply managed .env last with override 2026-06-19 07:46:33 -07:00
test_managed_scope_loaders.py fix(managed-scope): honor managed scope in config→env bridges too 2026-06-19 07:46:33 -07:00
test_managed_scope_overlay.py fix(managed-scope): honor managed scope in all standalone config loaders 2026-06-19 07:46:33 -07:00
test_managed_scope_regression.py test(config): pin config/env load behavior before managed scope 2026-06-19 07:46:33 -07:00
test_managed_scope_surfacing.py feat(managed-scope): surface managed scope in config show and doctor 2026-06-19 07:46:33 -07:00
test_managed_scope_writeguard.py feat(managed-scope): guard writes to managed config/env keys 2026-06-19 07:46:33 -07:00
test_managed_uv.py fix(update/windows): don't return _UvResult on Windows (subprocess argv crash) (#39820) 2026-06-05 07:54:08 -05:00
test_mcp_add_command_dest.py feat(cli): add --connect-timeout flag to hermes mcp add 2026-07-05 19:10:00 -07:00
test_mcp_catalog.py feat(mcp): enforce exact version pins across the whole MCP catalog 2026-07-15 04:56:25 -07:00
test_mcp_config.py fix(mcp): close hosted OAuth lifecycle gaps 2026-07-17 04:50:47 -07:00
test_mcp_dashboard_oauth.py fix(mcp): close hosted OAuth lifecycle gaps 2026-07-17 04:50:47 -07:00
test_mcp_reload_confirm_gate.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_mcp_security.py fix(security): close hermes-0day MCP-persistence attack surface 2026-06-21 19:05:27 -07:00
test_mcp_startup.py test(mcp): stub mcp_oauth in backgrounding test to deflake CI 2026-06-27 04:59:23 +05:30
test_mcp_tools_config.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_memory_providers.py fix(desktop): show Hindsight memory provider (#37546) 2026-06-18 16:48:47 -05:00
test_memory_reset.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_memory_setup.py feat(memory): improve OpenViking setup UX 2026-06-17 01:04:26 +08:00
test_memory_setup_provider_arg.py refactor(setup): route dependency installs through the canonical uv→pip→ensurepip ladder 2026-07-07 04:09:35 -07:00
test_migrate_xai.py fix(config): guard xai migration writer + drop gratuitous annotation 2026-07-05 23:00:34 +05:30
test_moa_config.py fix(moa): surface stale presets without retries 2026-07-17 13:49:12 -07:00
test_model_catalog.py feat(models): catalog-labeled silent default — GLM-5.2 marked "default": true in the model catalog 2026-07-15 00:10:31 -07:00
test_model_cost_guard.py fix(model): require confirmation for expensive model selections 2026-06-10 00:24:06 -07:00
test_model_normalize.py fix(gemini): strip native self prefixes before generateContent (#36141) 2026-06-13 13:47:08 -07:00
test_model_picker_expensive_confirm.py fix(model): persist /model switch by default across sessions 2026-06-19 07:07:06 -07:00
test_model_picker_viewport.py
test_model_provider_persistence.py fix(model): preserve named custom provider slug 2026-07-03 03:33:06 -07:00
test_model_switch_configured_provider_routing.py fix(model_switch): route typed configured models off openai-codex (#45006) 2026-06-23 02:03:21 +05:30
test_model_switch_context_display.py fix(cli): pass custom_providers to resolve_display_context_length (#59314) 2026-07-06 02:46:24 -07:00
test_model_switch_copilot_api_mode.py
test_model_switch_custom_providers.py test(model): isolate custom provider discovery 2026-07-10 13:10:30 +05:30
test_model_switch_filter_unresolved.py refactor(model): gate picker rows by runtime capability 2026-07-12 03:04:07 -07:00
test_model_switch_openai_api_mode.py fix(model-switch): override stale api_mode with host-mandated mode on OpenAI-direct switch 2026-07-17 15:47:08 -07:00
test_model_switch_opencode_anthropic.py
test_model_switch_persist_default.py fix(model): persist /model switch by default across sessions 2026-06-19 07:07:06 -07:00
test_model_switch_variant_tags.py
test_model_validation.py fix(tests): patch catalog urlopen wrapper in gemini probe tests (#64318) 2026-07-14 03:11:19 -07:00
test_models.py feat(models): catalog-labeled silent default — GLM-5.2 marked "default": true in the model catalog 2026-07-15 00:10:31 -07:00
test_models_dev_preferred_merge.py feat(kimi): discover K3 on coding endpoint 2026-07-16 13:33:02 -07:00
test_non_ascii_credential.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_nous_account.py feat(billing): /credits command — balance + portal top-up handoff (#44776) 2026-06-12 08:51:10 +00:00
test_nous_auth_keepalive.py fix Nous auth refresh for idle agents 2026-06-21 22:43:48 -07:00
test_nous_auth_status_cache.py fix(desktop): route global remote profile REST calls (#47011) 2026-06-15 23:24:55 -05:00
test_nous_billing_request.py feat(tui+cli): change your Nous plan from the terminal (/subscription, /topup, terminal-billing UX) (#51639) 2026-07-18 14:30:24 +05:30
test_nous_hermes_non_agentic.py
test_nous_inference_url_validation.py test(auth): mock new source-aware Nous state read boundary 2026-07-01 05:06:00 -07:00
test_nous_portal_staging_allowlist.py fix(auth): make HERMES_PORTAL_BASE_URL/NOUS_PORTAL_BASE_URL bypass the Portal host allowlist (#56864) 2026-07-02 06:52:46 +00:00
test_nous_session_validity.py feat(status): expose nous_session_valid on /api/status for hosted-agent self-heal 2026-07-07 00:53:56 -07:00
test_nous_subscription.py fix(whatsapp-cloud): review follow-ups for #43921 2026-06-11 07:51:01 -07:00
test_ollama_cloud_auth.py fix(completion): remove /model <arg> autocomplete from CLI/TUI (#39727) 2026-06-05 06:43:51 -07:00
test_ollama_cloud_provider.py test: remove low-value model-catalog mirror tests 2026-05-29 23:45:05 -07:00
test_oneshot_usage_file.py feat(oneshot): add --usage-file JSON usage report to hermes -z (#59615) 2026-07-06 11:10:51 -07:00
test_openai_codex_model_validation_fallback.py fix(codex-spark): defensive 128k entry in DEFAULT_CONTEXT_LENGTHS + clarify validation test docstring 2026-05-09 23:17:25 -07:00
test_openai_picker_curated.py fix(model-picker): OpenAI shows curated models; OpenRouter no longer phantom-shows (#37404) 2026-06-02 06:31:37 -07:00
test_opencode_go_flat_namespace.py
test_opencode_go_in_model_list.py chore: ruff auto-fix PLR6201 resweep — tuple → set in membership tests (#27355) 2026-05-17 02:29:41 -07:00
test_opencode_go_validation_fallback.py
test_opencode_zen_model_limit.py fix(models): live-first merge + update opencode-zen catalog + uncap aggregator picker 2026-06-27 21:23:25 -07:00
test_overlay_slug_resolution.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_path_completion.py test(cli): URL tokens must not trigger filesystem path completion 2026-06-17 12:33:56 +05:30
test_pet_toggle.py feat(pets): CLI pet pane + /pet command 2026-06-20 14:18:33 -05:00
test_picker_prewarm.py perf(/model): prewarm picker provider-models cache in background (#39847) 2026-06-05 06:55:09 -07:00
test_pin_kanban_board_env.py
test_pip_install_detection.py feat(install): warn pip/Homebrew installs are unsupported (CLI, TUI, desktop) (#57225) 2026-07-07 21:13:19 -07:00
test_placeholder_usage.py
test_plugin_auxiliary_tasks.py feat(plugins): add register_auxiliary_task() to PluginContext API 2026-05-23 17:49:47 -07:00
test_plugin_cli_registration.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_plugin_runtime_disable_gate.py fix(dashboard): run plugin gate after auth + enable example fixture 2026-07-01 04:05:15 -07:00
test_plugin_scanner_recursion.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_plugins.py feat(plugins): pass approve rule keys to approval gate 2026-07-07 15:14:30 -07:00
test_plugins_cmd.py fix(plugins): normalize browser-pasted GitHub repo URLs (#33539) 2026-06-13 13:23:59 -07:00
test_plugins_cmd_category_discovery.py fix(plugins): discover nested category plugins in 'plugins list' (issue #41066) 2026-06-07 08:02:55 +00:00
test_plugins_cmd_enable_disable_nested.py fix(web): correct 'disabled plugin' diagnosis for web backends (#59573) 2026-07-06 04:38:17 -07:00
test_plugins_cmd_list.py feat(plugins): surface entry-point plugins in hermes plugins list 2026-07-06 11:20:47 -07:00
test_plugins_transcription_registration.py feat(stt): add register_transcription_provider() plugin hook 2026-05-25 01:41:19 -07:00
test_plugins_tts_registration.py feat(tts): add register_tts_provider() plugin hook (closes #30398) 2026-05-24 18:04:54 -07:00
test_post_setup_gating.py
test_profile_describer.py fix(auxiliary): route direct-create aux callers through call_llm (#65029) 2026-07-15 07:39:17 -07:00
test_profile_distribution.py fix(dist): stop USER_OWNED_EXCLUDE from filtering nested directories 2026-06-07 21:50:57 -07:00
test_profile_export_credentials.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_profiles.py fix(profiles): allowlist default-export paths + preserve symlinks (#58394) 2026-07-05 00:48:50 -07:00
test_profiles_s6_hooks.py fix(s6): register profile gateways without auto-starting (#46266) 2026-06-15 11:43:23 +10:00
test_project_plugin_rce_bypass.py revert(plugins): restore user dashboard plugin backend API auto-import (#43719) (#51950) 2026-06-24 07:46:54 -07:00
test_projects_cli.py feat(projects): add per-profile project store 2026-06-25 16:40:26 -05:00
test_projects_db.py feat(projects): add per-profile project store 2026-06-25 16:40:26 -05:00
test_prompt_api_key.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
test_prompt_compose_command.py feat(cli): /prompt — compose your next prompt in $EDITOR (#50509) 2026-06-21 20:21:33 -07:00
test_prompt_size.py test(prompt-size): cover resolved-toolset parity and blank-slate minimal count 2026-07-05 19:13:20 -07:00
test_provider_catalog.py feat(providers): remove google-gemini-cli + google-antigravity OAuth providers (#50492) 2026-06-21 19:53:27 -07:00
test_provider_config_validation.py fix(config): stop provider-key warn-storm that stalls Windows logging 2026-07-05 11:44:53 +05:30
test_provider_groups.py feat(model-picker): show short description on grouped provider rows 2026-05-31 15:02:26 -07:00
test_provider_live_curated_merge.py fix(models): scope live-first picker merge to opencode aggregators only 2026-06-27 21:23:25 -07:00
test_provider_parity.py feat(moa): expose MoA presets as selectable virtual models (#46081) 2026-06-25 13:52:06 -07:00
test_provider_precedence.py fix(auth): explicit provider intent beats stale OAuth active_provider (#29285) 2026-06-27 19:49:02 -07:00
test_proxy.py refactor(auth): remove vestigial Nous min_key_ttl/inference_auth_mode params 2026-05-29 02:24:48 -07:00
test_psutil_android_extract.py revert(windows): roll back terminal-popup PRs #53791 #53810 #53829 (#53853) 2026-06-27 15:59:00 -07:00
test_pty_bridge.py fix(pty-bridge): terminate PTY process groups on teardown 2026-06-08 07:03:12 -07:00
test_quarantine_forensic_logging.py test: drop worktree-path sanity guard that fails in CI 2026-07-07 00:53:48 -07:00
test_reasoning_effort_menu.py fix(cli): migrate setup model/provider pickers off simple_term_menu to curses 2026-05-31 03:19:37 -07:00
test_reasoning_full_command.py feat(cli): /reasoning full — show complete thinking, not 10-line clamp (#50499) 2026-06-21 20:21:11 -07:00
test_redact_config_bridge.py
test_regression_16767.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_relaunch.py
test_remote_spending_gate_contract.py feat(tui+cli): change your Nous plan from the terminal (/subscription, /topup, terminal-billing UX) (#51639) 2026-07-18 14:30:24 +05:30
test_resolve_last_session.py
test_resolve_provider_openrouter_pool.py fix(auth): auto-detect OpenRouter credential from the pool, not just env (#42263) 2026-06-08 10:01:47 -07:00
test_run_with_idle_timeout.py fix(update): stream + idle-kill npm run build so a stalled webui-build can't soft-brick the install (#33803) 2026-05-28 03:34:47 -07:00
test_runtime_provider_resolution.py fix(model): keep configured provider authoritative 2026-07-11 05:43:07 -07:00
test_safe_mode.py fix(cli): safe mode also skips shell-hook registration 2026-07-07 02:32:32 -07:00
test_secret_prompt.py feat(memory): improve OpenViking setup UX 2026-06-17 01:04:26 +08:00
test_secrets_bitwarden_non_tty.py fix(secrets): fail early with clear error when bitwarden setup runs without TTY (#40571) 2026-06-06 18:36:40 -07:00
test_security_advisories.py feat(security): supply-chain advisory checker + lazy-install framework + tiered install fallback (#24220) 2026-05-12 01:02:25 -07:00
test_security_audit.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_security_audit_startup.py feat(security): startup security posture audit (warn-on-load) 2026-06-21 19:05:27 -07:00
test_send_cmd.py fix(send): helpful error when --file gets a binary; document MEDIA: attachments (#45116) 2026-06-12 11:48:06 -07:00
test_serve_command.py feat(cli): make hermes serve a real headless backend 2026-06-30 17:58:52 -05:00
test_service_manager.py fix(s6): dot-prefix gateway staging dir so svscan ignores it mid-build (#54834) 2026-06-29 21:33:00 +10:00
test_session_browse.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_session_export.py fix(session-export): escape html tool call names 2026-07-09 19:54:02 -07:00
test_session_export_html.py fix(export): escape tool-call name in HTML session export 2026-07-09 16:46:07 -07:00
test_session_export_html_escape.py fix(cli): normalize role into a single CSS token for the message class 2026-07-09 19:54:02 -07:00
test_session_export_md.py feat(sessions): export sessions to markdown 2026-07-07 12:36:41 -07:00
test_session_filters.py feat(sessions): full filter surface for prune + bulk archive subcommand (#59327) 2026-07-05 22:04:52 -07:00
test_session_handoff.py feat(session): make /handoff actually transfer the session live 2026-05-10 13:06:25 -07:00
test_session_listing.py feat(gateway): add /sessions search <query> (#57685) 2026-07-03 13:44:00 -07:00
test_session_recap.py fix(cli): sanitize terminal escapes when replaying stored history (/resume recap, /status recap) 2026-07-17 04:53:38 -07:00
test_sessions_delete.py feat(sessions): any prune filter matches all ages; preview shows age span (#59415) 2026-07-05 23:01:10 -07:00
test_sessions_export_md_cli.py feat(sessions): trace export + HF upload via 'sessions export --format trace' (#60507) 2026-07-07 15:12:49 -07:00
test_set_config_value.py feat(config): add get and unset commands 2026-07-16 05:44:43 -07:00
test_setup.py test(cli): cover non-interactive prompt_yes_no fallback 2026-06-24 17:56:30 +05:30
test_setup_agent_settings.py
test_setup_blank_slate.py test(setup): blank-slate disabled list must not overlap kept tools 2026-07-05 14:51:49 -07:00
test_setup_hermes_script.py chore: remove Atropos RL environments and tinker-atropos integration (#26106) 2026-05-15 10:36:38 +05:30
test_setup_irc.py test: deflake CI and dev-machine flaky tests in bulk (11 tests, 10 files) (#61816) 2026-07-09 20:03:11 -07:00
test_setup_matrix_e2ee.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_setup_menu_curses_migration.py feat(cli): ranked fuzzy search in the curses model picker 2026-06-01 16:58:58 -07:00
test_setup_model_provider.py fix(cli): require Chromium for local browser readiness in setup/status surfaces 2026-06-05 04:06:17 -07:00
test_setup_noninteractive.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_setup_ollama_cloud_force_refresh.py refactor(cli): extract 18 model-flow wizard functions into model_setup_flows (god-file Phase 2) 2026-06-08 09:42:44 -07:00
test_setup_openclaw_migration.py fix(tests): catch up 25 stale tests after recent merges (#28626) 2026-05-19 01:28:32 -07:00
test_setup_prompt_menus.py fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
test_setup_reconfigure.py feat(setup): thin out setup — Quick Setup via Nous Portal + Full Setup defaults (#35723) 2026-05-31 09:13:06 -07:00
test_signal_handler_kanban_worker.py fix: remove dead f-string prefixes via ruff F541 (216 sites) (#52336) 2026-07-05 13:42:46 -07:00
test_skills_config.py fix(cli): normalize malformed skills config in get_disabled_skills (#61797) 2026-07-09 19:57:54 -07:00
test_skills_hub.py feat(skills): show live per-source progress while browsing (#43398) 2026-06-10 01:02:40 -07:00
test_skills_install_flags.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_skills_skip_confirm.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_skills_subparser.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_skin_engine.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_slack_cli.py fix(slack): complete agent view workspace routing 2026-07-14 13:58:36 -07:00
test_spotify_auth.py fix(spotify): quarantine dead tokens on terminal refresh failure 2026-06-20 23:23:47 -07:00
test_startup_plugin_gating.py
test_status.py fix(cli): redact status API keys with --all 2026-06-30 04:38:43 -07:00
test_status_model_provider.py Merge commit '6110aed9b' into feat/whatsapp-cloud-api 2026-06-10 21:39:22 -04:00
test_status_provider_label.py fix(status): label provider as custom when config.yaml model.base_url is set 2026-07-02 04:59:02 -07:00
test_subcommands_batch.py feat(config): add get and unset commands 2026-07-16 05:44:43 -07:00
test_subcommands_followup.py refactor(cli): promote 9 closure handlers to top-level + extract their parsers (god-file Phase 2 follow-up) 2026-06-07 22:56:23 -07:00
test_subcommands_profile_gateway.py fix(gateway): preserve external supervisor ownership 2026-07-16 05:08:56 -07:00
test_subparser_routing_fallback.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_subprocess_timeouts.py
test_subscription_cli.py feat(tui+cli): change your Nous plan from the terminal (/subscription, /topup, terminal-billing UX) (#51639) 2026-07-18 14:30:24 +05:30
test_suppress_eio_on_interrupt.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_system_stats_platform.py fix(dashboard): show Windows 11 in host panel (#44511) 2026-06-11 19:06:29 -04:00
test_systemd_optional_directives.py fix(gateway): stop systemd retries on fatal config 2026-07-16 01:19:29 -07:00
test_teams_pipeline_plugin_cli.py
test_telegram_managed_bot.py fix(desktop): keep profile homes out of bootstrap (#46073) 2026-06-14 03:08:52 -07:00
test_tencent_tokenhub_provider.py feat(models): swap curated Tencent Hy3 Preview for GA tencent/hy3, drop owl-alpha (#60943) 2026-07-08 07:09:08 -07:00
test_terminal_menu_fallbacks.py fix(model): require confirmation for expensive model selections 2026-06-10 00:24:06 -07:00
test_timeouts.py fix(codex): size and propagate timeouts for Responses-API requests; lower stale defaults 2026-05-25 01:47:55 -07:00
test_timestamps_command.py feat(cli): /timestamps command + timestamps in /history (#50506) 2026-06-21 22:44:25 -07:00
test_tips.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_tool_token_estimation.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_tools_config.py fix(tools): enable platform-native toolsets when their composite is explicitly configured (#35527) 2026-07-07 16:27:28 -07:00
test_tools_disable_enable.py fix(tools): don't report platform-restricted toolsets as enabled 2026-07-16 01:17:11 -07:00
test_toolset_validation.py fix(config): surface invalid platform_toolsets instead of silently dropping tools (#38798) 2026-06-26 14:07:43 +05:30
test_tts_picker.py feat(tts): add register_tts_provider() plugin hook (closes #30398) 2026-05-24 18:04:54 -07:00
test_tui_bundled.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_tui_heap_sizing.py fix(tui): cgroup-aware V8 heap cap so memory-limited containers stop dying silently (#38541) 2026-06-03 16:40:28 -07:00
test_tui_mouse_residue_suppression.py fix(tui): suppress mouse-residue leaks during Python launcher startup (#31213) 2026-05-27 22:03:45 -05:00
test_tui_npm_install.py fix(desktop): force npm --include=dev so self-update rebuild can't be broken by NODE_ENV=production (#38416) 2026-07-16 15:45:07 +00:00
test_tui_resume_flow.py fix(cli): pass TUI Python env from dashboard chat (salvage #44797) (#66581) 2026-07-17 23:04:33 +00:00
test_uninstall_dry_run.py feat: add uninstall dry-run mode 2026-07-07 05:12:24 -07:00
test_uninstall_node_symlinks.py fix(installer): symlink bundled node/npm into command bin dir for FHS root installs 2026-06-04 02:31:49 -07:00
test_update_autostash.py fix(cli,deps): clear esbuild audit loop 2026-06-15 06:18:27 -07:00
test_update_check.py fix(desktop): show remote backend updates without counts 2026-06-25 21:39:29 -06:00
test_update_concurrent_quarantine.py fix(gateway): cold-start installed Windows gateway after update when none was running (#50804) 2026-06-22 06:02:31 -07:00
test_update_config_clears_custom_fields.py fix(model): clear stale endpoint credentials across switches 2026-06-19 19:58:26 -07:00
test_update_hangup_protection.py fix(update): route loud build/installer output to update.log instead of the terminal (#53616) 2026-06-27 11:43:01 -07:00
test_update_interrupted_recovery.py test(cli): cover Windows self-lock recovery guard + cmd-quote its hint 2026-06-28 02:40:37 -07:00
test_update_modified_notice.py refactor(skills): dedupe file-listing, share user-modified predicate, trim diff contract 2026-06-18 12:42:58 +05:30
test_update_post_pull_syntax_guard.py fix(install): guard Windows desktop installs against broken web_server 2026-07-05 19:35:30 -07:00
test_update_stale_dashboard.py fix(cli): exclude desktop-managed backend from stale-dashboard kill 2026-06-03 04:59:49 -07:00
test_update_venv_health.py fix(update): harden #57659 follow-ups — task restore on failure, --force-venv split, trampoline detection, managed-install health (#57680) 2026-07-03 04:08:37 -07:00
test_update_yes_flag.py
test_update_zip_atomic_replace.py fix(cli): make ZIP-update directory replace atomic so it can't delete ui-tui 2026-06-21 13:10:22 -07:00
test_update_zip_symlink_reject.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_upstage_provider.py fix(upstage): collapse unknown future efforts to high; behavior-contract tests 2026-07-15 00:09:24 +05:30
test_urllib_security.py fix(security): order sanitizer after installed hooks 2026-07-11 12:28:55 +05:30
test_user_providers_model_switch.py fix(providers): pass extra headers to model discovery 2026-07-03 04:23:15 +05:30
test_uv_tool_update.py fix(install): scrap rebuild venv 2026-06-04 23:20:29 -04:00
test_verify_console_scripts.py fix(windows): verify launchers after primary install 2026-06-28 17:02:05 -05:00
test_verify_core_dependencies.py fix(cli): quarantine running hermes.exe during update dep-verification repair on Windows (#40409) 2026-06-06 12:50:58 -05:00
test_vertex_provider.py feat(vertex): add Google Vertex AI provider for Gemini (OAuth2) 2026-07-01 05:25:33 -07:00
test_video_gen_picker.py fix(tools): video_gen picker reflects active xAI selection and runs xai_grok post_setup 2026-05-15 12:11:32 -07:00
test_voice_wrapper.py
test_web_oauth_dispatch.py fix(dashboard): support mobile OAuth login 2026-07-09 12:21:16 +05:30
test_web_server.py fix(dashboard): keep custom themes visible after embedded chat starts (#60601) 2026-07-18 00:34:54 -04:00
test_web_server_boot_handshake.py test(web_server): add integration tests for desktop boot handshake fix 2026-06-21 12:29:18 -07:00
test_web_server_console_ws.py refactor(console): remove hosted-context command blocking from Hermes Console (#66144) 2026-07-17 04:33:34 -07:00
test_web_server_cron_profiles.py fix(cron): scope profile runtime during webhook fire 2026-07-10 11:43:09 +05:30
test_web_server_files.py fix(dashboard): close credential-dir-tree gap + .git-credentials in managed-files guard 2026-07-04 17:58:05 +05:30
test_web_server_fs.py fix(desktop): scope remote workspace defaults 2026-06-11 09:41:35 -07:00
test_web_server_gateway_topology.py feat(dashboard): expose profile names + gateway_mode on gated /api/status (#60585) 2026-07-08 00:20:13 +00:00
test_web_server_git.py test(web_git): assert default branch invariant, not hardcoded main 2026-06-28 16:29:52 -05:00
test_web_server_host_header.py fix(dashboard): validate WebSocket Host and Origin 2026-05-24 15:00:44 -07:00
test_web_server_messaging_profiles.py test: valid-shape Telegram token in port-binding guard fixture 2026-07-16 07:17:55 -07:00
test_web_server_oauth_write.py fix(web-server): close OAuth token TOCTOU by writing 0o600 atomically 2026-07-07 13:54:07 -07:00
test_web_server_profile_unification.py fix(dashboard): add HTTP MCP authentication (#65146) 2026-07-16 00:02:09 +05:30
test_web_server_pty_import.py feat(windows): enable dashboard /chat tab via ConPTY (win_pty_bridge) + tests (#42251) 2026-06-08 11:32:43 -07:00
test_web_server_pty_reconnect.py fix(dashboard): reap PTY bridge on child EOF, not only in writer finally (#54190) 2026-06-28 03:58:18 -07:00
test_web_server_session_search.py perf(desktop): make session-id search SQL-bounded, not O(n) 2026-06-04 07:49:34 -07:00
test_web_server_skill_editor.py feat(dashboard): SKILL.md editor on Skills page + attach-skill selector in cron modals (#44231) 2026-06-11 06:10:27 -07:00
test_web_server_skills_profiles.py fix(desktop,gateway,mcp): post-merge — CI contract, review corrections, hub search 2026-07-03 15:22:43 -05:00
test_web_ui_build.py fix(desktop): force npm --include=dev so self-update rebuild can't be broken by NODE_ENV=production (#38416) 2026-07-16 15:45:07 +00:00
test_webhook_cli.py fix(webhook): make dual-stack bind exclusive 2026-07-16 12:36:51 +05:30
test_whatsapp_cloud_setup.py feat(whatsapp): add WhatsApp Business Cloud API adapter 2026-05-23 01:07:01 -04:00
test_whatsapp_onboarding.py Add WhatsApp dashboard pairing flow 2026-07-07 17:45:51 -07:00
test_whatsapp_setup_ordering.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_win_pty_bridge.py feat(windows): enable dashboard /chat tab via ConPTY (win_pty_bridge) + tests (#42251) 2026-06-08 11:32:43 -07:00
test_windows_native_docs.py docs(windows): fix Get-Command PATH guidance to venv\Scripts\hermes.exe (#40613) 2026-06-07 18:28:23 -07:00
test_xai_curated_models.py chore: add AUTHOR_MAP entry for #47904 salvage 2026-06-17 09:49:46 -07:00
test_xai_model_flow.py feat(auth): make xAI Grok OAuth device-code-only, drop loopback login 2026-07-02 13:17:41 -07:00
test_xai_oauth_profile_auth.py fix(auth): resolve xAI OAuth credentials across profiles 2026-06-15 17:03:35 +05:30
test_xai_oauth_refresh.py feat(auth): make xAI Grok OAuth device-code-only, drop loopback login 2026-07-02 13:17:41 -07:00
test_xai_oauth_writethrough.py fix(auth): write rotated xAI OAuth tokens back to global root (#43589) 2026-06-15 17:08:19 +05:30
test_xai_provider_labels.py fix(model): include Premium+ in xAI OAuth label 2026-05-24 18:12:16 -07:00
test_xai_retirement.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_xiaomi_provider.py chore: prune unused imports and duplicate import redefinitions 2026-05-28 22:26:25 -07:00
test_yolo_startup_order.py fix(cli): set HERMES_YOLO_MODE before plugin discovery at startup 2026-07-09 16:58:37 -07:00