[needs-vps] fix(notion-web): Chrome TLS impersonation for runInferenceTranscript - #8159
Merged
diegosouzapw merged 2 commits intoJul 23, 2026
Conversation
Node/undici fetch is rejected by Notion's edge with HTTP 200 temporarily-unavailable and empty assistant text (messages appear in the thread, UI shows 502 No response from Notion AI). The same cookie and body succeed via curl/Schannel and a browser Chrome JA3 handshake. Route inference through tls-client-node (chrome_146), matching Claude/ Perplexity web providers. Also detect nested patch-start error objects so operators see temporarily-unavailable instead of a misleading empty-body 502, and treat that subtype as retryable. Verified live: notion-web/fable-5, hyperagent/fable, and promptql/vertex-claude-fable-5 all return PONG through the packaged backend; unit tests 83/83.
Contributor
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Owner
|
Thanks, @artickc! 🙏 Merged — Chrome TLS/JA3 impersonation for the notion-web runInference path (new notionTlsClient). Merged on the strength of the unit suite (37/37 green + typecheck) — the live JA3-against-notion-edge check is pending a configured notion-web session on the VPS (none is set up right now), so if it ever gets blocked in the field, the fingerprint is the first place to look. The client itself is well-covered at the unit level. |
diegosouzapw
merged commit Jul 23, 2026
27f0ad0
into
diegosouzapw:release/v3.8.49
2 of 5 checks passed
diegosouzapw
added a commit
that referenced
this pull request
Jul 24, 2026
…ated usage-analytics file Root cause A (#8159): sendNotionInferenceRequest() in open-sse/executors/notion-web.ts was migrated from fetch() to tlsFetchNotion() (open-sse/services/notionTlsClient.ts, native tls-client-node binary) to get past Notion's Cloudflare TLS fingerprinting. #8159 updated the mock in the sibling tests/unit/executor-notion-web.test.ts (installNotionTlsMock, wired through __setTlsFetchOverrideForTesting) but never touched tests/unit/executor-notion-web-thread-sessions.test.ts (split out earlier by #7900) — its 3 execute()-driven tests still mocked globalThis.fetch, which tlsFetchNotion() never calls once the native TLS client loads successfully. Confirmed live: all 3 tests hit real https://app.notion.com with a fake cookie and got a real 401 (~8.1-8.5s each here; on a network with blocked/slow egress this would instead hang up to the client's ~190s timeout+grace per test — a CI-hang risk). Fix: replicate installNotionTlsMock verbatim from the sibling file into executor-notion-web-thread-sessions.test.ts so the 3 tests mock the TLS override point instead of global fetch. Suite is now fully hermetic — 8/8 pass, no network I/O, total runtime 31.0s -> 8.0s. Root cause B (#7700): tests/unit/usage-analytics-route-extra.test.ts was created as a byte-for-byte duplicate of 10 of the 22 tests in tests/unit/usage-analytics-route.test.ts. #7300 later fixed a fixture bug in the retention-window boundary test ("does not double-count raw and aggregated rows") in the main file only — reading getUserDatabaseSettings().retention.usageHistory live instead of a hardcoded 30-day cutoff (default retention is 365 days) — leaving the duplicate copy on the stale hardcoded value, which now fails (1 !== 2). Fix: delete the duplicate file. All 10 of its test names exist verbatim in the main file (verified with comm -12) and that file passes 22/22: - does not double-count raw and aggregated rows - does not persist guessed API key attribution - does not throw Unknown named parameter on short range (needsAggregated=false) - does not throw Unknown named parameter with apiKey filter on long range - groups renamed API key usage by stable ID - includes activityMap for heatmap - includes cost by API key - omits global aggregates when filtering by API key - returns 500 on database errors - returns weeklyPattern for the costs dashboard No coverage loss — same production code, same assertions, one fewer redundant file. Validation: - RED executor-notion-web-thread-sessions.test.ts: 5 pass / 3 fail (401 !== 200, real network hit), 31.0s - RED usage-analytics-route-extra.test.ts: 9 pass / 1 fail (1 !== 2), 18.7s - GREEN executor-notion-web-thread-sessions.test.ts: 8/8 pass, 8.0s, hermetic (no network) - GREEN executor-notion-web.test.ts (sibling, untouched): 37/37 pass, byte-identical diff - GREEN usage-analytics-route.test.ts (untouched): 22/22 pass, byte-identical diff - npx eslint on the changed file: clean - npm run typecheck:core: clean (exit 0) Refs #8159 Refs #7300 Refs #7700
diegosouzapw
added a commit
that referenced
this pull request
Jul 24, 2026
…alytics suite (#8392) * test: hermetic TLS mock for notion thread-session suite + drop duplicated usage-analytics file Root cause A (#8159): sendNotionInferenceRequest() in open-sse/executors/notion-web.ts was migrated from fetch() to tlsFetchNotion() (open-sse/services/notionTlsClient.ts, native tls-client-node binary) to get past Notion's Cloudflare TLS fingerprinting. #8159 updated the mock in the sibling tests/unit/executor-notion-web.test.ts (installNotionTlsMock, wired through __setTlsFetchOverrideForTesting) but never touched tests/unit/executor-notion-web-thread-sessions.test.ts (split out earlier by #7900) — its 3 execute()-driven tests still mocked globalThis.fetch, which tlsFetchNotion() never calls once the native TLS client loads successfully. Confirmed live: all 3 tests hit real https://app.notion.com with a fake cookie and got a real 401 (~8.1-8.5s each here; on a network with blocked/slow egress this would instead hang up to the client's ~190s timeout+grace per test — a CI-hang risk). Fix: replicate installNotionTlsMock verbatim from the sibling file into executor-notion-web-thread-sessions.test.ts so the 3 tests mock the TLS override point instead of global fetch. Suite is now fully hermetic — 8/8 pass, no network I/O, total runtime 31.0s -> 8.0s. Root cause B (#7700): tests/unit/usage-analytics-route-extra.test.ts was created as a byte-for-byte duplicate of 10 of the 22 tests in tests/unit/usage-analytics-route.test.ts. #7300 later fixed a fixture bug in the retention-window boundary test ("does not double-count raw and aggregated rows") in the main file only — reading getUserDatabaseSettings().retention.usageHistory live instead of a hardcoded 30-day cutoff (default retention is 365 days) — leaving the duplicate copy on the stale hardcoded value, which now fails (1 !== 2). Fix: delete the duplicate file. All 10 of its test names exist verbatim in the main file (verified with comm -12) and that file passes 22/22: - does not double-count raw and aggregated rows - does not persist guessed API key attribution - does not throw Unknown named parameter on short range (needsAggregated=false) - does not throw Unknown named parameter with apiKey filter on long range - groups renamed API key usage by stable ID - includes activityMap for heatmap - includes cost by API key - omits global aggregates when filtering by API key - returns 500 on database errors - returns weeklyPattern for the costs dashboard No coverage loss — same production code, same assertions, one fewer redundant file. Validation: - RED executor-notion-web-thread-sessions.test.ts: 5 pass / 3 fail (401 !== 200, real network hit), 31.0s - RED usage-analytics-route-extra.test.ts: 9 pass / 1 fail (1 !== 2), 18.7s - GREEN executor-notion-web-thread-sessions.test.ts: 8/8 pass, 8.0s, hermetic (no network) - GREEN executor-notion-web.test.ts (sibling, untouched): 37/37 pass, byte-identical diff - GREEN usage-analytics-route.test.ts (untouched): 22/22 pass, byte-identical diff - npx eslint on the changed file: clean - npm run typecheck:core: clean (exit 0) Refs #8159 Refs #7300 Refs #7700 * chore(quality): register usage-analytics-route-extra deletion in test-masking allowlist check:test-masking hard-flags any deleted test file without a _deletedWithReplacement entry. The deletion is legitimate (100% duplicate suite, coverage retained verbatim in tests/unit/usage-analytics-route.test.ts) -- same registration pattern as the video-dashscope entry. Refs #7700 Refs #7300
Merged
HouMinXi
pushed a commit
to HouMinXi/OmniRoute
that referenced
this pull request
Aug 2, 2026
…ipt (diegosouzapw#8159) Node/undici fetch is rejected by Notion's edge with HTTP 200 temporarily-unavailable and empty assistant text (messages appear in the thread, UI shows 502 No response from Notion AI). The same cookie and body succeed via curl/Schannel and a browser Chrome JA3 handshake. Route inference through tls-client-node (chrome_146), matching Claude/ Perplexity web providers. Also detect nested patch-start error objects so operators see temporarily-unavailable instead of a misleading empty-body 502, and treat that subtype as retryable. Verified live: notion-web/fable-5, hyperagent/fable, and promptql/vertex-claude-fable-5 all return PONG through the packaged backend; unit tests 83/83. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
HouMinXi
pushed a commit
to HouMinXi/OmniRoute
that referenced
this pull request
Aug 2, 2026
…alytics suite (diegosouzapw#8392) * test: hermetic TLS mock for notion thread-session suite + drop duplicated usage-analytics file Root cause A (diegosouzapw#8159): sendNotionInferenceRequest() in open-sse/executors/notion-web.ts was migrated from fetch() to tlsFetchNotion() (open-sse/services/notionTlsClient.ts, native tls-client-node binary) to get past Notion's Cloudflare TLS fingerprinting. diegosouzapw#8159 updated the mock in the sibling tests/unit/executor-notion-web.test.ts (installNotionTlsMock, wired through __setTlsFetchOverrideForTesting) but never touched tests/unit/executor-notion-web-thread-sessions.test.ts (split out earlier by diegosouzapw#7900) — its 3 execute()-driven tests still mocked globalThis.fetch, which tlsFetchNotion() never calls once the native TLS client loads successfully. Confirmed live: all 3 tests hit real https://app.notion.com with a fake cookie and got a real 401 (~8.1-8.5s each here; on a network with blocked/slow egress this would instead hang up to the client's ~190s timeout+grace per test — a CI-hang risk). Fix: replicate installNotionTlsMock verbatim from the sibling file into executor-notion-web-thread-sessions.test.ts so the 3 tests mock the TLS override point instead of global fetch. Suite is now fully hermetic — 8/8 pass, no network I/O, total runtime 31.0s -> 8.0s. Root cause B (diegosouzapw#7700): tests/unit/usage-analytics-route-extra.test.ts was created as a byte-for-byte duplicate of 10 of the 22 tests in tests/unit/usage-analytics-route.test.ts. diegosouzapw#7300 later fixed a fixture bug in the retention-window boundary test ("does not double-count raw and aggregated rows") in the main file only — reading getUserDatabaseSettings().retention.usageHistory live instead of a hardcoded 30-day cutoff (default retention is 365 days) — leaving the duplicate copy on the stale hardcoded value, which now fails (1 !== 2). Fix: delete the duplicate file. All 10 of its test names exist verbatim in the main file (verified with comm -12) and that file passes 22/22: - does not double-count raw and aggregated rows - does not persist guessed API key attribution - does not throw Unknown named parameter on short range (needsAggregated=false) - does not throw Unknown named parameter with apiKey filter on long range - groups renamed API key usage by stable ID - includes activityMap for heatmap - includes cost by API key - omits global aggregates when filtering by API key - returns 500 on database errors - returns weeklyPattern for the costs dashboard No coverage loss — same production code, same assertions, one fewer redundant file. Validation: - RED executor-notion-web-thread-sessions.test.ts: 5 pass / 3 fail (401 !== 200, real network hit), 31.0s - RED usage-analytics-route-extra.test.ts: 9 pass / 1 fail (1 !== 2), 18.7s - GREEN executor-notion-web-thread-sessions.test.ts: 8/8 pass, 8.0s, hermetic (no network) - GREEN executor-notion-web.test.ts (sibling, untouched): 37/37 pass, byte-identical diff - GREEN usage-analytics-route.test.ts (untouched): 22/22 pass, byte-identical diff - npx eslint on the changed file: clean - npm run typecheck:core: clean (exit 0) Refs diegosouzapw#8159 Refs diegosouzapw#7300 Refs diegosouzapw#7700 * chore(quality): register usage-analytics-route-extra deletion in test-masking allowlist check:test-masking hard-flags any deleted test file without a _deletedWithReplacement entry. The deletion is legitimate (100% duplicate suite, coverage retained verbatim in tests/unit/usage-analytics-route.test.ts) -- same registration pattern as the video-dashscope entry. Refs diegosouzapw#7700 Refs diegosouzapw#7300
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…ipt (diegosouzapw#8159) Node/undici fetch is rejected by Notion's edge with HTTP 200 temporarily-unavailable and empty assistant text (messages appear in the thread, UI shows 502 No response from Notion AI). The same cookie and body succeed via curl/Schannel and a browser Chrome JA3 handshake. Route inference through tls-client-node (chrome_146), matching Claude/ Perplexity web providers. Also detect nested patch-start error objects so operators see temporarily-unavailable instead of a misleading empty-body 502, and treat that subtype as retryable. Verified live: notion-web/fable-5, hyperagent/fable, and promptql/vertex-claude-fable-5 all return PONG through the packaged backend; unit tests 83/83. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…alytics suite (diegosouzapw#8392) * test: hermetic TLS mock for notion thread-session suite + drop duplicated usage-analytics file Root cause A (diegosouzapw#8159): sendNotionInferenceRequest() in open-sse/executors/notion-web.ts was migrated from fetch() to tlsFetchNotion() (open-sse/services/notionTlsClient.ts, native tls-client-node binary) to get past Notion's Cloudflare TLS fingerprinting. diegosouzapw#8159 updated the mock in the sibling tests/unit/executor-notion-web.test.ts (installNotionTlsMock, wired through __setTlsFetchOverrideForTesting) but never touched tests/unit/executor-notion-web-thread-sessions.test.ts (split out earlier by diegosouzapw#7900) — its 3 execute()-driven tests still mocked globalThis.fetch, which tlsFetchNotion() never calls once the native TLS client loads successfully. Confirmed live: all 3 tests hit real https://app.notion.com with a fake cookie and got a real 401 (~8.1-8.5s each here; on a network with blocked/slow egress this would instead hang up to the client's ~190s timeout+grace per test — a CI-hang risk). Fix: replicate installNotionTlsMock verbatim from the sibling file into executor-notion-web-thread-sessions.test.ts so the 3 tests mock the TLS override point instead of global fetch. Suite is now fully hermetic — 8/8 pass, no network I/O, total runtime 31.0s -> 8.0s. Root cause B (diegosouzapw#7700): tests/unit/usage-analytics-route-extra.test.ts was created as a byte-for-byte duplicate of 10 of the 22 tests in tests/unit/usage-analytics-route.test.ts. diegosouzapw#7300 later fixed a fixture bug in the retention-window boundary test ("does not double-count raw and aggregated rows") in the main file only — reading getUserDatabaseSettings().retention.usageHistory live instead of a hardcoded 30-day cutoff (default retention is 365 days) — leaving the duplicate copy on the stale hardcoded value, which now fails (1 !== 2). Fix: delete the duplicate file. All 10 of its test names exist verbatim in the main file (verified with comm -12) and that file passes 22/22: - does not double-count raw and aggregated rows - does not persist guessed API key attribution - does not throw Unknown named parameter on short range (needsAggregated=false) - does not throw Unknown named parameter with apiKey filter on long range - groups renamed API key usage by stable ID - includes activityMap for heatmap - includes cost by API key - omits global aggregates when filtering by API key - returns 500 on database errors - returns weeklyPattern for the costs dashboard No coverage loss — same production code, same assertions, one fewer redundant file. Validation: - RED executor-notion-web-thread-sessions.test.ts: 5 pass / 3 fail (401 !== 200, real network hit), 31.0s - RED usage-analytics-route-extra.test.ts: 9 pass / 1 fail (1 !== 2), 18.7s - GREEN executor-notion-web-thread-sessions.test.ts: 8/8 pass, 8.0s, hermetic (no network) - GREEN executor-notion-web.test.ts (sibling, untouched): 37/37 pass, byte-identical diff - GREEN usage-analytics-route.test.ts (untouched): 22/22 pass, byte-identical diff - npx eslint on the changed file: clean - npm run typecheck:core: clean (exit 0) Refs diegosouzapw#8159 Refs diegosouzapw#7300 Refs diegosouzapw#7700 * chore(quality): register usage-analytics-route-extra deletion in test-masking allowlist check:test-masking hard-flags any deleted test file without a _deletedWithReplacement entry. The deletion is legitimate (100% duplicate suite, coverage retained verbatim in tests/unit/usage-analytics-route.test.ts) -- same registration pattern as the video-dashscope entry. Refs diegosouzapw#7700 Refs diegosouzapw#7300
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
temporarily-unavailableand no assistant text for Node/undici clients (user message still lands in the Notion thread → VibeProxy surfaces502 No response from Notion AI).runInferenceTranscriptthroughtls-client-node(chrome_146), same pattern as Claude/Perplexity web providers.patch-start.data.s[]errors so operators see the real subtype instead of an empty-body 502; treattemporarily-unavailableas retryable.Test plan
tlsFetchNotion+parseNotionInferenceStream→ PONG with productiontoken_v2notion-web/fable-5→ PONGhyperagent/fable→ PONG (executor already on release; missing from older packaged build)promptql/vertex-claude-fable-5→ PONG (fresh enrich-token JWT)executor-notion-web+ hyperagent + promptql 83/83Notes
HyperAgent (#7994) and PromptQL (#7911) were already merged to
release/v3.8.49. Failures in the field were from an older packagedVibeProxyServices.exethat fell through toDefaultExecutor(OpenAI 401 / GraphQL issuer 401). Repackage ships the registered executors.