Skip to content

feat: enable Contributor max via Muse User-Agent fingerprint - #20

Merged
BlockedPath merged 3 commits into
BlockedPath:mainfrom
AdityaVG13:feat/contributor-max-fingerprint
Sep 26, 2026
Merged

BlockedPath merged 3 commits into
BlockedPath:mainfrom
AdityaVG13:feat/contributor-max-fingerprint

Conversation

@AdityaVG13

@AdityaVG13 AdityaVG13 commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Maintainer update (f4a6330): the fingerprint is now opt-in via META_MUSE_USER_AGENT=1. It is sent only for muse-spark-1.3-contributor on direct https://api.meta.ai/v1 requests, and Contributor max is exposed only when opted in. The live probe asserts only the fingerprinted 200, and the unrelated credential test is removed. README has an opt-in section with a warning. The original description below reflects the first revision.


What

muse-spark-1.3-contributor now exposes the max reasoning effort, and the extension sends the captured Muse CLI User-Agent on direct Meta Model API requests so the server accepts it. Works the same on both credential types: a static API key (META_API_KEY/MODEL_API_KEY) and Muse Code login (/login meta, minted key).

Why

Live-probed 2026-09-25 against POST /v1/responses on login-minted credentials: Contributor + reasoning.effort "max" returns HTTP 400 with a generic client UA and HTTP 200 with the Muse UA — identical payload, only the header changed (API-key parity reported in the OMP PR below). Standard 1.3 accepts max with any UA (unchanged). This mirrors the oh-my-pi #12199 finding for OMP.

Meta documents max for standard-tier 1.3 only, so Contributor max is treated as observed wire behavior throughout (code comments, README, tests) — not a permission claim, and it may be gated differently later.

How

  • extensions/meta.ts
    • MUSE_USER_AGENT: captured fingerprint (muse-build/1.3.0 ..., from the Muse CLI inference entrypoint)
    • isDirectMetaModelApiUrl(): strict direct-endpoint check (https, api.meta.ai case-insensitive, default port, bare /v1); proxies, lookalike hosts, subpaths, and non-default ports never match
    • applyMetaUserAgentFingerprint(): setdefault — explicit User-Agent headers (any casing, including null suppression) always win
    • New before_provider_headers hook (available on the full >=0.83 <0.86 peer range, verified per version): Meta provider only, endpoint verified from the composed model, no fingerprint when the endpoint cannot be verified
    • Fallback map for muse-spark-1.3-contributor gains max -> "max"; toProviderModels already inherits fallback max for bare catalog IDs, so no change needed there. Older revisions stay clamped.
  • Tests: endpoint table, setdefault/override behavior, hook wiring (Meta-only, direct-only, unverified-skipped), dual-auth provider-config guard, and a credential-gated live probe (400 bare / 200 fingerprinted, skipped in CI like the existing cache probe)
  • README: feature bullet, Models paragraph, static-key note

Verification

bun run typecheck   # clean
bun test            # 37 pass, 1 skip (CI-only live catalog test); both live probes pass locally

Also mutation-checked the endpoint guard (flipped comparison fails the test, then reverted).

