Skip to content

feat(tools): surface the Nous free tool pool (entitlement + setup prompt)#36153

Merged
alt-glitch merged 1 commit into
mainfrom
hermes/hermes-9758d510
Jun 1, 2026
Merged

feat(tools): surface the Nous free tool pool (entitlement + setup prompt)#36153
alt-glitch merged 1 commit into
mainfrom
hermes/hermes-9758d510

Conversation

@alt-glitch

Copy link
Copy Markdown
Collaborator

What

Wires the Nous Portal free tool pool through Hermes so $0 subscribers (no paid plan, no purchased credits) can use the managed Tool Gateway backends the pool funds — and prompts them to turn those tools on during setup.

Entitlement (read side). Parse the Portal's tool_access: { enabled, coverage } claim — present on both the OAuth JWT and /api/oauth/account — into NousToolAccessInfo, and gate managed tools on:

  • tool_gateway_entitled — paid access OR a live pool (coarse "any managed tool")
  • tool_gateway_entitled_for(category) — per-tool; the pool funds web/image/tts/browser/modal but not video

This replaces the old paid-only gate across feature detection, the charge picker (ensure_nous_portal_access(coverage_category=…)), managed defaults, and managed_nous_tools_enabled. Per-backend availability honours coverage, so a pool user gets image gen but not video.

Setup prompt (the new UX). prompt_enable_tool_gateway is now a per-tool checklist that renders whenever the pool is enabled (pool-enabled is the trigger), lists only the pool-covered tools (video excluded for free-pool users), and is framed as the "free tool pool" for $0 users instead of a paid subscription. It fires in the existing first-time hermes setup_model_flow_nous path and self-limits once the covered tools are already gateway-routed.

Contract

  • Emitted: tool_access: { enabled, coverage } on the JWT + account API. enabled gates the offer; coverage selects which tools. No new field.
  • Read: tool_gateway_entitled / tool_gateway_entitled_for(category), with the gateway-key → coverage-category map (MANAGED_FEATURE_COVERAGE_CATEGORY).
  • Written: use_gateway: true (+ backend/provider/cloud_provider) per chosen tool.

The account-API echo of tool_access is a separate nous-account-service change (needed for the authoritative force_fresh path); the JWT already carries the claim, so the default path works without it.

Tests

209 passing across test_nous_subscription, test_tools_config, test_tool_backend_helpers, test_cli_provider_resolution, test_setup, test_setup_model_provider. ruff + ty clean on the changed files. Run per-file (the repo isolates tests via scripts/run_tests_parallel.py; a single combined pytest process has pre-existing cross-file contamination).

Note for reviewers

The base entitlement wiring (tool_access parsing + per-category gates) landed on this branch without dedicated unit tests; the new setup-prompt tests exercise it indirectly. Happy to backfill direct read-side tests if wanted.

Read the Portal's tool_access claim (JWT + /api/oauth/account) into NousToolAccessInfo and gate managed Tool Gateway access on it: tool_gateway_entitled (paid OR live pool) and per-category tool_gateway_entitled_for(). The pool funds web/image/tts/browser but not video, so per-backend availability, the charge picker (ensure_nous_portal_access coverage_category), and managed defaults all respect coverage.

Setup: rebuild prompt_enable_tool_gateway as a per-tool checklist that renders whenever the pool is enabled, lists only pool-covered tools (video excluded for free-pool users), and is framed as the free tool pool for $0 subscribers rather than a paid subscription. get_gateway_eligible_tools now gates and filters off the entitlement snapshot.
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-9758d510 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9556 on HEAD, 9554 on base (🆕 +2)

🆕 New issues (1):

Rule Count
invalid-argument-type 1
First entries
tests/hermes_cli/test_nous_subscription.py:344: [invalid-argument-type] invalid-argument-type: Argument to function `prompt_enable_tool_gateway` is incorrect: Expected `dict[str, object]`, found `dict[str, dict[str, str]]`

✅ Fixed issues: none

Unchanged: 4953 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch

Copy link
Copy Markdown
Collaborator Author

@daimon-nous review please. some contracts from the gateway not live yet

@daimon-nous

daimon-nous Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Reviewed PR branch (9 commits behind main). Only the 6 claimed files are touched — all changes are net-new infrastructure that doesn't exist on current main.

Verdict: Approve with one nit. Clean design, fail-closed parsing, correct per-category gating, good test coverage, backwards compatible.

What it does right

Entitlement model. NousToolAccessInfo mirrors the Portal's tool_access claim cleanly. The two-tier gate (tool_gateway_entitled for coarse, tool_gateway_entitled_for(category) for per-tool) avoids the combinatorial explosion of adding per-tool booleans to the dataclass. Paid users bypass all coverage checks — no risk of regression for existing subscribers.

Fail-closed parsing. _tool_access_from_value rejects non-dict input → None, uses _coerce_bool(...) is True for enabled, val is True for each coverage entry. No truthy coercion surprises.

Backwards compatibility. coverage_category is Optional[str] = None on both format_nous_portal_entitlement_message and ensure_nous_portal_access. Existing callers (auth.py, main.py, status.py, conversation_loop.py, tool_backend_helpers.py) don't pass it → same behavior as before.

managed_nous_tools_enabled() update is the right choke point — single-line change from paid_service_access is Truetool_gateway_entitled fans out to all 15+ production callers without per-file edits.

Video gen correctly excluded. managed_video_available now has its own _entitled_for("fal-video") check instead of aliasing to managed_image_available. Pool users get fal-video: False from the JWT → video row hidden in _visible_providers, excluded from get_gateway_eligible_tools, and gated in apply_nous_managed_defaults. Three layers of defense.

