Repository navigation
fix(core): declare the evaluation runtime on /flags requests - #4885
Conversation
`/flags` filters flags by `evaluation_runtime`, but the SDK never said which runtime it is, so the service had to infer one from request headers. That inference is best-effort by design; it is why the service grew an explicit `evaluation_runtime` request field. For this SDK the inference is one header wide. `detect_evaluation_runtime_from_request` reads the User-Agent first and otherwise falls back to browser signals such as `sec-fetch-mode`, which Node's global fetch sets on every request. A User-Agent that a proxy, gateway, or edge runtime drops or rewrites therefore does not land in the undetermined branch, it resolves to the client runtime, and the server-side flags are dropped from an otherwise successful response. Flags marked `all`, and flags carrying no runtime, are returned either way. Add `getEvaluationRuntime()` to `PostHogCoreStateless`, defaulting to undefined so browser, react-native, and web clients keep relying on the server's inference, and override it in `PostHogBackendClient` to declare `server`. `getFlags()` sends the field when it is defined. The historical `FlagRequest` predating that field does not deny unknown fields, so older deployments ignore it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 15dad7057a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
15dad70 to
4c0978c
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4c0978c6c1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| * does not depend on that header surviving proxies, gateways, or edge runtimes. | ||
| */ | ||
| protected override getEvaluationRuntime(): 'server' { | ||
| return 'server' |
There was a problem hiding this comment.
Filter local definitions by the declared runtime
When local evaluation is enabled and /flags/definitions contains a flag with evaluation_runtime: "client", this override affects only remote /flags requests; FeatureFlagsPoller.updateFlagState() still filters definitions solely by evaluation context at packages/node/src/extensions/feature-flags/feature-flags.ts:674-676. Consequently, a client-only flag is returned when it can be computed locally but disappears when the same lookup requires server fallback, making backend behavior and experiment exposure depend on local evaluability. Pass the server runtime into the poller and exclude client-only definitions there as well.
Useful? React with 👍 / 👎.
|
Both automated review comments are correct, and together they describe why the local-evaluation half is not in this PR. The first one flagged that filtering client-runtime flags in the poller removes them from the Both hold. The filter itself is right, what it needs is for the poller to surface which keys it excluded, so the bootstrap can tell an incomplete result from a complete one instead of quietly shipping a short flag set to the browser. That is new public surface and its shape is yours to choose, so I left it out rather than pick one. What is left here is the wire half only: the SDK states its runtime instead of letting the service infer it from headers. It is additive, independent of the local path, and I am happy to build the local half once that decision exists. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a1df460f42
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| '@posthog/core': patch | ||
| --- | ||
|
|
||
| Declare `evaluation_runtime: "server"` on `/flags` requests from posthog-node and posthog-edge, so the server filters flags by a stated runtime instead of inferring one from the `User-Agent` and browser-ish headers. |
There was a problem hiding this comment.
Rewrite the changeset as a user-facing fix
When this changeset is published to both package changelogs, users will see internal wire-format details (evaluation_runtime, /flags, and header inference) rather than the behavior being fixed. RELEASING.md:17 requires a short, user-facing description that states the fix without implementation details; describe the restored reliability of server-side feature flags instead.
AGENTS.md reference: AGENTS.md:L4-L4
Useful? React with 👍 / 👎.
JamesPatrickGill
left a comment
There was a problem hiding this comment.
Checked the mechanism against the flags service rather than the PR body. The two risks both hold up: the undefined default leaves browser/web/react-native byte-identical (the untouched exact-body assertion at packages/core/src/__tests__/posthog.featureflags.spec.ts:322 is the proof), and older deployments do ignore the field — FlagRequest before #39298 has no deny_unknown_fields.
One ask inline.
Not for this PR: the local-evaluation poller ignores evaluation_runtime, so a key can resolve differently locally vs /flags. And @posthog/next bootstraps browser flags through the node client, so client-runtime flags never reach the browser. Both pre-existing — the service already inferred server from an intact User-Agent.
| '@posthog/core': patch | ||
| --- | ||
|
|
||
| Declare `evaluation_runtime: "server"` on `/flags` requests from posthog-node and posthog-edge, so the server filters flags by a stated runtime instead of inferring one from the `User-Agent` and browser-ish headers. |
There was a problem hiding this comment.
Worth a clause on what users lose. A server SDK behind a proxy that rewrites the User-Agent gets client-runtime flags today. After this it does not, and they read as undefined. Right outcome, but it is a silent value change and this line only mentions the gain.
There was a problem hiding this comment.
Added, thanks. The line now names the client-runtime flags that stop resolving alongside the fix.
|
I reproduced the cases James mentioned to separate the behavior changed by this PR from the existing local-evaluation/bootstrap issues. Before vs after this PRAll fixture flags are enabled for the test user. “Before” below emulates the pre-PR request body by making
Flags configured for all runtimes remain available in both proxy cases. Takeaways: James’s requested changelog warning is warranted: affected proxy configurations can lose previously returned client-only values ( Reproduction and limitsRan eight passing characterization tests using the built Node SDK, actual HTTP requests to a local fixture server, and the actual Next.js server Command: This was not a live PostHog service test, and the before comparison emulates the old wire behavior rather than running a separate historical build. For Next.js, the tests verify the bootstrap contents and first-load-disable option; the browser consequence was checked against |
## Dependency Updates | Package | From | To | Type | | --- | --- | --- | --- | | `@babel/core` | 8.0.1 | 8.0.5 | patch | | `@effect/platform-node` | 4.0.0-rc.112 | 4.0.0-rc.115 | prerelease | | `@effect/sql-d1` | 4.0.0-rc.112 | 4.0.0-rc.115 | prerelease | | `@effect/vitest` | 4.0.0-rc.112 | 4.0.0-rc.115 | prerelease | | `@inlang/paraglide-js` | 2.25.0 | 2.25.4 | patch | | `@react-email/ui` | 6.9.3 | 6.9.5 | patch | | `@rolldown/plugin-babel` | 0.2.3 | 0.2.4 | patch | | `@tanstack/react-query` | 5.103.0 | 5.103.1 | patch | | `@tanstack/react-router` | 1.170.32 | 1.170.38 | patch | | `@tanstack/react-router-devtools` | 1.167.1 | 1.167.2 | patch | | `@tanstack/react-router-ssr-query` | 1.167.2 | 1.167.3 | patch | | `@tanstack/react-start` | 1.168.49 | 1.168.56 | patch | | `@testing-library/dom` | 10.4.1 | 10.4.2 | patch | | `ai` | 7.0.101 | 7.0.106 | patch | | `effect` | 4.0.0-rc.112 | 4.0.0-rc.115 | prerelease | | `effectful-better-auth` | 1.0.0 | 1.0.2 | patch | | `jose` | 6.2.10 | 6.2.12 | patch | | `posthog-node` | 5.52.1 | 5.52.4 | patch | | `react-email` | 6.9.3 | 6.9.5 | patch | | `ws` | 8.21.0 | 8.21.3 | patch | ## Release Notes <details> <summary><b>@<!---->babel/core</b> (8.0.1 → 8.0.5)</summary> ## v8.0.5 (2026-09-10) Thanks @<!---->drubetti, @<!---->jibin7jose, @<!---->joelle-a-dev, @<!---->journey-ad, @<!---->Kjubikstronk, @<!---->MatteoGabriele, and @<!---->zhangli091011 for your first PRs! #### 👓 Spec Compliance * `babel-parser` * [#18218](babel/babel#18218) fix(parser): do not form html entity for invalid codepoint ([@<!---->JLHwung](https://github.com/JLHwung)) #### 🐛 Bug Fix * `babel-parser` * [#18215](babel/babel#18215) fix(parser): remove non-decimal prefix from bigint ([@<!---->JLHwung](https://github.com/JLHwung)) * [#18063](babel/babel#18063) Disallow new import prop access ([@<!---->JLHwung](https://github.com/JLHwung)) * `babel-plugin-transform-destructuring` * [#18214](babel/babel#18214) fix: only collapse the empty-object check when it targets the declared binding ([@<!---->Kjubikstronk](https://github.com/Kjubikstronk)) * `babel-plugin-transform-typescript` * [#18190](babel/babel#18190) fix(ts): improve variable handling in namespace ([@<!---->JLHwung](https://github.com/JLHwung)) * [#18207](babel/babel#18207) [ts] Allow merging `enum` into a `namespace` ([@<!---->nicolo-ribaudo](https://github.com/nicolo-ribaudo)) * `babel-node` * [#18217](babel/babel#18217) Fix import path for babel-node.js ([@<!---->drubetti](https://github.com/drubetti)) * `babel-plugin-bugfix-safari-rest-destructuring-rhs-array` * [#18213](babel/babel#18213) fix(bugfix): bound check before array access ([@<!---->JLHwung](https://github.com/JLHwung)) * `babel-traverse` * [#18183](babel/babel#18183) fix(remove): unwrap single sequence expression ([@<!---->JLHwung](https://github.com/JLHwung)) * [#18153](babel/babel#18153) Handle null in `getAll{Prev,Next}Siblings` ([@<!---->JLHwung](https://github.com/JLHwung)) * `babel-plugin-transform-block-scoping` * [#18194](https://github.com/babel/b …[full notes](https://github.com/babel/babel/releases/tag/v8.0.5) </details> <details> <summary><b>@<!---->effect/platform-node</b> (4.0.0-rc.112 → 4.0.0-rc.115) — 2 releases</summary> <details> <summary><b>4.0.0-rc.115</b></summary> ### Patch Changes - Updated dependencies [[`657254b`](Effect-TS/effect@657254b), [`f9ef0e9`](Effect-TS/effect@f9ef0e9), [`4f73f9e`](Effect-TS/effect@4f73f9e)]: - effect@4.0.0-rc.115 - @<!---->effect/platform-node-shared@4.0.0-rc.115 </details> <details> <summary><b>4.0.0-rc.114</b></summary> ### Patch Changes - Updated dependencies [[`3ff4952`](Effect-TS/effect@3ff4952), [`6d55555`](Effect-TS/effect@6d55555), [`716e0c0`](Effect-TS/effect@716e0c0), [`d4e4ad5`](Effect-TS/effect@d4e4ad5), [`b1988f4`](Effect-TS/effect@b1988f4), [`482b7d7`](Effect-TS/effect@482b7d7), [`716e0c0`](Effect-TS/effect@716e0c0), [`9941e6d`](Effect-TS/effect@9941e6d)]: - effect@4.0.0-rc.114 - @<!---->effect/platform-node-shared@4.0.0-rc.114 </details> </details> <details> <summary><b>@<!---->effect/sql-d1</b> (4.0.0-rc.112 → 4.0.0-rc.115) — 2 releases</summary> <details> <summary><b>4.0.0-rc.115</b></summary> ### Patch Changes - Updated dependencies [[`657254b`](Effect-TS/effect@657254b), [`f9ef0e9`](Effect-TS/effect@f9ef0e9), [`4f73f9e`](Effect-TS/effect@4f73f9e)]: - effect@4.0.0-rc.115 </details> <details> <summary><b>4.0.0-rc.114</b></summary> ### Patch Changes - Updated dependencies [[`3ff4952`](Effect-TS/effect@3ff4952), [`6d55555`](Effect-TS/effect@6d55555), [`716e0c0`](Effect-TS/effect@716e0c0), [`d4e4ad5`](Effect-TS/effect@d4e4ad5), [`b1988f4`](Effect-TS/effect@b1988f4), [`482b7d7`](Effect-TS/effect@482b7d7), [`716e0c0`](Effect-TS/effect@716e0c0), [`9941e6d`](Effect-TS/effect@9941e6d)]: - effect@4.0.0-rc.114 </details> </details> <details> <summary><b>@<!---->effect/vitest</b> (4.0.0-rc.112 → 4.0.0-rc.115) — 2 releases</summary> <details> <summary><b>4.0.0-rc.115</b></summary> ### Patch Changes - Updated dependencies [[`657254b`](Effect-TS/effect@657254b), [`f9ef0e9`](Effect-TS/effect@f9ef0e9), [`4f73f9e`](Effect-TS/effect@4f73f9e)]: - effect@4.0.0-rc.115 </details> <details> <summary><b>4.0.0-rc.114</b></summary> ### Patch Changes - Updated dependencies [[`3ff4952`](Effect-TS/effect@3ff4952), [`6d55555`](Effect-TS/effect@6d55555), [`716e0c0`](Effect-TS/effect@716e0c0), [`d4e4ad5`](Effect-TS/effect@d4e4ad5), [`b1988f4`](Effect-TS/effect@b1988f4), [`482b7d7`](Effect-TS/effect@482b7d7), [`716e0c0`](Effect-TS/effect@716e0c0), [`9941e6d`](Effect-TS/effect@9941e6d)]: - effect@4.0.0-rc.114 </details> </details> <details> <summary><b>@<!---->tanstack/react-query</b> (5.103.0 → 5.103.1)</summary> ### Patch Changes - Updated dependencies \[[`8330b2f`](TanStack/query@8330b2f), [`3212966`](TanStack/query@3212966)]: - @<!---->tanstack/query-core@5.103.1 </details> <details> <summary><b>@<!---->tanstack/react-router</b> (1.170.32 → 1.170.38) — 3 releases</summary> <details> <summary><b>1.170.38</b></summary> ### Patch Changes - Updated dependencies \[[`cecae54`](TanStack/router@cecae54), [`0103578`](TanStack/router@0103578), [`ce10dcd`](TanStack/router@ce10dcd), [`84936cc`](TanStack/router@84936cc), [`bbd2336`](TanStack/router@bbd2336)]: - @<!---->tanstack/router-core@1.171.32 </details> <details> <summary><b>1.170.37</b></summary> ### Patch Changes - [#8418](TanStack/router#8418) [`e561fa1`](TanStack/router@e561fa1) - `deepEqual` now takes its flags as positional arguments — `deepEqual(a, b, partial?, explicitUndefined?)` — instead of an options object. The router's hot callers (Link option stabilization and active-state checks, `matchRoute`) no longer allocate an options object per comparison, and the comparator reads two booleans instead of a polymorphic object. `explicitUndefined` replaces `ignoreUndefined: false`. `deepEqual` is an internal helper; it stays exported for compatibility of two-argument calls. - [#8419](TanStack/router#8419) [`a1c8d1a`](TanStack/router@a1c8d1a) - `resolvePath` (internal helper) now takes positional arguments — `resolvePath(base, to, trailingSlash?, cache?)` — so `buildLocation` and `matchRoute` no longer allocate an options object per path resolution. - [#8204](TanStack/router#8204) [`cbbfbe3`](TanStack/router@cbbfbe3) - Stream large deferred SSR hydration payloads through a backpressure-aware router transport, fail known setup errors before response creation, and close cancelled or expired transforms safely. Start now cancels discarded middleware and HEAD response bodies, including plain streams and derived branches. Server-function raw streams share one ordered response. Arbitrary or sequential consumption can require potentially unbounded buffering of unread data on the client. Cancelling one raw stream discards it locally, while aborting the whole call cancels the response and server work. Consume streams concurrently, cancel unused streams promptly, or use separate calls when independent backpressure is required. A raw stream that exceeds its unread-byte limit now fails alone; sibling stream …[full notes](https://github.com/TanStack/router/releases/tag/%40tanstack/react-router%401.170.37) </details> <details> <summary><b>1.170.36</b></summary> ### Patch Changes - [#8390](TanStack/router#8390) [`b747fb8`](TanStack/router@b747fb8) - Keep the Link location cache out of server bundles: `buildLocation` only creates, reads and writes it when `isServer` is false. Render React Links on the server without the extra prop copies and the forwarded-ref hook. Link SSR rendering is 20-40% faster in the Link benchmarks and the React Start SSR request loop about 7% faster. React `activeProps` and `inactiveProps` now follow one precedence rule on every link, including links whose destination is blocked for using a disallowed scheme: state props override element props, `ref` and event handlers, while `href`, `disabled` and `target` stay controlled by the router. Previously a blocked link ignored a `ref` or handler from its inactive props. React `Link` and `useLinkProps` split router options from element props with one key set on the client and the server. Element props pass through as given: external links forward them verbatim, falsy values included, and `useLinkProps` now returns `children` for router-controlled links as it already did for external ones. - [#8324](TanStack/router#8324) [`6387d58`](TanStack/router@6387d58) - Reuse hydration snapshot getters to avoid unnecessary store-instance effect updates when Links and other hydration-aware components rerender. - [#8318](TanStack/router#8318) [`9b2adaf`](TanStack/router@9b2adaf) - Allow active and inactive Link props to override base element props in React and Solid while preserving class/style merging. Keep React's `href`, `target`, and `disabled` values controlled by routing options. Preserve Vue object and nested-array class bindings, including reactive updates and server rendering, without mutating cach …[full notes](https://github.com/TanStack/router/releases/tag/%40tanstack/react-router%401.170.36) </details> </details> <details> <summary><b>@<!---->tanstack/react-router-devtools</b> (1.167.1 → 1.167.2)</summary> ### Patch Changes - Updated dependencies \[[`d76a332`](TanStack/router@d76a332), [`b747fb8`](TanStack/router@b747fb8), [`6cfb1e8`](TanStack/router@6cfb1e8), [`700a714`](TanStack/router@700a714), [`700a714`](TanStack/router@700a714), [`6387d58`](TanStack/router@6387d58), [`7e349c3`](TanStack/router@7e349c3), [`9b2adaf`](TanStack/router@9b2adaf), [`873c830`](TanStack/router@873c830), [`7e349c3`](TanStack/router@7e349c3), [`634da91`](TanStack/router@634da91), [`e9396c9`](TanStack/router@e9396c9), [`634da91`](TanStack/router@634da91), [`f151ab0`](TanStack/router@f151ab0), [`bc57fa3`](TanStack/router@bc57fa3), [`6387d58`](TanStack/router@6387d58), [`9872d2a`](TanStack/router@9872d2a), [`d76a332`](TanStack/router@d76a332), [`634da91`](TanStack/router@634da91), [`634da91`](TanStack/router@634da91), [`7e349c3`](ht …[full notes](https://github.com/TanStack/router/releases/tag/%40tanstack/react-router-devtools%401.167.2) </details> <details> <summary><b>@<!---->tanstack/react-router-ssr-query</b> (1.167.2 → 1.167.3)</summary> ### Patch Changes - [#8204](TanStack/router#8204) [`cbbfbe3`](TanStack/router@cbbfbe3) - Stream large deferred SSR hydration payloads through a backpressure-aware router transport, fail known setup errors before response creation, and close cancelled or expired transforms safely. Start now cancels discarded middleware and HEAD response bodies, including plain streams and derived branches. Server-function raw streams share one ordered response. Arbitrary or sequential consumption can require potentially unbounded buffering of unread data on the client. Cancelling one raw stream discards it locally, while aborting the whole call cancels the response and server work. Consume streams concurrently, cancel unused streams promptly, or use separate calls when independent backpressure is required. A raw stream that exceeds its unread-byte limit now fails alone; sibling streams and the JSON result keep flowing. The JSON wire shape of a `RawStream` server-function argument changed. Clients and servers must run matching versions for requests that pass a `RawStream`. The frame-protocol constants (`FRAME_TYPE_*`, `MAX_FRAME_PAYLOAD_SIZE`, `MAX_FRAMED_STREAMS`) moved from the `@tanstack/start-client-core` root to the `@tanstack/start-client-core/client-rpc` subpath. Router requests whose `Accept` header allows neither `text/html` nor `*/*` now receive `406 Not Acceptable` instead of `500`. Framework adapters share the body `<Scripts>` composition (`getSsrBodyScriptParts`, `composeSsrBodyScripts`) and the eager HTML response wrapper (`renderSsrHtmlResponse`) from `@tanstack/router-core`. Solid SSR now emits one document type and renders late lazy errors through route boundaries. A Solid `<Await>` without a `fallback` no longer holds the streamed shell; it renders inside the nearest `<Suspense>` boundary like React and Vue, and now renders falsy resolved values. …[full notes](https://github.com/TanStack/router/releases/tag/%40tanstack/react-router-ssr-query%401.167.3) </details> <details> <summary><b>@<!---->tanstack/react-start</b> (1.168.49 → 1.168.56) — 4 releases</summary> <details> <summary><b>1.168.56</b></summary> ### Patch Changes - Updated dependencies \[]: - @<!---->tanstack/react-router@1.170.38 - @<!---->tanstack/react-start-client@1.168.36 - @<!---->tanstack/react-start-rsc@0.1.55 - @<!---->tanstack/react-start-server@1.167.43 - @<!---->tanstack/start-client-core@1.170.32 - @<!---->tanstack/start-plugin-core@1.171.46 - @<!---->tanstack/start-server-core@1.169.37 </details> <details> <summary><b>1.168.55</b></summary> ### Patch Changes - Updated dependencies \[[`e561fa1`](TanStack/router@e561fa1), [`cbbfbe3`](TanStack/router@cbbfbe3), [`a1c8d1a`](TanStack/router@a1c8d1a), [`cbbfbe3`](TanStack/router@cbbfbe3), [`8e164d2`](TanStack/router@8e164d2)]: - @<!---->tanstack/react-router@1.170.37 - @<!---->tanstack/start-plugin-core@1.171.45 - @<!---->tanstack/start-client-core@1.170.31 - @<!---->tanstack/start-server-core@1.169.36 - @<!---->tanstack/react-start-client@1.168.35 - @<!---->tanstack/react-start-rsc@0.1.54 - @<!---->tanstack/react-start-server@1.167.42 </details> <details> <summary><b>1.168.54</b></summary> ### Patch Changes - Updated dependencies \[[`ab99818`](TanStack/router@ab99818)]: - @<!---->tanstack/start-plugin-core@1.171.44 - @<!---->tanstack/react-start-rsc@0.1.53 </details> <details> <summary><b>1.168.53</b></summary> ### Patch Changes - Updated dependencies \[[`b747fb8`](TanStack/router@b747fb8), [`6387d58`](TanStack/router@6387d58), [`9b2adaf`](TanStack/router@9b2adaf), [`634da91`](TanStack/router@634da91), [`e9396c9`](TanStack/router@e9396c9), [`6387d58`](TanStack/router@6387d58), [`9872d2a`](TanStack/router@9872d2a), [`7e349c3`](TanStack/router@7e349c3), [`e9396c9`](TanStack/router@e9396c9)]: - @<!---->tanstack/react-router@1.170.36 - @<!---->tanstack/start-server-core@1.169.35 - @<!---->tanstack/react-start-client@1.168.34 - @<!---->tanstack/react-start-rsc@0.1.52 - @<!---->tanstack/react-start-server@1.167.41 - @<!---->tanstack/start-client-core@1.170.30 - @<!---->tanstack/start-plugin-core@1.171.43 </details> </details> <details> <summary><b>@<!---->testing-library/dom</b> (10.4.1 → 10.4.2)</summary> ## [10.4.2](testing-library/dom-testing-library@v10.4.1...v10.4.2) (2026-09-13) ### Bug Fixes * **deps:** pin @<!---->types/node to a TypeScript 4-compatible version ([#1386](testing-library/dom-testing-library#1386)) ([6049cc0](testing-library/dom-testing-library@6049cc0)) </details> <details> <summary><b>ai</b> (7.0.101 → 7.0.106)</summary> ### Patch Changes - 4775577: fix(ai): preserve provider metadata when simulating text streams - 6696728: fix(ai): report the prepareStep model in streamed step results - 09516a1: fix(ai): prevent unhandled rejections when UI message stream reading stops early - 1aef01e: fix(ai): preserve prototype-named properties in serialized tool outputs - 9c1ea74: fix(ai): close telemetry spans when provider response streams fail - 107343a: fix(ai): use the prepareStep-selected model for streamed response metadata fallbacks - 03c3e33: fix(ai): preserve tool calls required by retained pending approvals - 5d42ebd: fix(ai): skip input available callbacks for invalid streamed tool calls - 4a67783: fix(ai): cancel prompt attachment downloads when model calls are aborted or time out - 1058ed5: fix(ai): strip streamed JSON fences before arbitrary trailing whitespace - 84f5d1b: fix(ai): stream null and empty string JSON partial outputs - 2d53a5d: fix(ai): prevent onEnd after aborting a multi-step text stream - 2a5ed55: fix(ai): stream structured output from the final tool-loop step - Updated dependencies [4fdf51e] - Updated dependencies [0455398] - @<!---->ai-sdk/gateway@4.0.86 - @<!---->ai-sdk/provider-utils@5.0.44 </details> <details> <summary><b>effect</b> (4.0.0-rc.112 → 4.0.0-rc.115) — 2 releases</summary> <details> <summary><b>4.0.0-rc.115</b></summary> ### Patch Changes - [#8196](Effect-TS/effect#8196) [`657254b`](Effect-TS/effect@657254b) Thanks @<!---->gcanti! - Optimize schema initialization while preserving custom constructor options. - [#8190](Effect-TS/effect#8190) [`f9ef0e9`](Effect-TS/effect@f9ef0e9) Thanks @<!---->javascript-unsafe! - Omit response bodies for statuses 204, 205, and 304 in `HttpServerResponse.toWeb` and the Bun/Deno HTTP adapters, preventing invalid Web responses and hung requests. Cancel omitted raw `ReadableStream` bodies, and finalize request resources without starting omitted Effect streams. - [#8187](Effect-TS/effect#8187) [`4f73f9e`](Effect-TS/effect@4f73f9e) Thanks @<!---->tim-smart! - Parameterize persistence lookup keys in both SQL backing stores' `getMany` queries. </details> <details> <summary><b>4.0.0-rc.114</b></summary> ### Patch Changes - [#8177](Effect-TS/effect#8177) [`3ff4952`](Effect-TS/effect@3ff4952) Thanks @<!---->tim-smart! - Allow `Effect.cachedWithTTL` to compute the TTL from each completed `Exit`, so successes and failures can use different cache durations. - [#8164](Effect-TS/effect#8164) [`6d55555`](Effect-TS/effect@6d55555) Thanks @<!---->sam-goodwin! - Keep Node and Bun file stats usable when optional numeric metadata exceeds the safe integer range by returning `Option.none()` for those fields. - [#8162](Effect-TS/effect#8162) [`716e0c0`](Effect-TS/effect@716e0c0) Thanks @<!---->tim-smart! - Fix published declarations referencing symbols stripped as `@internal`, which broke consumers compiling with `skipLibCheck: false`. `Effectable.d.ts` now uses the public `Effect.TypeId`, `Match.d.ts` no longer aliases an internal `Contextual` type, `Schema.d.ts` ships the `AnnotationSchemaConstraint` alias it references, and the CLI's `toFlagDoc` helper is marked internal so it no longer leaks `Param.getParamMetadata`. - [#8160](Effect-TS/effect#8160) [`d4e4ad5`](Effect-TS/effect@d4e4ad5) Thanks @<!---->gcanti! - Fix `SchemaRepresentation.toCodeDocument` generating invalid TypeScript for optional tuple elements containing unions or nested readonly tuples. Optional element types are now parenthesized, for example `readonly [(string | number)?]` instead of `readonly [string | number?]`. Generated runtime schemas are unchanged. - [#8158](Effect-TS/effect#8158) [`b1988f4`](Effect-TS/effect@b1988f4) Thanks @<!---->gcanti! - Fix `SchemaRepresentation.toCodeDocument` dropping Struct fields named `__proto__` …[full notes](https://github.com/Effect-TS/effect/releases/tag/effect%404.0.0-rc.114) </details> </details> <details> <summary><b>jose</b> (6.2.10 → 6.2.12) — 2 releases</summary> <details> <summary><b>6.2.12</b></summary> ### Documentation * clarify and shorten public API guidance ([be62530](panva/jose@be62530)) ### Refactor * simplify JWS and JWE operation cores ([92e9640](panva/jose@92e9640)) ### Performance * avoid copying AES-GCM output ([6925d43](panva/jose@6925d43)) * deduplicate pending jwks key imports ([bf5138b](panva/jose@bf5138b)) * encode single-signature JWS input once ([7bc9a33](panva/jose@7bc9a33)) * normalize General JWE shared headers once ([78637bd](panva/jose@78637bd)) * normalize jwks selection metadata once ([fd3ae3f](panva/jose@fd3ae3f)) * use native encoding for larger ASCII strings ([b23a6f3](panva/jose@b23a6f3)) </details> <details> <summary><b>6.2.11</b></summary> ### Documentation * render subpath indexes as tables ([94589ee](panva/jose@94589ee)) * shorten API index descriptions ([681482f](panva/jose@681482f)) ### Refactor * model JWE key management modes ([e01dda6](panva/jose@e01dda6)) * **types:** reduce declaration repetition ([55b970f](panva/jose@55b970f)) </details> </details> <details> <summary><b>posthog-node</b> (5.52.1 → 5.52.4) — 3 releases</summary> <details> <summary><b>5.52.4</b></summary> ## 5.52.4 ### Patch Changes - [#4885](PostHog/posthog-js#4885) [`39a8980`](PostHog/posthog-js@39a8980) Thanks [@<!---->decknamec](https://github.com/decknamec)! - Server-side feature flags now resolve in posthog-node and posthog-edge even when a proxy rewrites the request's `User-Agent`. Flags restricted to the `client` runtime now resolve to `undefined` in these SDKs, where a rewritten `User-Agent` previously let them through. (2026-09-15) - Updated dependencies [[`39a8980`](PostHog/posthog-js@39a8980)]: - @<!---->posthog/core@1.54.2 </details> <details> <summary><b>5.52.3</b></summary> ## 5.52.3 ### Patch Changes - [#4941](PostHog/posthog-js#4941) [`07c1045`](PostHog/posthog-js@07c1045) Thanks [@<!---->marandaneto](https://github.com/marandaneto)! - Capture causes and AggregateError members with relationship metadata and individual stacks, limiting output to 50 entries and 1,000 member inspections. (2026-09-15) - Updated dependencies [[`07c1045`](PostHog/posthog-js@07c1045)]: - @<!---->posthog/core@1.54.1 </details> <details> <summary><b>5.52.2</b></summary> ## 5.52.2 ### Patch Changes - [#4951](PostHog/posthog-js#4951) [`55c5142`](PostHog/posthog-js@55c5142) Thanks [@<!---->marandaneto](https://github.com/marandaneto)! - Fix Express exception events reporting the initial response status instead of the final HTTP status. (2026-09-14) </details> </details> <details> <summary><b>react-email</b> (6.9.3 → 6.9.5)</summary> ### Patch Changes * 0250981: fix responsive padding not applying to inner td for Container and Section </details> <details> <summary><b>ws</b> (8.21.0 → 8.21.3) — 3 releases</summary> <details> <summary><b>8.21.3</b></summary> # Bug fixes - The server now correctly rejects permessage-deflate offers if the incoming `client_max_window_bits` parameter value is smaller than its configured `clientMaxWindowBits` (e97a20ea). </details> <details> <summary><b>8.21.2</b></summary> # Bug fixes - Fixed a test for [CITGM][] (2eb3be0b). [CITGM]: https://github.com/nodejs/citgm </details> <details> <summary><b>8.21.1</b></summary> # Bug fixes - Empty fragments are now counted toward the limit (a2f4e7c0). - The default values of the `maxBufferedChunks` and `maxFragments` options have been reduced (f197ac65). </details> </details> --- *This PR was auto-generated by [catalog-update-action](https://github.com/brandhaug/catalog-update-action).* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Problem
/flagsresponse. The key is simply absent, sogetAllFlags()omits it andgetFeatureFlag()returnsundefined. That is distinguishable from an explicitfalse, but the response carries nothing that says the key was filtered out rather than left unevaluated./flagsfilters byevaluation_runtimeand infers the runtime from request headers (detect_evaluation_runtime_from_request), because the SDK never states it. The endpoint has accepted an explicitevaluation_runtimefield since feat(flags): let requests explicitly pass in theevaluation_runtimeposthog#39298, added because that inference is unreliable.fetchis undici, which setssec-fetch-mode: corson every request, and that is one of the browser signals checked right after theUser-Agent. A User-Agent that misses the SDK allowlist therefore lands in the client branch and loses the server-side flags. Flags markedall, and flags with no runtime, survive either way.Three POSTs to
/flagsagainst a live EU Cloud project. A and B send identical bodies and differ only inUser-Agent; C repeats B's headers and adds the field. The stored responses carryerrorsWhileComputingFlags: falseand noquotaLimited; the status codes and the exact headers sent are from my own run, not reconstructable from those response bodies:User-Agentposthog-node/5.51.8{token, distinct_id}Mozilla/5.0{token, distinct_id}google-oauthMozilla/5.0evaluation_runtime: "server"google-oauthbackWhat this does not show: that any real deployment loses its
User-Agent, or anything about a specific outage. Case B rewrites the header deliberately, which is the runtime filter working as designed, and case A is the counterweight: against the current backend a genuineposthog-nodeUser-Agent gets the flag back correctly.google-oauth's runtime is inferred from this behavior, not read from its configuration. The claim is narrower: a value the SDK knows for certain is re-derived from a header that crosses arbitrary infrastructure, and the omission is unannounced.Changes
/flagsrequests.getEvaluationRuntime()onPostHogCoreStatelessreturnsundefinedby default,PostHogBackendClientoverrides it to'server', andgetFlags()sends the field when defined. Browser, react-native, and web clients keep relying on the server's inference.FlagRequestin the revision preceding #39298 declares nodeny_unknown_fields, so by the serde contract an older deployment ignores the extra key. That is a source reading, not a test against a running old server.This PR originally carried a second commit that taught the local-evaluation poller the same runtime filter. I have dropped it in response to the review. Making that half correct needs posthog-node to surface which keys were excluded by runtime, so that
@posthog/nextwithbootstrapFlags: truecan tell an incomplete bootstrap from a complete one instead of quietly shipping a short flag set to the browser. That is new public surface and its shape is your call, not mine. I would rather land the wire half now and bring the local half back once that decision exists, and I am happy to build it then.Test strategy
A new unit test asserts the
/flagsbody carriesevaluation_runtime: "server". The maximal-flags wire snapshot and six existing exact-body assertions were updated to match, which is what pins the field's presence and position for every other request shape already covered.Beyond the suite I ran the built
dist/entrypoints/index.node.mjsagainst a local server that portsdetect_evaluation_runtime_from_requestpluscollect_excluded_by_runtimefrom the Rust source. Onmaina request whoseUser-Agentis lost is classified client and loses the server-only flag; with this change the declared field pins it toserver. The live measurement above ran against a real project with its public token; that run issued/flagsrequests only. I have not characterized what the endpoint does server-side beyond responding, so I am not claiming it is side-effect free.For platform impact I compared build artifacts rather than arguing from the default. An independent reviewer bundled the browser and web-lite code from both revisions and compared the finished fetch body strings byte for byte, finding no new property on either, and the same for the react-native class under the repository's native mocks.
pnpm bundle-size:array origin/mainreports+0.00%on minified, gzip and brotli; those are that script's esbuild comparison numbers, not absolute production sizes.Edge cases considered: clients that do not declare a runtime, which keep the previous behavior because the core default is
undefined; and older deployments receiving an unknown body field. Verified on this reduced branch withTURBO_FORCE=true pnpm test:unit --concurrency=1(64/64 tasks), posthog-node 939 tests,@posthog/core1236 tests, functional 4/4, lint 32/32, andpnpm generate-referencesleaving an empty diff. An earlier parallelpnpm test:unitin this repo was killed with exit 137 before completion; the cause was not established, so the serial run is the one I trust.Release info Sub-libraries affected
Libraries affected
@posthog/coretakes a patch bump too. It is not on the list, but the newgetEvaluationRuntime()hook lives there.Checklist
undefineddefault+0.00%on all three sizes)If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
evaluation_runtimeat all. The live measurement confirms the response set moves with theUser-Agent, but case A shows the current backend handles a genuine posthog-node User-Agent correctly, so our symptom is not reproduced here. I am raising the fragility on its own merits.getAllFlagsAndPayloadsandevaluateFlagsdisagreeing on local-vs-remote merge precedence is a separate bug (a real difference, but no demonstrable wrong outcome, so left alone).