Skip to content

fix(muse-spark-web): fresh WS token via browserPool, preserve proto envelope when patching - #12914

Open
wray-lee wants to merge 2 commits into
diegosouzapw:release/v3.8.51from
wray-lee:fix/meta-ws-timeout-10727
Open

wray-lee wants to merge 2 commits into
diegosouzapw:release/v3.8.51from
wray-lee:fix/meta-ws-timeout-10727

Conversation

@wray-lee

@wray-lee wray-lee commented Sep 7, 2026 •

Copy link
Copy Markdown

Two things were making this provider hang after 4 WS messages:

  1. The ecto1 token goes stale fast. meta.ai embeds a fresh ecto1:... token in the page HTML on every load, and the gateway only accepts a recent one. A token pasted from DevTools is usually already dead by the time the WS connects, so the gateway answers with a 0x0e frame and goes quiet. Plain HTTP can't read the page either — meta.ai serves a JS challenge that 403s non-browser clients, which is why we couldn't just fetch it server-side.

  2. The proto patch broke the payload. Field [1,1,5] is itself a nested proto (field 5 → field 5 → field 1 → UUID). The old code replaced the whole value with a bare UUID string, dropping the envelopes. Gateway sees a structurally invalid payload and rejects again. This only surfaced once [WIP] Add FastAPI OpenAI-compatible LLM gateway with routing #1 was fixed: fresh token, still 0x0e. Diffing patched against the raw template is what localized it.

What changed:

  • fetchFreshAccessToken() — Heisenberg GraphQL call to pick up a rotated ecto_1_sess when the server sends one, then the existing browserPool to load meta.ai headless, wait out the JS challenge (networkidle), and grab the token from the page. Cached per cookie hash for 4 min (tokens last ~5 min) so we don't launch a browser on every request. Falls back to the static token the user provided.
  • buildWsPromptFrame() — convId patch now traverses [1,1,5,5,1] and swaps just the innermost UUID, keeping both envelopes.
  • Fresh templates (mode_thinking, flags, varint 26) — the old ones also carried a stale UA.

Test coverage

  • tests/unit/muse-spark-ws-proto-patch-10727.test.ts (new) — 5 tests:
    • proto patching as a pure byte transform: asserts the [1,1,5] envelope still wraps {5:{1:uuid}} (40 bytes, not a collapsed 36-byte bare string), byte-length identity when round-tripping the template's own values, and that prompt/requestId patches leave sibling markers (KADABRA, mode_thinking) intact. Confirmed failing against the pre-fix patch code (3 of the 5), green with the fix.
    • token fetcher with mocked browserPool seams: extracts accessToken from the page, serves the second call from the 4-min cache without re-acquiring a context, and returns a sanitized failure when the page has no token.
  • tests/unit/muse-spark-ws-timeout-diagnostics-10727.test.ts — updated; the browser path is mocked so the readyState diagnostics keep running hermetically.