UX improvement. prompt_choice (all-or-nothing) → prompt_checklist (per-tool) is strictly better. Pre-selects unconfigured tools, leaves has_direct unchecked, labels correctly differentiate. Pool users see "free tool pool" framing, paid users see "Nous subscription".

Graceful degradation for unreleased account API. When /api/oauth/account doesn't yet return tool_access, payload.get("tool_access")None_tool_access_from_value(None)None → falls back to paid_service_access only. JWT path works today.

Nit

Stale docstringget_gateway_eligible_tools (L730) still says "All lists are empty when the user is not a paid Nous subscriber". Should reflect the new reality: "...when the user is not entitled (paid access or free tool pool)".

Verified

  • E2E entitlement logic: pool user → entitled overall, entitled for fal, NOT for fal-video. Paid → entitled everywhere. Free (no pool, no paid) → not entitled. ✓
  • All 198 targeted tests pass (test_nous_subscription 22, test_tools_config 88, test_tool_backend_helpers 51, test_cli_provider_resolution 20, test_setup 17). ✓
  • MANAGED_FEATURE_COVERAGE_CATEGORY values are a strict superset of _ALL_GATEWAY_KEYS mapped through it. ✓

@daimon-nous daimon-nous Bot added type/feature New feature or request P2 Medium — degraded but workaround exists comp/tools Tool registry, model_tools, toolsets comp/cli CLI entry point, hermes_cli/, setup wizard area/auth Authentication, OAuth, credential pools provider/nous Nous Research API (OAuth) labels Jun 1, 2026
@alt-glitch alt-glitch merged commit e1c7a9a into main Jun 1, 2026
23 checks passed
@alt-glitch alt-glitch deleted the hermes/hermes-9758d510 branch June 1, 2026 01:02
@daimon-nous

daimon-nous Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Post-merge review — this PR was merged before review was requested. Analysis below.

Verdict: ✅ LGTM — clean, well-scoped feature.

Adds NousToolAccessInfo (parsed from JWT tool_access claim) to gate managed tools on a free tool pool in addition to paid access. Per-category entitlement (tool_gateway_entitled_for) correctly separates video (not pool-funded) from web/image/tts/browser. Setup prompt switches from single prompt_choice to per-tool prompt_checklist.

What's right:

  • Fail-closed parsing. _tool_access_from_value returns None for non-dict input; _coerce_bool(…) is True rejects stringified booleans; coverage entries use val is True. No path yields accidental entitlement.
  • Graceful degradation for the account-API gap. The PR notes the account-API echo of tool_access is a separate service deploy. When it's missing, payload.get("tool_access")None_tool_access_from_value returns Nonetool_access=Nonetool_gateway_entitled falls back to paid_service_access is True only. Existing paid-only behavior preserved until the service ships.
  • Coarse gate preserved. managed_nous_tools_enabled() (used by ~40 runtime callers) now returns True for pool users via .tool_gateway_entitled, but per-category fine-gating is only added where needed (feature detection, setup prompt, hermes tools select). No unnecessary churn to existing callers.
  • Video exclusion is multi-layered: _visible_providers hides the managed video row for pool users, get_gateway_eligible_tools filters it out of the offer, apply_nous_managed_defaults gates it on entitled_for("fal-video"), and ensure_nous_portal_access(coverage_category="fal-video") denies it at hermes tools select time. Four independent checks — belt and suspenders.
  • UX framing. Pool users see "free tool pool", paid users see "Nous subscription". source_label is propagated to the confirmation print. Clean.

Minor observations (not blocking):

  1. TOOL_COVERAGE_CATEGORIES tuple exported but unused at runtime. It's a documentation artifact mirroring the Portal's categories. The actual gating uses MANAGED_FEATURE_COVERAGE_CATEGORY (tool key → category). Harmless, but could be a comment instead of a module-level constant.
  2. No direct unit tests for ensure_nous_portal_access(coverage_category=…). The per-category path is exercised indirectly through get_gateway_eligible_tools and prompt_enable_tool_gateway tests. A dedicated test covering the "entitled overall but not for this category" → billing nudge path would strengthen coverage. The PR acknowledges this gap.
  3. _visible_providers video hide check has a redundant condition. pool_only and … and not acct.tool_gateway_entitled_for("fal-video") — the third clause is always true when pool_only is true (pool coverage has fal-video: False). Defense-in-depth, not a bug.

22 tests pass, covering pool exclusion, paid inclusion, checklist rendering, selective writes, and regression guards.

@mxnstrexgl mxnstrexgl left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — automated review passed. No security, quality, or test coverage issues detected.

JoeKowal pushed a commit to JoeKowal/hermes-agent that referenced this pull request Jun 4, 2026
…esearch#36153)

Read the Portal's tool_access claim (JWT + /api/oauth/account) into NousToolAccessInfo and gate managed Tool Gateway access on it: tool_gateway_entitled (paid OR live pool) and per-category tool_gateway_entitled_for(). The pool funds web/image/tts/browser but not video, so per-backend availability, the charge picker (ensure_nous_portal_access coverage_category), and managed defaults all respect coverage.

Setup: rebuild prompt_enable_tool_gateway as a per-tool checklist that renders whenever the pool is enabled, lists only pool-covered tools (video excluded for free-pool users), and is framed as the free tool pool for $0 subscribers rather than a paid subscription. get_gateway_eligible_tools now gates and filters off the entitlement snapshot.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/cli CLI entry point, hermes_cli/, setup wizard comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists provider/nous Nous Research API (OAuth) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants