Skip to content

feat(providers): expose a usage-fetch capability in the provider plugin manifest - #11903

Merged
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.51from
pacocartones:feat/11722-usage-fetch-capability
Aug 30, 2026
Merged

diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.51from
pacocartones:feat/11722-usage-fetch-capability

Conversation

@pacocartones

@pacocartones pacocartones commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

An external dashboard that integrates OmniRoute has no API to ask, per provider, whether
usage or quota can be fetched — today it has to read open-sse/services/usage.ts and
re-check that file after every release. The manifest already answers "what can this
provider do" for auth type, executor, Responses support and sidecar eligibility, so this
adds the missing tag rather than a new surface.

capabilitiesFor() now emits usage-fetch for registry entries listed in
USAGE_FETCHER_PROVIDERS. 40 of the 267 registry providers carry the tag.

Resolution is on the entry id and its alias: USAGE_FETCHER_PROVIDERS is keyed by the
strings the usage dispatcher accepts, so it mixes canonical ids (hyperagent) with aliases
(ha, pql, cnl, xao) — the same way getProviderPluginManifestEntryFromRegistry
already resolves a lookup. Today no provider matches on alias alone, so the alias arm
changes nothing; it is there so the tag stays correct if a future entry is registered only
under its alias.

Scope is exactly what the issue asked for: discovery only — no new fetcher, no quota
change, no activation.
Nothing reads the tag yet, and the Dashboard quota widget stays
gated by USAGE_SUPPORTED_PROVIDERS.

Why the list moved to a leaf

USAGE_FETCHER_PROVIDERS moves to a new zero-dependency leaf,
open-sse/services/usage/fetcherProviders.ts, re-exported from services/usage.ts (value
and the derived UsageFetcherProvider type), so every existing import path keeps working
unchanged
— the 20+ call sites and tests that import from services/usage.ts are
untouched.

The move is what keeps the manifest a light leaf. services/usage.ts is the dispatcher and
pulls ~490 local modules (DB, sockets, child_process); config/providerPluginManifest.ts
is a JSON-safe config module served over HTTP at GET /api/v1/provider-plugin-manifest.
Importing the dispatcher there would have grown its graph from 17 → ~490 modules and
dragged the DB layer into the manifest route. Importing the leaf leaves it at 18, with
no new external dependency. Duplicating the list instead would have broken the
single-source-of-truth invariant that the list exists to protect.

This follows the pattern already established by the other usage/ leaves (scalars.ts,
quota.ts) — a behavior-preserving extraction, which also fits the 3.8.5x "non-breaking
structural prep" phase.

Open question from the issue, deliberately not answered here