Live-session validation (WS handshake can't run in CI)

The WS flow can't be exercised in CI (it needs a real authenticated session), so it was verified against live meta.ai with a local harness — a standalone replay of the executor's frame builder, not a repo script. It takes the ecto_1_sess cookie as META_COOKIE; the auth token is extracted from the loaded page, the same way fetchFreshAccessToken does it.

META_COOKIE='<ecto_1_sess>' node <local-live-replay>
# token: ecto1:Q8yEDAGWS-... len=322 (extracted from live page via headless Chromium)
# warmup + mode switch: ok (convId 2ac94e6a-a137-4397-8998-cee79859f616)
# patched 944/945 bytes (only the timestamp varint differs), frame sent
# MSG #1-#3: hello/intro-ack/session-ack
# MSG #4: 0x0d response (accepted — pre-fix this was a bare 0x0e rejection)
# 81 messages, gotContent: true — "Hi Wray —"

Under the same credentials, with a fresh token, only the patching differing:

  • collapsed envelope ([1,1] write, pre-fix) → MSG #4: 3b 0e0000, then silence
  • envelope-preserving [1,1,5,5,1] → 0x0d, streamed to completion

A stale token produces the same 0x0e at msg #4 regardless of patching — which is why the two bugs had to be fixed in this order to see either one clearly.

Closes #10727

@diegosouzapw

Copy link
Copy Markdown
Owner

The two-part root-cause analysis here is convincing — the stale ecto1 token plus the nested-proto overwrite bug at [1,1,5] both make sense as independent failure modes, and it's a good catch that the first fix (fresh token) is what exposed the second (proto corruption).

Before this can merge, though: the two test files you touched both explicitly disable the new fetchFreshAccessToken/browserPool path to avoid launching a real browser, which means the new logic (the browser-based token fetch, its 4-minute cache, and the nested-proto patch in buildWsPromptFrame) has no automated coverage at all right now. The proto-patch part especially is pure byte transformation and doesn't need a browser or network to test — could you add a synthetic-input test for that, plus a mocked-browserPool test for the token fetcher's cache/fallback behavior? We'll also want a documented live-session validation before merge, since the WS handshake itself can't be exercised in CI.

…nvelope when patching (diegosouzapw#10727)

Two things were making this provider hang after 4 WS messages:

1. The ecto1 token goes stale fast. meta.ai embeds a fresh token in
   the page HTML on every load, and the gateway only accepts a recent
   one. A token pasted from DevTools is usually already dead by the
   time the WS connects, so the gateway answers with a 0x0e frame and
   goes quiet. Plain HTTP cannot read the page either -- meta.ai
   serves a JS challenge that 403s non-browser clients.

2. The proto patch broke the payload. Field [1,1,5] is a
   double-wrapped nested proto (field 5 -> field 5 -> field 1 ->
   UUID). The old code replaced the whole value with a bare UUID
   string, dropping the envelopes. Gateway sees a structurally
   invalid payload and rejects again.

Changes:
- fetchFreshAccessToken(): Heisenberg GraphQL to refresh ecto_1_sess,
  then the existing browserPool to load meta.ai headless, wait out the
  JS challenge (networkidle), and grab the token. Cached per cookie
  hash for 4 min. Falls back to the user-provided static token.
- buildWsPromptFrame(): convId patch now traverses [1,1,5,5,1] and
  swaps only the innermost UUID, keeping both envelopes.
- Fresh templates (mode_thinking, flags, varint 26).
- Unit coverage for the proto byte transform and the token
  fetcher cache/fallback with mocked browserPool seams.
@wray-lee
wray-lee force-pushed the fix/meta-ws-timeout-10727 branch from ab7474d to c72baa9 Compare September 15, 2026 12:39
@wray-lee

Copy link
Copy Markdown
Author

Wrote the synthetic proto test you asked for, and it caught a second bug in the patch. [1,1,5] is double wrapped (field 5 → field 5 → field 1 → uuid). My code was writing the bare uuid over the outer wrapper, same corruption as the original bug. The earlier live runs only passed because the script wasn't patching the conversation id, so the template's own id went through.

c72baa9 walks [1,1,5,5,1] and swaps just the uuid. The test decodes the built frame and checks the envelope is intact; it fails against the previous commit, passes with the fix. Token fetcher is also covered with the browserPool calls stubbed out, so extraction, the cache and the failure path all run without a browser.

Re-ran live with the fix: 81 messages, streamed reply, frame 4 is 0x0d (accepted) instead of 0x0e. Full output in the PR body.

…iegosouzapw#12914)

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
@isaacfinnegan

Copy link
Copy Markdown

After applying this, I found it works, but ran into an issue with the websocket not getting closed and had to apply this
completion.patch

@wray-lee

Copy link
Copy Markdown
Author

WS cleanup is handler-layer — out of scope for this fix. Feel free to open a separate PR based off this branch if you want it tracked.

This branch has not been deployed

No deployments
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.

fix(providers): Meta AI WebSocket timed out

3 participants