Skip to content

[v3.8.50] Fix Z.ai web browser transport and model capabilities - #8451

Merged
diegosouzapw merged 6 commits into
cheaper-inference:release/v3.8.50from
backryun:agent/zai-web-browser-transport
Aug 12, 2026
Merged

diegosouzapw merged 6 commits into
cheaper-inference:release/v3.8.50from
backryun:agent/zai-web-browser-transport

Conversation

@backryun

@backryun backryun commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Rework zai-web authentication around the chat.z.ai Local Storage token and an authenticated browser transport that obtains the per-request CAPTCHA proof.
  • Replace stale imported model discovery with the four verified models: GLM-5.2, GLM-5.1, GLM-5-Turbo, and GLM-5V-Turbo.
  • Support GLM-5.2 Deep Think High/Max controls and real GLM-5V image uploads through the authenticated Z.ai page.
  • Improve model-test, request-queue, abort, and browser-transport errors, including the Z.ai-scoped 60-second test/queue budget and correct failure reporting.
  • Update the dashboard credential guide and add focused regression coverage.

Root cause

The previous provider path expected a cookie-based credential and stale remote model discovery. It could not reliably complete Z.ai's current CAPTCHA-gated browser flow, coordinate clicks were intercepted by the landing-page animation, multimodal parts were not uploaded as files, and local abort/queue failures could be surfaced with misleading status or success state.

Impact

Users now copy only the token value from chat.z.ai Local Storage. OmniRoute performs the authenticated page request, model/Deep Think selection, CAPTCHA handling, and image upload. The public model list is curated and no longer imports obsolete entries.

Latest rebase and validation

Rebased agent/zai-web-browser-transport onto:

upstream/release/v3.8.50 @ 371c10ea5f1184ffcb74d71f6bec15885c2dfe28

The rebase completed without conflicts. git range-diff matched all five original commits one-to-one:

1: 38095fa79 = 1: 56614f29b fix: complete Z.ai web browser transport
2: b5b0d1e2e = 2: d4ea6c128 refactor: address Z.ai review feedback
3: 6ed0e8285 = 3: 960d69b9d test(zai-web): reconcile the #8014 endpoint guard with the chats/new + signed flow
4: 02ab9afc4 = 4: 432905f86 fix(zai-web): surface upstream error frames instead of finishing empty
5: e603e43bb = 5: 02a3aa9ed refactor(sse): extract the zai-web transports so the complexity ratchet holds

Fresh checks on the rebased branch:

  • PR-touched focused Z.ai/support unit files (13 files): 91/91 passed.
  • npm run typecheck:core: passed.
  • npm run check:dashboard-typecheck: passed; 254 dashboard errors remain within the frozen baseline.
  • npm run lint: passed.
  • npm run check:file-size: passed (121 frozen files; 44 frozen test files).
  • npm run check:complexity-ratchets: passed (complexity=2202 vs baseline 2774; cognitiveComplexity=980 vs baseline 1223).
  • npm run check:complexity: passed.
  • npm run check:cognitive-complexity: passed.
  • git diff --check upstream/release/v3.8.50...HEAD && git diff --check: passed.
  • No dependencies were installed or updated, and no live upstream request was made during this local revalidation.

Current relevant file sizes:

file lines cap
open-sse/executors/zai-web.ts 597 800
open-sse/executors/zai-web/protocol.ts 554 800
open-sse/executors/zai-web/stream.ts 214 800
open-sse/executors/zai-web/browserAutomation.ts 150 800
open-sse/services/browserBackedChat.ts 843 850 frozen

@backryun

Copy link
Copy Markdown
Contributor Author
image image image perfectly fixed!

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thanks for the deep rework here — the localStorage-token flow, the HMAC signature reverse-engineering, the CAPTCHA-aware browser transport, and the Deep Think/vision capability mapping are all clearly the product of real, live testing against chat.z.ai, and the 28 new/updated unit tests hold up (I ran the full suite locally and everything passes). This is exactly the kind of fix we want to land.