The issue asked whether to also expose a usage-supported signal (USAGE_SUPPORTED_PROVIDERS,
which gates the quota widget per #10078). The acceptance criteria only list usage-fetch, so
this PR ships just that. Happy to add the second tag in a follow-up if you want it.

Likewise, the onUsageFetch(connection) plugin runtime seam raised in the issue comment is a
runtime extension, not discoverability, and is out of this PR's scope.

Related Issues

Validation

  • Change type: provider (manifest metadata) + docs
  • Focused tests and category gates from the golden path
  • npm run lint
  • Reconciled with the current active release base; focused checks rerun afterward
  • Production-code changes include a new or updated automated test in this PR

Base is release/v3.8.51 at 13afbfa.

Failing-then-passing (Hard Rule #18) — demonstrated by toggling only the emission block
in capabilitiesFor(), everything else identical:

capabilitiesFor() emission Result
removed tests 7 · pass 5 · fail 2
present tests 7 · pass 7 · fail 0

Both failures are assertion failures on the new behavior, not import errors:

✖ manifest advertises usage-fetch for providers with a wired usage fetcher (#11722)
  AssertionError: claude has a wired usage fetcher, so the manifest must advertise usage-fetch
✖ usage-fetch matches the fetcher list by alias too (#11722)
  AssertionError: assert.ok(entry.capabilities.includes("usage-fetch"))

The negative test (manifest omits usage-fetch for providers without a usage fetcher) passes
in both states by design — it is a guard against over-tagging, not a RED/GREEN case.

Regression run over every suite that touches USAGE_FETCHER_PROVIDERS or the manifest —
141/141 passing (15 suites: provider-plugin-manifest, usage-families-split,
qoder-usage-quota, ollama-cloud-usage, xai-usage, xai-oauth-usage, agy-usage-quota,
agentrouter-quota-visibility, executor-hyperagent, executor-promptql, firecrawl-usage,
command-code-usage, kimi-coding-apikey-quota-4435, grok-cli-provider-limits,
qwen-token-plan-quota-fetcher).

Manifest parity check against the live registry: the set of providers tagged usage-fetch
equals the set computed independently from USAGE_FETCHER_PROVIDERS (no missing, no extra),
capabilities arrays stay sorted, the manifest still JSON-round-trips, and usage-fetch is
the only new tag emitted across all 267 providers.

Gates that depend on this change — all green

Gate Result
check:cycles (CI roots) PASS — no cycles
check:cycles on open-sse/config + open-sse/services byte-identical to base — the new import adds no cycle
check:file-size PASS
check:docs-sync PASS
check:doc-links PASS
check:deprecated-versions PASS
eslint (repo invocation, with config/quality/eslint-suppressions.json) PASS
prettier --check on changed files PASS
markdownlint on the changed doc identical to base (pre-existing MD025 from the frontmatter + H1)

Pre-existing red on the base — NOT from this PR

Verified by running each on a pristine checkout of release/v3.8.51 @ 13afbfa with a
clean tree:

  • check:docs-counts — 11 STRICT drifts, output byte-identical before and after
    this PR (diff is empty). All of them are stale counters: provider count 351 vs "357" in
    README.md / AGENTS.md / llm.txt / package.json description / four
    docs/diagrams/*.svg, DB migrations 165 vs "160", i18n locales, MCP tools, compression
    engines. None touches capabilities or the manifest.
  • check:fabricated-docs — 1 drift, docs/providers/CHATGPT_WEB.md:129 referencing
    tests/unit/migration-163-retire-chatgpt-web.test.ts, which no longer exists. Same on base.
  • typecheck:core — 1 error, open-sse/executors/antigravity/executeAttempt.ts(392,47)
    TS2345. Identical on the pristine base; this PR introduces zero new typecheck errors.

Happy to fold any of these into a separate maintenance PR if useful — they are unrelated to
this change, so I left them alone.

Tests Added Or Updated

  • tests/unit/provider-plugin-manifest.test.ts — 3 new tests (4 → 7):
    • manifest advertises usage-fetch for providers with a wired usage fetcher (#11722)
    • manifest omits usage-fetch for providers without a usage fetcher (#11722)
    • usage-fetch matches the fetcher list by alias too (#11722)

Each asserts against the real USAGE_FETCHER_PROVIDERS rather than a hardcoded copy, with a
fixture guard, so the tests cannot silently drift if the list changes.

Changelog fragment: changelog.d/features/11903-usage-fetch-capability.md
(check:changelog-integrity passes).

Coverage Notes

open-sse/config/providerPluginManifest.ts: the new branch in capabilitiesFor() is covered
in both directions — tagged (claude), untagged (openai, anthropic, claude-web), and
the alias arm. open-sse/services/usage/fetcherProviders.ts is pure data with no branches;
it is exercised by the manifest tests and by the 15 suites listed above through the
services/usage.ts re-export.

Reviewer Notes

  • No behavior change. The only runtime effect is one extra string in the capabilities
    array of 40 manifest entries. No fetcher, quota path, routing decision or UI reads it.
  • The riskiest part is the constant move, not the feature. It is a pure move — the array
    literal is byte-identical, and the re-export preserves both the value and the
    UsageFetcherProvider type. The 141-test regression run above is what covers it.
  • ProviderPluginCapability is a widening union change: schemaVersion stays 1, since a
    consumer that ignores unknown tags is unaffected. Say the word if you would rather bump it.
  • Docs: the capability list in docs/reference/PROVIDER_PLUGIN_MANIFEST.md gained
    usage-fetch, plus a new Capability Tags section documenting all seven tags and
    spelling out that usage-fetch is discovery only. It also notes why the tag count (40) is
    lower than the fetcher-list length (46): firecrawl is a search provider and amazon-q is
    an ACP provider, so neither has an entry in the chat-provider registry the manifest is
    generated from.

@pacocartones

Copy link
Copy Markdown
Contributor Author

CI on this PR is red, but every failure also fails on the base commit with a clean tree — none is introduced here. I re-ran the exact failing files on a pristine checkout of release/v3.8.51 @ 13afbfa and diffed the failing-test sets: 16 failures, identical on both sides, diff empty.

Job Failure Same on clean base?
Unit 1/4 #7253 release-green: docs contain zero fabricated API/file-ref drift, 4× provider asset provenance gate / repository provider asset manifest covers the audited 142-file snapshot, Failed to configure Qwen Code yes
Unit 2/4 6A.8: all workspace package deps are in the allowlist yes
Unit 3/4 the gate exits 0 against the current (synced) repo state, provider connection max_concurrent column is healed…029, 2026 discontinued free tiers — providers.ts hasFree reconciliation, g4f.space sub-providers no longer advertise an anonymous free tier, no first-party TypeScript module is imported through a .js specifier yes
Unit 4/4 runFabricatedDocsCheck: real documentation has no fabricated claims, web-cookie health probe (#11488), candidate detection matches catalogued cookie providers only, falls back to providerSpecificData.cookie when apiKey is empty, web-session contract preserves representative token and cookie semantics yes
Docs Gates (fast-path) check:docs-all → check:docs-counts (11 STRICT drifts) + check:fabricated-docs (1) yes — output byte-identical to base
No new ESLint warnings lint:json --max-warnings 0 exits 2 with "There are suppressions left that do not occur anymore" yes — same message, same exit 2 on base
Build (advisory) / Fast Quality Gates inherit the above yes

The drifts are all stale counters and stale references unrelated to this change: provider count 351 vs "357" in README.md / AGENTS.md / llm.txt / the package.json description / four docs/diagrams/*.svg, DB migrations 165 vs "160", plus docs/providers/CHATGPT_WEB.md:129 pointing at tests/unit/migration-163-retire-chatgpt-web.test.ts, which no longer exists.

typecheck:core behaves the same way: 1 error in open-sse/executors/antigravity/executeAttempt.ts(392,47), identical on the pristine base. This PR adds zero new typecheck errors.

What is green and does depend on this change: Vitest, Change Classification, Merge integrity (changelog + generated skills), semgrep, plus locally check:cycles, check:file-size, check:docs-sync, check:doc-links, check:deprecated-versions, check:changelog-integrity, npm run lint (repo invocation with the suppressions file), and prettier --check on every file I touched.

The change's own tests: 7/7 in tests/unit/provider-plugin-manifest.test.ts, and 141/141 across the 15 suites that touch USAGE_FETCHER_PROVIDERS or the manifest.

Happy to open a separate maintenance PR for the counter/reference drift if that would help — I left it alone here to keep this PR to the one capability.

…in manifest

An external dashboard that wants to show, per provider, whether OmniRoute can read
usage or quota has no API for it today — it has to read `open-sse/services/usage.ts`
and re-check the file after every release. The manifest already answers "what can this
provider do" for auth, executor, Responses and sidecar eligibility, so this adds the
missing tag rather than a new surface.

`capabilitiesFor()` now emits `usage-fetch` for registry entries listed in
`USAGE_FETCHER_PROVIDERS`, resolving on the entry id and on its alias: the list is keyed
by the strings the usage dispatcher accepts, so it mixes canonical ids ("hyperagent")
with aliases ("ha"), the same way `getProviderPluginManifestEntryFromRegistry` already
resolves a lookup. 40 of the 267 registry providers carry the tag.

Discovery only — no new fetcher, no quota change, no activation. Nothing reads the tag
yet, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`.

`USAGE_FETCHER_PROVIDERS` moves to a new zero-dependency leaf,
`open-sse/services/usage/fetcherProviders.ts`, and is re-exported from
`services/usage.ts` so every existing import path keeps working. The move is what keeps
the manifest a light leaf: `services/usage.ts` is the dispatcher and pulls ~490 modules
(DB, sockets, child_process), while `config/providerPluginManifest.ts` is a JSON-safe
config module served over HTTP at `GET /api/v1/provider-plugin-manifest`. Importing the
dispatcher there would have grown its graph from 17 modules to ~490; importing the leaf
leaves it at 18 with no new external dependency, and duplicating the list would have
broken the single-source-of-truth invariant the list exists to protect.

Closes diegosouzapw#11722
@pacocartones
pacocartones force-pushed the feat/11722-usage-fetch-capability branch from cb84bf7 to 7f522ba Compare August 29, 2026 02:51
@diegosouzapw
diegosouzapw merged commit 15b1648 into diegosouzapw:release/v3.8.51 Aug 30, 2026
14 of 15 checks passed
maxmad64bis pushed a commit to maxmad64bis/OmniRoute that referenced this pull request Aug 31, 2026
Publish the second usage capability from diegosouzapw#11722. The manifest now
advertises usage-supported alongside usage-fetch so integrators can
tell whether the server usage routes accept a provider without reading
TypeScript. USAGE_SUPPORTED_PROVIDERS moves to a zero-import leaf
(src/shared/constants/providers/usageSupported.ts) and is re-exported
from providers.ts, mirroring the fetcherProviders leaf from diegosouzapw#11903 and
keeping the manifest a light module.

usage-fetch resolves on id or alias (dispatcher accepts both);
usage-supported resolves on id alone, matching the runtime guard
(USAGE_SUPPORTED_PROVIDERS.includes with no alias resolution). 7
differences prove the two notions are distinct (42 shared, 4
fetcher-only, 3 supported-only).
diegosouzapw added a commit to maxmad64bis/OmniRoute that referenced this pull request Sep 1, 2026
…kspace boundary)

The manifest lives in open-sse and may not import from src/ (the open-sse
typecheck gate). Same pure-data leaf pattern as fetcherProviders.ts (diegosouzapw#11903):
open-sse/services/usage/supportedProviders.ts owns the list (typed readonly
string[] so .includes(string) keeps compiling), and src/shared/constants/
providers.ts re-exports it so every existing import path is unchanged. The
dead UsageSupportedProvider type is gone; the list matches the current base
(kilocode included — openrouter/devin-cli arrive with diegosouzapw#12256, which needs a
re-sync onto whichever of the pair lands first).
diegosouzapw pushed a commit that referenced this pull request Sep 1, 2026
…12214)

The provider plugin manifest already exposed usage-fetch (40 providers, #11903); this publishes the second capability, usage-supported, so integrators can tell without reading TypeScript whether the server usage routes accept a provider. #11903 closed #11722 after shipping only half of it and said so at the time — this is the follow-up it promised.

The two scopes genuinely differ and the docs now say how: usage-fetch resolves on id or alias (the dispatcher accepts both), usage-supported on id alone, because the runtime guard does a plain USAGE_SUPPORTED_PROVIDERS.includes(providerId) with no alias resolution. 42 providers carry both tags, 4 carry only usage-fetch (opencode, opencode-zen, openrouter, xai) and 3 only usage-supported (adobe-firefly, firefly, xiaomi-mimo-token-plan) — 7 measured differences, so neither tag implies the other. No list mutation, no new route, schemaVersion stays 1.

USAGE_SUPPORTED_PROVIDERS moved out of src/shared/constants/providers.ts into an import-free leaf at open-sse/services/usage/supportedProviders.ts, keeping the manifest's import graph light — the same move fetcherProviders.ts got in #11903, landed on the correct side of the workspace boundary.

Base note: the branch forked 46 commits before kilocode joined the list, so a wholesale take of its providers.ts would have silently dropped that id. Verified against the current release tip before merging — both sides hold the same 46 ids, nothing lost.

Verified on the current tip: typecheck:core clean, check:cycles OK across 417 files (the import-free-leaf claim holds), and 63/63 focused tests across provider-plugin-manifest, usage-fetcher-registration-coverage, adobe-firefly and agentrouter-quota-dashboard-rendering.

Thanks @maxmad64bis — and for finishing the half of #11722 that was left open rather than letting it sit.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…in manifest (diegosouzapw#11903)

Boarded with 8 other PRs in one combined worktree: typecheck:core, check:file-size, check:changelog-integrity, check:complexity, check:cognitive-complexity, check:cycles, check-native-deps all green; 75/75 focused tests pass. Discovery-only as claimed — nothing reads the new tag yet, dashboard quota widget stays gated by USAGE_SUPPORTED_PROVIDERS. Thanks.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…iegosouzapw#12214)

The provider plugin manifest already exposed usage-fetch (40 providers, diegosouzapw#11903); this publishes the second capability, usage-supported, so integrators can tell without reading TypeScript whether the server usage routes accept a provider. diegosouzapw#11903 closed diegosouzapw#11722 after shipping only half of it and said so at the time — this is the follow-up it promised.

The two scopes genuinely differ and the docs now say how: usage-fetch resolves on id or alias (the dispatcher accepts both), usage-supported on id alone, because the runtime guard does a plain USAGE_SUPPORTED_PROVIDERS.includes(providerId) with no alias resolution. 42 providers carry both tags, 4 carry only usage-fetch (opencode, opencode-zen, openrouter, xai) and 3 only usage-supported (adobe-firefly, firefly, xiaomi-mimo-token-plan) — 7 measured differences, so neither tag implies the other. No list mutation, no new route, schemaVersion stays 1.

USAGE_SUPPORTED_PROVIDERS moved out of src/shared/constants/providers.ts into an import-free leaf at open-sse/services/usage/supportedProviders.ts, keeping the manifest's import graph light — the same move fetcherProviders.ts got in diegosouzapw#11903, landed on the correct side of the workspace boundary.

Base note: the branch forked 46 commits before kilocode joined the list, so a wholesale take of its providers.ts would have silently dropped that id. Verified against the current release tip before merging — both sides hold the same 46 ids, nothing lost.

Verified on the current tip: typecheck:core clean, check:cycles OK across 417 files (the import-free-leaf claim holds), and 63/63 focused tests across provider-plugin-manifest, usage-fetcher-registration-coverage, adobe-firefly and agentrouter-quota-dashboard-rendering.

Thanks @maxmad64bis — and for finishing the half of diegosouzapw#11722 that was left open rather than letting it sit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(providers): expose a usage-fetch capability in provider-plugin-manifest

2 participants