@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 7ac19fdc-b7b8-49d0-9a9a-504817e4f060


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@BlockedPath BlockedPath left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the thorough PR — the endpoint guard, setdefault semantics, and test coverage are clean. I checked it out locally: typecheck and hermetic tests pass on Pi 0.85.1, and the hook behaves the same on Pi 0.87.1 (tests pass; the one 0.87 typecheck failure is pre-existing on main and fixed in #21).

I can't merge it as-is, though, mainly for a policy reason:

1. Default-on client impersonation. This makes every Meta request from this package identify as the first-party Muse CLI in order to get past a server-side gate Meta only documents for standard-tier 1.3. For a published package that puts users' keys/accounts at risk if Meta treats it as abuse, and it's not something I want enabled silently.

If you'd like to keep going, I'd accept it with these changes:

  • Opt-in only, e.g. META_MUSE_USER_AGENT=1 (or a provider setting). Off by default, with a README note that it spoofs the Muse CLI User-Agent, is unsupported by Meta, and may stop working or violate Meta's terms.
  • Scope it to muse-spark-1.3-contributor, not every Meta request. Today the header is applied to all Meta traffic, including models that don't need it.
  • Gate the max mapping on the same opt-in. With max: "max" unconditionally in the Contributor fallback map, users without the fingerprint (e.g. behind a proxy baseUrl) are offered a level that 400s.
  • Fix the hardcoded platform. linux-x86_64 is sent from every OS.

2. Hook uses the session model, not the request model. before_provider_headers gets ctx.model from the session (createContext() → getModel()), so the check follows the active session model rather than the model the request is for. A non-Meta request made while the session model is Meta (or the reverse) gets the wrong result. That's worth a comment at least, and it's another reason to keep the blast radius small via opt-in plus model scoping.

3. Smaller items

  • The live probe asserts the bare request returns 400, so it breaks if Meta ever opens max up. Please assert only the fingerprinted 200 (or make the bare check informational).
  • The "accepts both API-key and Muse Code login credentials" test is unrelated to this change. Please drop it or move it to a separate PR.
  • Please add a CHANGELOG.md entry under Unreleased.
  • Please rebase on main once #21 (Pi 0.87 support) lands.

Happy to re-review once it's opt-in and scoped.

Live-probed 2026-09-25 against POST /v1/responses: muse-spark-1.3-contributor accepts reasoning.effort "max" only when the request carries the Muse CLI User-Agent (400 without it, 200 with it, identical payload). Standard 1.3 accepts max with any User-Agent, as before.

- fallback map for muse-spark-1.3-contributor gains max -> "max"
  (toProviderModels already inherits fallback max for bare catalog IDs)
- new before_provider_headers hook setdefaults the captured Muse
  User-Agent on direct api.meta.ai requests only; explicit headers
  (any casing, incl. null suppression) and non-Meta endpoints untouched
- applies to both credential types: static API key and Muse Code
  login (minted) keys hit the same direct wire
- hermetic wire-contract tests + gated live probe (400 bare / 200
  fingerprinted); README updated
First-hand live verification used login-minted credentials; the API-key leg comes from the OMP report. Keep the comment honest.
@BlockedPath BlockedPath self-assigned this Sep 26, 2026
@BlockedPath
BlockedPath force-pushed the feat/contributor-max-fingerprint branch from 5c6fc42 to 601f3ab Compare September 26, 2026 01:34
@BlockedPath

Copy link
Copy Markdown
Owner

Maintainer update:

  • I rebased this onto main (now including fix(meta): support pi 0.87 (peer range + 0.87.1 devDeps + TranscriptContext test helper) #21, Pi 0.87 support) and pushed it to your branch. Both commits applied unchanged, and CI is green. Please git pull --rebase (or reset to 601f3ab) before you push again.
  • Correction to point 2 of my review: the session-model vs request-model concern is mostly theoretical. On Pi 0.87.1, before_provider_headers only runs for the agent's own requests, and cache warming stops once the session model changes. You don't need to address it.
  • I confirmed the fingerprint reaches the wire: hook headers are merged after pi-ai's default pi (...) User-Agent and replace it. That's one more reason to scope it to muse-spark-1.3-contributor, so other Meta traffic keeps Pi's normal User-Agent.

The rest of the requested changes still stand: opt-in, Contributor-only scoping, max gated on the opt-in, the platform string, the live-test assertion, and the changelog entry.

…or-only

Maintainer follow-up to the review on this PR.

- Gate the fingerprint behind META_MUSE_USER_AGENT=1|true|yes (off by
  default), since it identifies Pi as Meta's first-party Muse client.
- Scope the before_provider_headers hook to muse-spark-1.3-contributor;
  all other Meta traffic keeps Pi's own User-Agent.
- Gate Contributor max on the same opt-in across fallback, catalog, and
  cached-restore model lists (gateMuseMaxEffort), so users who have not
  opted in are never offered a level that 400s. Server-advertised or
  custom max mappings still pass through.
- Keep the captured UA byte-for-byte (documented), since variants for
  other platforms are unverified.
- Live probe: assert only the fingerprinted 200 and log the bare status,
  so the suite survives Meta opening max up.
- Drop the unrelated credential-type test; add hermetic env helper,
  opt-in parsing, hook scoping, gating, and cached re-gate tests.
- README opt-in section with a warning, CHANGELOG, and AGENTS.md notes.

@BlockedPath BlockedPath left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Requested changes addressed in f4a6330: the fingerprint is opt-in (META_MUSE_USER_AGENT), sent only for muse-spark-1.3-contributor, and Contributor max is gated on the same flag. CI is green. Thanks @AdityaVG13!

@BlockedPath
BlockedPath merged commit 3e85728 into BlockedPath:main Sep 26, 2026
7 of 9 checks passed
@AdityaVG13
AdityaVG13 deleted the feat/contributor-max-fingerprint branch September 26, 2026 09:22
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.

2 participants