Before we can merge as-is, CI is currently red on two gates we'll need addressed on your branch:

  1. File-size quality gate: open-sse/executors/zai-web.ts grew to 1364 lines (our cap for a file this size is 800), and open-sse/services/browserBackedChat.ts grew past its frozen 850-line ceiling to 954. Could you split the browser-automation helpers (model selection, Deep Think toggle, web-search toggle, etc.) into a separate module? A couple of the new/updated test files also went over their size caps.
  2. "No new ESLint warnings": this job is failing on your PR run but I wasn't able to pin the exact violation from the CI log (it writes to a JSON artifact rather than printing to console) — could you run npm run lint:json -- --max-warnings 0 locally and check .artifacts/eslint-results.json for the offending rule/file?

Separately, since this PR also changes the global rate-limit-queue and model-test timeout defaults (15s→60s, 30s→60s) for every provider, not just zai-web, two of our docs (docs/architecture/RESILIENCE_GUIDE.md and docs/reference/ENVIRONMENT.md) now state a stale 15s default with an explanation that no longer matches the code — a small doc update alongside the fix would keep that accurate. We'll also double check whether that default should stay global or be scoped just to zai-web the way a couple of other slow providers already are in modelTestRunner.ts.

Everything else — the executor logic, error sanitization, and test coverage — looks good on our side. We'll take care of confirming the exact ESLint violation and reviewing the file split with you; the docs update we're happy to fold in ourselves if that's easier. Thanks again for the thorough live validation work on this one.

@backryun
backryun force-pushed the agent/zai-web-browser-transport branch from 1130c12 to 9085fdf Compare July 25, 2026 03:52
@backryun

Copy link
Copy Markdown
Contributor Author

@diegosouzapw Thanks for the detailed review — all requested changes have been addressed in commit 9085fdf7e:

  • Split open-sse/executors/zai-web.ts into focused protocol, browser-automation, and stream modules; the entry file is now 502 lines.
  • Extracted the browser-backed chat types; browserBackedChat.ts is now 842 lines.
  • Split the oversized model-sync and stream-handler tests; all files touched by this review are now below their applicable size caps.
  • Restored the global queue/model-test defaults to 15s/30s and scoped the 60s minimum specifically to zai-web, so the existing resilience/environment docs remain accurate.
  • Ran npm run lint:json -- --max-warnings 0 from a clean cache: it passes with zero new warnings and no additional suppressions. The current GitHub lint failure is from a restored stale .eslintcache reporting suppressions left that do not occur anymore; the same command passes cleanly without that cache.

Validation after the split: 126 relevant unit tests passed, along with npm run typecheck:core, npm run check:docs-all, the exact ESLint command above, and the pre-commit quality hooks.

I also checked the five currently red jobs. Their reported file-size/test failures are in existing base files and tests outside this PR diff; none point to the Z.ai changes. The branch is ready for another review or CI rerun.

@backryun
backryun force-pushed the agent/zai-web-browser-transport branch 2 times, most recently from 5de8cfe to c1c0862 Compare July 25, 2026 08:05
@backryun

Copy link
Copy Markdown
Contributor Author

Rebased onto release/v3.8.49 @ 30709255c — CONFLICTING → MERGEABLE, 0 behind. Rebase rather than merge, so no merge commit; the branch is still the same two commits plus one test-reconciliation commit.

What conflicted, and why

#8503 merged at 07:56 and touched the two files this branch also rewrites: open-sse/executors/zai-web.ts and tests/unit/executor-zai-web.test.ts.

zai-web.ts — constants

This branch's side is a strict superset: same CHAT_URL = ${BASE_URL}/api/v2/chat/completions, plus NEW_CHAT_URL and the signature constants. Took this branch's side; #8503's endpoint fix is fully preserved.

zai-web.ts — header doc

#8503's block described Cookie-header auth (Cookie + Authorization both sent from the cookie). This branch replaced that model with the localStorage token, so keeping it would have left the file documenting an auth flow it no longer implements. Took this branch's doc — but kept #8503's #8014 lineage, rewritten to fit:

 * Completions go to /api/v2/chat/completions; the older unversioned
 * /api/chat/completions path is stale and 404s model-independently (#8014).

executor-zai-web.test.ts

#8503's assertions read capturedUrl / capturedInit / headers.Cookie. This branch replaced the capture with ZaiFetchCapture { completionUrl, completionInit, newChatInit }, so those identifiers no longer exist in the file. Took this branch's side, but re-expressed #8503's v2 assertion in the new structure rather than dropping it.

Two tests then failed — worth flagging, because one was a real gap

A clean rebase is not the same as a correct one, so I ran the suites.

1. My conflict resolution was wrong. I re-expressed #8503's assertion as exact equality, but this branch's completion URL carries the request signature as a query string:

https://chat.z.ai/api/v2/chat/completions?timestamp=…&signature_timestamp=…

Changed to a prefix match. My error, fixed.

2. zai-web-chat-endpoint-8014-probe.test.ts captured nothing — actual: '', meaning fetch was never called. The probe drives the executor with a bare cookie credential and no captcha proof; under this branch that routes through the browser transport, so no direct request is issued and the guard silently verifies nothing.

That is a genuine incompatibility between the merged guard and this branch's architecture, not a flake. Adapted the setup while keeping the test's identity and intent:

  • supplied a direct-path credential ({ token, captcha_verify_param }), matching how the existing suite reaches that path
  • matched on pathname across all requests, since the executor also probes the homepage for the frontend version and calls /api/v1/chats/new before the completion

The guard ends up stronger than before: it now asserts that no request reaches the stale unversioned path and that exactly one completions request is issued, against v2.

54/54 across the zai suites; typecheck:core and eslint clean.

On the earlier review items

Two of the three are already addressed by this branch's second commit, so flagging rather than re-doing:

  • File size — zai-web.ts 1364 → 505, split into zai-web/protocol.ts (554), stream.ts (174), browserAutomation.ts (156); browserBackedChat.ts 842, under its frozen 850.
  • Timeout scope — the 60s is now scoped to zai-web in modelTestRunner.ts alongside DOLA_PRO / GITHUB_PHI_REASONING, and src/lib/resilience/settings.ts is untouched.

Which resolves the docs item by removing it: with the global default back at 15s, RESILIENCE_GUIDE.md and ENVIRONMENT.md are accurate as-is and should not be updated — doing so now would introduce the very inaccuracy the review was guarding against. Recorded that in the PR description rather than leaving it to be rediscovered.

Process note

This branch belongs to another session, so the rebase ran in a detached worktree — the branch was never checked out, and stayed available throughout — and the push used --force-with-lease pinned to the expected prior SHA (5de8cfef2), so it would have failed rather than overwritten anything landed in the meantime. Only the base moved; no authored work was touched beyond the two conflict resolutions and the test fix described above.

@SamIsHere006

Copy link
Copy Markdown

[21:35:52] 📊 [USAGE] ZAI-WEB | in=97 | out=0 | account=4dd836b1... (estimated)
[21:35:52] 🌊 [STREAM] ZAI-WEB | glm-5.2 | 414ms | complete
POST /api/v1/chat/completions 200 in 481ms (next.js: 9ms, proxy.ts: 11ms, application-code: 462ms)
[16:05:52.334] DEBUG: [sse] Stream readiness confirmed in 2ms (zai-web/glm-5.2)
module: "sse"
tag: "STREAM"
[16:05:52.335] DEBUG: [sse] Standard passthrough mode
module: "sse"
tag: "STREAM"

i'm not getting any response from any model on z.ai

@SamIsHere006

Copy link
Copy Markdown
{960F69C2-F47C-45C9-872A-A9DAC41E9318}

@backryun

Copy link
Copy Markdown
Contributor Author

@SamIsHere006 — thanks for the logs, that's a useful failure signature: HTTP 200 with out=0 and the stream closing in 414 ms means the request was accepted and the SSE stream carried no content frames. That is a different failure from the original #8014 report, which was a model-independent 404.

One question decides where this belongs, and I can't answer it from the log you posted: which build are you on?

The model name doesn't settle it — glm-5.2 can appear on either. release/v3.8.49 gained live model discovery for zai-web (#7678), so it pulls the catalog from chat.z.ai/api/models rather than the hardcoded 3-model list, and glm-5.2 will show up there too.

What does settle it: does your log contain a request to /api/v1/chats/new?

  • No chats/new → you are on release/v3.8.49. That build POSTs straight to /api/v2/chat/completions.
  • chats/new present → you are on this PR's branch, and the failure is in this branch's flow.

If it helps, omniroute --version plus the commit you built from would answer it directly.

What I can verify, for whoever picks this up

I checked open-sse/executors/zai-web.ts on the current release tip (4053e2314) — 398 lines:

piece release tip
POST /api/v2/chat/completions present (#8503)
POST /api/v1/chats/new 0 occurrences
X-Signature header 0
captcha_verify_param 0
chat_id in the request body 0

The body it sends is { stream, model, messages, params: {}, features: {…} } — no chat context.

That is consistent with #8503's own scope note, which said so explicitly rather than claiming otherwise:

the reference implementation also suggests the v2 flow may require a prior POST /api/v1/chats/new (chatId) and an X-Signature HMAC header. Those cannot be verified without a live chat.z.ai cookie session, so they are intentionally not included in this PR

So if you are on release, a 404 turning into "200 with an empty stream" after the endpoint was repointed is the shape you would expect, and #8014 would need reopening rather than staying closed as fixed. I'm deliberately not asserting that yet — I have no live z.ai session to confirm causation, and the same symptom could have other causes.

If you are on this branch instead, then it is a bug here and the log line above is the place to start.

Either way the answer to the chats/new question tells us which, so it seems worth asking before anyone changes code.

@backryun
backryun force-pushed the agent/zai-web-browser-transport branch from c1c0862 to cdc64ee Compare July 25, 2026 21:30
@backryun

Copy link
Copy Markdown
Contributor Author

@SamIsHere006 — I could not reproduce your case directly (no live z.ai session here), but reading the stream path turned up something that explains why your log is so uninformative, and it is worth fixing regardless of which build you are on. Pushed as a commit on this branch.

Why out=0 tells you nothing

HTTP failures are already handled properly — a non-200 becomes a real error with the upstream's body attached. The gap is the case you hit: 200, with the failure described inside the SSE body.

const delta = parseSsePayload(raw);
if (delta && onDelta(delta)) return true;   // null → dropped, silently

parseZaiFrame recognises exactly two shapes: an OpenAI-style choices[], and z.ai's internal {delta_content, phase, done} envelope. Anything else returns null and is discarded. The stream then closes normally:

if (!roleState.emitted) emitChunk(controller, { role: "assistant", content: "" });
emitChunk(controller, {}, "stop");
controller.enqueue("data: [DONE]\n\n");

Which is exactly your log: 200, out=0, "complete". A rejected signature, an expired captcha, a stale token and a protocol change all produce that same line. Hard Rule #6 — never silently swallow errors in SSE streams.

I confirmed the same swallow exists on release/v3.8.49, so this is not specific to this branch.

What changed

parseZaiFrame now treats an explicitly error-shaped frame (error at the top level or under data; string or {detail|message|msg}) as terminal, checked before the delta paths so it cannot fall through to the null. It surfaces as:

[Z.ai error] <the upstream's own message>

matching the mid-stream convention the other web executors already use. Anything already streamed before the failure is preserved, and the message goes through sanitizeErrorMessage.

What I deliberately did not change: a contentless frame still parses to null. That is not an oversight — z.ai genuinely sends phase frames with no delta_content, and executor-zai-web.test.ts pins that behaviour ("returns null for frames with no usable delta"). Treating "nothing parseable arrived" as a failure would be inventing policy on top of an observed protocol and could raise false errors on a working stream. So this only adds recognition of explicit error frames — narrower than my first draft, which I threw away after reading how the parser was actually built.

7 tests, RED before / GREEN after, including a regression guard that phase-only streams are still not reported as errors. 94/94 across the zai + stream suites.

What this gets you

If you re-run against this branch, the reply should now carry the actual reason instead of nothing. Please paste whatever [Z.ai error] text you get — that is the missing piece, and it also answers the earlier question about which build you are on:

  • if the message names a signature or chatId problem, that is the protocol path
  • if it names auth, the credential needs refreshing
  • if nothing appears at all, the upstream is sending a shape not covered here and I would like to see the raw frame

Sorry the first answer had to be a question rather than a fix — with the error swallowed there was genuinely nothing to go on.

@backryun
backryun force-pushed the agent/zai-web-browser-transport branch 6 times, most recently from 298a37b to 76f6f27 Compare July 28, 2026 02:03
@backryun

Copy link
Copy Markdown
Contributor Author

Root refresh (2026-07-28): rebased onto release/v3.8.49 at d6c0693 and force-pushed safely with lease. New head: 76f6f277a. Validation on the rebased tree: 91/91 focused tests, core typecheck, dashboard frozen-baseline typecheck, file-size gate, complexity ratchets, changed-file ESLint, and diff check all pass.

@diegosouzapw diegosouzapw changed the title Fix Z.ai web browser transport and model capabilities [v3.8.50] Fix Z.ai web browser transport and model capabilities Jul 28, 2026
@diegosouzapw diegosouzapw added the deferred-v3.8.50 Adiada para o ciclo v3.8.50 (validacao VPS, refactor, ou escopo grande) label Jul 28, 2026
@backryun
backryun force-pushed the agent/zai-web-browser-transport branch from 76f6f27 to 3656ff9 Compare July 28, 2026 03:27
@backryun
backryun force-pushed the agent/zai-web-browser-transport branch from 3656ff9 to a5c9d70 Compare July 28, 2026 04:43
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Heads-up: this PR is running against a stale base

release/v3.8.49 has moved 4 commits ahead of the base this PR was last built against, and several gate failures that were red on the older base have since been repaired on the branch itself:

Gate class Repaired by
No new ESLint warnings (exit 2 — orphaned suppressions, not a lint violation) #8706, #8831
Fast Quality Gates / check:file-size / Quality Ratchet #8585, #8767, plus the post-merge-train re-pins
Docs Gates (fast-path) (missing Polish API_REFERENCE) #8831
backoff-clamp + compressionDetailNormalizers unit tests #8706

I verified this on a clean checkout of the current tip (3b515d90b3): lint:json --max-warnings 0, check:file-size, check:docs-sync, error-classification.test.ts (24/24) and check-db-rules.test.ts (22/22) all pass with no changes.

Currently failing here:

  • Build (advisory)
  • Docs Gates (fast-path)
  • No new ESLint warnings

Please update the branch (the Update branch button, or rebase onto origin/release/v3.8.49) and let CI re-run. That should clear any failure caused by the stale base.

Note this is not a promise that every red disappears — a failure specific to the changes in this PR will survive the rebase, and Build (advisory) is advisory and does not block. Rebasing just removes the noise so what is left is actually yours.

No action was taken on this PR beyond this comment.

@backryun
backryun force-pushed the agent/zai-web-browser-transport branch from a5c9d70 to ea00f97 Compare July 28, 2026 07:15
@backryun
backryun force-pushed the agent/zai-web-browser-transport branch 19 times, most recently from 18d5030 to 855bf9f Compare August 9, 2026 04:36
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Review fix pushed (non-streaming error surface).

Found during review: the silent-empty fix only covered the streaming path. collectZaiNonStreaming drained delta.error frames silently, so a stream:false request with an upstream error frame (rejected signature, expired captcha, stale token) returned a successful empty 200 instead of surfacing the failure.

Fix (single commit, author preserved, maintainer co-author):

  • collectZaiNonStreaming now throws on an error frame, matching the streaming path's [Z.ai error] convention; the caller's existing try/catch converts it to makeErrorResult(502, "Z.ai stream failed: …").
  • Two regression tests added (error frame → reject; happy path → content).

Validation: tests/unit/zai-web-silent-empty-repro.test.ts 9/9 pass. (executor-zai-web.test.ts load is blocked only by conolModels.ts, which is missing on the base — inherited base-red #9985, not this PR.)

Still needs the VPS browser smoke (real chat.z.ai token + CAPTCHA, Hard Rule #18) before merge.

backryun and others added 6 commits August 12, 2026 20:29
…th the chats/new + signed flow

Rebasing onto release/v3.8.49 pulled in cheaper-inference#8503, which repointed CHAT_URL to
/api/v2/chat/completions and added an endpoint probe. This branch already
targets v2, so the executor conflict resolved to this branch's superset
(NEW_CHAT_URL + signature constants alongside the same v2 CHAT_URL). The two
tests needed adapting, because cheaper-inference#8503's assertions assume the pre-rework flow:

- executor-zai-web.test.ts: the completion URL now carries the request
  signature as a query string, so an exact-equality check on the endpoint can
  never match. Assert the v2 prefix instead.
- zai-web-chat-endpoint-8014-probe.test.ts: the probe drove the executor with a
  bare cookie credential and no captcha proof, which now routes through the
  browser transport — fetch was never called and the probe captured nothing.
  Supplied a direct-path credential, and matched on pathname across all
  requests (the executor also probes the homepage for the frontend version and
  calls /api/v1/chats/new first).

The guard's intent is unchanged and slightly strengthened: it now asserts no
request reaches the stale unversioned path and that exactly one completions
request is issued, against v2.

54/54 across the zai suites; typecheck:core and eslint clean.
Reported on this PR: HTTP 200, `out=0`, stream "complete", no content and no
diagnosis.

Cause. HTTP-level failures are already handled — fetchUpstream turns any !ok
response into a makeErrorResult with the sanitized body. The gap is a 200 whose
SSE body carries an error payload: parseZaiFrame returns null for it,
drainSseDeltas drops it, and buildZaiStreamingBody then closes with an empty
assistant message + stop + [DONE]. The caller reads that as a successful empty
completion, so a rejected signature, an expired captcha and a stale token all
look identical — which is why this had to be diagnosed by reading code rather
than logs. Hard Rule #6.

Fix. parseZaiFrame now classifies an affirmatively error-shaped frame
(`error` at the top level or under `data`, string or {detail|message|msg}) as a
terminal delta, checked before the delta paths so it cannot fall through to the
"no usable delta" null. The stream emits it as `[Z.ai error] <message>`,
matching the mid-stream convention the other web executors already use
(zed-hosted's createErrorChunk) — the 200 is on the wire, so the status cannot
change, but the caller must not be left reading a blank success. Content
streamed before the failure is preserved. Message goes through
sanitizeErrorMessage (Rule cheaper-inference#12).

Deliberately NOT changed: a contentless frame still parses to null. That is
live-validated behaviour, not an oversight — z.ai emits phase frames with no
delta_content, and executor-zai-web.test.ts pins it ("returns null for frames
with no usable delta"). Treating "nothing parseable arrived" as a failure would
invent policy on top of an observed protocol and risk false errors on the happy
path, so this only adds recognition of explicit error frames.

Tests (TDD, RED then GREEN): zai-web-silent-empty-repro.test.ts — 7 cases.
Error frame classified and terminal; surfaced through the stream with the
upstream's own text; surfaced after partial content without losing it; plus a
REGRESSION GUARD that contentless/phase-only frames are still skipped, and two
controls that the happy path and reasoning-only output are untouched. The guard
and controls passed before the fix; the four error cases did not.

94/94 across the zai + stream suites; typecheck:core, eslint and check:file-size
clean.
…et holds

The v3.8.49 merge-train rebaseline (cheaper-inference#8686) set the ceiling to the tip's own
measurement, leaving zero headroom, so this branch's +5 cyclomatic / +3 cognitive
own-growth had nowhere to sit once rebased onto it.

Eight violations, all in code this branch introduces, resolved by extraction —
no behaviour change:

- `execute` (152 lines, complexity 25, cognitive 20) now delegates to
  `resolveZaiRequest()` for the four client-error rejections and to a
  `fetchViaSignedApi()` method for the CAPTCHA/signature path, so it reads as
  "validate, pick a transport, shape the response".
- `fetchThroughBrowser` (126 lines, cognitive 16) hands its image decoding to
  `resolveZaiBrowserAttachments()`, its Playwright options to
  `buildZaiBrowserChatOptions()`, and its call-log payload to
  `buildZaiBrowserAuditBody()`.
- `configureZaiBrowserEffort` (cognitive 35 — the worst of the set) repeated a
  wrap-and-relabel try/catch four times inside an if/else. `runStage`, which
  already existed one function below, is now module-scoped and reused, and the
  toggle collapses to `checked !== config.enabled` (same four cases).
- `validateWebCookieProvider` (complexity 19) moves its can-we-probe-this
  cascade into `resolveWebCookieProbe()`, which returns either a rejection or
  the URL + headers to use.
- `acquireBrowserContext`'s creation closure (complexity 17) hands cookie and
  localStorage seeding to `seedContextSession()`.

That last extraction also clears a violation that predates this branch —
`acquireBrowserContext` was already over the 80-line ceiling — so cyclomatic
lands at 2187 against a baseline of 2188.

Verified: check:complexity-ratchets green both metrics; typecheck:core clean;
ESLint clean on all four files; 85 tests across the zai-web, web-cookie
validation, browser-pool and model-test-runner suites pass.
collectZaiNonStreaming ignored delta.error — a 200 whose SSE body carries
an error frame (rejected signature, expired captcha, stale token) came
back as a successful empty completion. Now it throws on an error frame,
matching the streaming path's [Z.ai error] convention; the caller's
existing try/catch returns makeErrorResult(502) instead of an empty 200.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deferred-v3.8.50 Adiada para o ciclo v3.8.50 (validacao VPS, refactor, ou escopo grande)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants