Skip to content

feat(node)!: posthog-node 6 on capture v1 (DO NOT MERGE) - #5407

Draft
eli-r-ph wants to merge 17 commits into
mainfrom
capture-v1
Draft

eli-r-ph wants to merge 17 commits into
mainfrom
capture-v1

Conversation

@eli-r-ph

@eli-r-ph eli-r-ph commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Problem

posthog-node still sends events to the legacy capture endpoints, and capture v1 is only an opt-in through an environment variable. posthog-node 6 makes capture v1 the only capture path, with AI events on their own lane at /i/v1/ai/events, as posthog-python 8, posthog-go v2 and posthog-rs 1.0 do on their v1 branches.

Changes

Release info Sub-libraries affected

Libraries affected

  • All of them
  • posthog-js (web)
  • posthog-js-lite (web lite)
  • posthog-node
  • posthog-react-native
  • @posthog/react-native-plugin
  • @posthog/react
  • @posthog/ai
  • @posthog/convex
  • @posthog/next
  • @posthog/nextjs-config
  • @posthog/nuxt
  • @posthog/openfeature-node-provider
  • @posthog/openfeature-web-provider
  • @posthog/rollup-plugin
  • @posthog/webpack-plugin
  • @posthog/types
  • @posthog/browser-common

The wrapper packages that depend on posthog-node get their release plan in a later PR of this series.

Checklist

  • All commits are signed (see Commit signing)
  • Tests for new code
  • Accounted for the impact of any changes across different platforms
  • Accounted for backwards compatibility of any changes (no breaking changes!)
  • Took care not to unnecessarily increase the bundle size

If releasing new changes

  • Ran pnpm changeset to generate a changeset file

🤖 Agent context

Authored with Claude Code (Claude Opus 5.5), directed by @eli-r-ph. Changeset only; each feature PR carries its own tests. Behavior follows the v1 branches of posthog-python, posthog-go and posthog-rs, adapted to posthog-node's own API and style.

🤖 Generated with Claude Code

@eli-r-ph eli-r-ph self-assigned this Oct 23, 2026
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Possible changeset mismatch — 2 undeclared

This is informational — the PR is not blocked. Click the triangle above to collapse, or push a fix and this comment will auto-delete.

Modified in this PR but not in any changeset:

  • @posthog/ai
  • @posthog/nextjs-config

If this package should ship the change, add it to the changeset frontmatter:

---
"@posthog/ai": patch
"@posthog/nextjs-config": patch
---

Changesets in this PR:

  • posthog-node — major

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Coherence

0 spec problems · 0 failing chokepoints · 2 spec gaps · 0 guard failures

44 components, 7 bullets (5 invariants, 2 requirements), 0 problems

Chokepoints: none declared.

Spec gaps: 2 entrances with outside or unknown trust and no traced control, and 10 more held by the adoption baseline

  • packages/web/Web.spec.md entrance public api (trust host-app): packages/web
  • packages/webpack-plugin/Webpack-plugin.spec.md entrance webpack plugin (trust build): packages/webpack-plugin

Close one with guard: <chokepoint>, an invariant that names it on its entrances: line, or control: none — <reason>; scaffold control proposes it.

* test(node): read capture events from either wire format

Add shared helpers that find analytics capture requests on the legacy
/batch/ endpoint or the capture v1 analytics endpoint, and return their
events in the legacy shape. A v1 event's options, session and window
ids and SDK info header map back to the properties they replace, so one
assertion holds for both formats.

Port the tests that read events off the wire to these helpers, and
answer capture requests with a body that both senders accept. The same
tests pass on the legacy default and with POSTHOG_CAPTURE_MODE=v1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ai): accept capture v1 requests in the cassette collector

The collector accepted only the legacy /batch/ request. It now also
accepts the capture v1 analytics and AI endpoints with a bearer token,
and returns their events in the legacy shape, so the replay tests hold
for both wire formats.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(convex): read example events from either capture endpoint

The example tests read events only from /batch/ calls and checked the
api_key in the body. Read them from the legacy or the capture v1
analytics endpoint, and accept the project key from the body or the
bearer token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(convex): give the sequenced fetch mock the headers field its type requires

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Node SDK compliance v2

Commit: 86fd5a1
Workflow run and artifacts

Capture v1 — acceptance

✅ 28 passed / ❌ 3 non-passing / ⚠️ 853 not selected

Drill-down: run artifacts → node-v2-v1 → acceptance/report.json.diagnostics.json (match case_id below).

❌ server_snapshot_payload_false — failed_assertion

Expected native data result

Code: snapshot_result

Failed step: acceptance/public/evaluate-flags.feature:868

the public snapshot outcomes should have these semantics:
❌ server_snapshot_payload_zero — failed_assertion

Expected native data result

Code: snapshot_result

Failed step: acceptance/public/evaluate-flags.feature:868

the public snapshot outcomes should have these semantics:
❌ server_snapshot_payload_null — failed_assertion

Expected native data result

Code: snapshot_result

Failed step: acceptance/public/evaluate-flags.feature:868

the public snapshot outcomes should have these semantics:

Harness exit: 1; report validation exit: 1.

Capture v1 — migration

✅ 115 passed / ❌ 1 non-passing / ⚠️ 41 not selected

Drill-down: run artifacts → node-v2-v1 → reports/report.json.diagnostics.json (match case_id below).

❌ migration/yaml-parity-v1/remote-flags-v1.feature::Omitted GeoIP settings default to enabled in flag requests — failed_assertion

Flags field differs

Code: flag_request_field

Failed step: migration/yaml-parity-v1/remote-flags-v1.feature:134

the first flags request field "geoip_disable" should equal JSON false

Operation: /flags/

Field: geoip_disable

Expected:

false

Actual value:

true

Harness exit: 1; report validation exit: 1.

eli-r-ph and others added 3 commits October 26, 2026 21:53
* refactor(node): name the capture lane on CaptureError and export it

Rename CaptureV1Error to CaptureError (and its params and drop types),
add the endpoint the batch was sent to, and export the class so callers
can instanceof it. The sender now takes its lane path from config so
the analytics and AI lanes can share it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node)!: send every event through capture v1

Remove the POSTHOG_CAPTURE_MODE switch and the legacy /batch/ path.
Every capture() event, including $ai_* events, goes to
/i/v1/analytics/events; the SDK no longer reroutes by event name.
captureAi and captureAiImmediate send to /i/v1/ai/events through the
same sender, with one lazily created sender per lane. A 413 on the AI
lane is now a terminal CaptureError instead of a split-and-retry.

A client built while POSTHOG_CAPTURE_MODE is set logs one warning
that the variable is no longer read. Tests that read events through the
shared helpers pass unchanged; the wire snapshot, the capture lane
wiring tests and the delivery error tests now assert the v1 wire.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci(compliance): run the node v2 harness on the capture v1 profile only

posthog-node no longer has a v0 capture mode, so drop the v0 matrix
leg and the node-legacy profile. The adapter stops setting
POSTHOG_CAPTURE_MODE and stops advertising capture_v0,
capture_v0_batch and capture_ai_v0. run-compliance.sh no longer takes
a mode argument, and the PR comment reports both suites for the one
profile. The job stays advisory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): add changeset for capture v1 only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): keep the process alive while a capture send retries

The v1 sender waited between retries and timed each attempt with unref'd
timers. A script ending in `await posthog.shutdown()` could exit during a
retry wait and lose the batch. Use ref'd timers inside an in-flight send,
as the v0 path did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ai): accept only capture v1 requests in the cassette collector

posthog-node no longer sends to /batch/, so the collector now rejects the
legacy endpoint and body, and requires the bearer token and the v1 event
fields on every request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(convex): assert the capture v1 wire in the example tests

posthog-node sends events only to the capture v1 analytics endpoint.
Read events from that endpoint alone, and check the bearer token and the
absence of an api_key in the body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ci): download the node compliance artifact into the folder the report reads

With one artifact left, a pattern download extracts it straight into the
target path, so the report found no node-v2-v1 folder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(node): serialize capture v1 events once for reuse

Add serializeV1Event, which builds an event's wire JSON once and records
its byte size and the size of its properties, and buildV1Body, which joins
already-serialized events into a request body. Nothing calls them yet; the
capture lane uses them to batch by bytes without serializing twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node): add a self-contained capture lane

A lane owns one endpoint's in-memory queue, flush timer, flushAt trigger,
byte- and count-aware batching and in-flight send, and keeps events that
failed without an HTTP response for the next flush. It takes its sender,
config, callbacks, logger and clock from the caller.

The sender contract it needs is declared here as V1SendOptions (an abort
signal and keepUnsent); the capture sender implements it when the client
switches to lanes. Nothing uses the lane yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…iance (#5423) (#5451)

Generated-By: PostHog Desktop
Task-Id: eac276ac-44b1-4fc2-9666-d5463c7b28c4


(cherry picked from commit bd41fe4)

Co-authored-by: posthog[bot] <206114724+posthog[bot]@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Error tracking smoke

Commit: 86fd5a19797f967c26542037e4cd7c1f8b73aabc · CI run and artifacts

7/8 passed (0 skipped, 0 uncovered)

Fixture Result First failure
rollup-4 ✅ Passed —
vite-8-autocapture ✅ Passed —
webpack-5.112 ✅ Passed —
next-14 ✅ Passed —
next-15 ✅ Passed —
next-16-webpack ✅ Passed —
next-16-turbopack ❌ Failed release 1.0.0: maps dangling (none), expected deleted
nuxt-4 ✅ Passed —

eli-r-ph and others added 12 commits October 28, 2026 13:10
…5410)

* refactor(core): let subclasses call normalizeMessage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(node): send pre-serialized events and let callers keep unsent ones

The capture sender now takes events serialized once, builds the body from
those JSON strings, accepts an abort signal that stops attempts and backoff
waits, and can hand back events whose last attempt got no HTTP response.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): cancel capture backoff waits cleanly on abort

The default backoff wait now clears its timer when the send is aborted and
removes its abort listener when it finishes, so a signal shared by every
send of a lane does not collect listeners.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node)!: send analytics and AI events on separate capture lanes

Each lane has its own queue, timer, flushAt trigger, batching and in-flight
send, so a slow or failing lane no longer delays or drops the other's
events. flush() waits for both lanes and no longer rejects on delivery
failures, maxQueueSize applies to analytics events only, batches stop at
about 5 MiB, and events that fail with a network error are kept for the
next flush. Shutdown drains both lanes against one deadline and aborts
sends still running when it passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): add changeset for separate capture lanes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): keep the shutdown deadline while a capture body is compressing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(node)!: retry capture requests on the fleet schedule

Capture waits 100 ms before the first retry and doubles each wait, with
+/-25% jitter, up to 30 seconds; Retry-After raises a wait and jitter never
shortens it. The new captureMaxRetries option (integer 0-9) sets the retry
count and falls back to fetchRetryCount; fetchRetryDelay no longer affects
capture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node): choose the capture compression codec per lane

captureCompression ('none' | 'gzip' | 'deflate' | 'zstd', default 'gzip') sets
the analytics lane codec and maps disableCompression: true to 'none' when unset.
captureAiCompression sets the AI lane codec on its own (default 'gzip'). The
Node build compresses with node:zlib, with zstd from Node 22.15; the edge build
uses CompressionStream for gzip and deflate. An unavailable zstd falls back to
gzip with one debug log. Content-Encoding names the codec used, and each request
sends a fresh copy of the compressed body.

The convex example tests now decode capture bodies of any type and codec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node): size and time the AI capture lane on its own

captureAiMaxQueueSize (default 1000) replaces the fixed AI queue cap, and
captureAiRequestTimeoutMs (default 30000) sets the AI lane request timeout.
The analytics lane keeps requestTimeout. Invalid values log one error and
use the default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): add changeset for capture retries and compression

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): regenerate the public API references

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node)!: retry capture requests that get 404 or 429

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): keep captureAiMaxQueueSize as set when it is below flushAt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): cap capture settings at the timer limit and hide the compressor hook

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(core): add an optional redirect mode to PostHogFetchOptions

Type-only and additive: no core runtime change. It lets posthog-node send capture requests with `redirect: 'manual'`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node)!: follow only same-origin 307 and 308 capture redirects

Capture requests use redirect: 'manual' and follow a 307 or 308 whose Location
has the configured host's origin, at most 5 times, with the same method,
headers and a fresh copy of the body. Any other redirect is a final failure
reported as a CaptureError. A custom fetch that follows redirects itself
still works.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node): send event uuids in canonical form

Before an event is queued or sent, and again after before_send, its uuid must
be 32 hex digits, the hyphenated form, the braced hyphenated form or the
urn:uuid: form, in any case; it is sent lowercase and hyphenated, and results
match by that form. Any other uuid is replaced with a generated v7 uuid and one
warning that does not repeat the value. An empty or missing uuid is generated
silently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node): report copies the server dropped as duplicates

When a 2xx result carries duplicates_dropped: n, the first event with that
uuid in the request takes the result, the next n are reported as drops with
details duplicate_event_uuid and are never retried, and later copies take the
kept result. Values that are not positive integers are ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node): log one line per batch that loses capture events

With no 'error' listener, a batch that loses events (server drops, exhausted
retries, a final request failure) and a lane flush that drops events locally
each log one console line. It names the lane, the count of events not
delivered and each cause with its HTTP status, and never includes server
response text, event content or uuids. With a listener there is no line: the
listener gets the CaptureError as before. Local drops at shutdown now arrive
in one report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): add changeset for capture redirects, uuids and duplicates

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): regenerate the public API references

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(react-native): regenerate the public API references

The optional `redirect` field on `PostHogFetchOptions` is part of the React
Native public API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): warn once per call for an invalid uuid, add a core changeset for the redirect option

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…anged (#5412)

* feat(node)!: send event option values unchanged

The wire builder no longer converts option values. It sends the event's
`options` object as given, removes each legacy option property
(`$process_person_profile`, `$cookieless_mode`, `$ignore_sent_at`,
`$product_tour_id`) from `properties`, and uses its value only when the
matching option is missing or null. Unknown option keys pass through, and
an `options` value that is not an object is logged once and ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node): warn when a non-string session or window id is dropped

`$session_id` and `$window_id` still move to the top-level `session_id` and
`window_id` only when they are strings. A null value is dropped silently;
any other value is dropped with a warning that names its JSON type and
never the value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node): add per-event options to every capture call

`capture`, `captureImmediate`, `captureAi`, `captureAiImmediate`, `identify`,
`alias`, `groupIdentify`, `setPersonProperties` and `unsetPersonProperties`
(and their immediate forms) take an optional `options` map that is sent to
capture unchanged. An `options` value that is not an object is logged once
and the event is sent without it.

`before_send` receives the options and its returned options are sent. The
hook now gets copies of `properties` and its `$set`, `$set_once`, `$groups`
and `$group_set` maps, so it cannot change the caller's objects.

An event with no distinct id now sets the `process_person_profile` option
to false, unless the caller set it, instead of the legacy
`$process_person_profile` property.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): add changeset for per-event capture options

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): regenerate the public API references

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): accept options on alias in the IPostHog interface

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): give before_send a full copy of plain values and always an options object

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(node): before_send always gets an options object

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(node): note that capture drops an event with an unreadable option

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ntext options (#5413)

* feat(node): add registerOptions() and context options

`registerOptions()` and `unregisterOptions()` set options for every event,
next to `register()`. A context takes `options` next to `properties`;
nested contexts inherit them unless `fresh: true`.

Each event fills the options it left unset (missing or null) from the
context, then from `registerOptions()`, then from the SDK's personless
option, before `before_send` runs. A value that is not an object logs one
error and is ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node)!: fill context, global and SDK values before before_send

Each event now fills the properties it left unset (key missing or
undefined) from the context, then from `register()`, then from the values
the SDK adds: `$is_server`, `$geoip_disable` from the `disableGeoip`
setting, feature flag properties from `flags` or `sendFeatureFlags`, and,
on `$exception`, the OpenTelemetry trace ids (as a pair) and the injected
`$release_id`. So a value set on the event, in context or with
`register()` now beats every SDK value, and `before_send` sees and can
change all of them; nothing is added after the hook.

`$set`, `$set_once`, `$groups` and `$group_set` fill one level deep. Typed
`groups` merge over a `$groups` property key by key. A per-call
`disableGeoip` sets `$geoip_disable` under the event's own property, so
`disableGeoip: false` now sends `$geoip_disable: false`. The minimal
`$feature_flag_called` allowlist runs after every fill and keeps the
properties that set options and session fields. `$bot_pageview` renaming
happens before the hook.

`$lib` and `$lib_version` are no longer added to properties; the request
header carries them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node)!: apply every layer to identify, alias and group identify

Context and `register()` properties and options now reach `$identify`,
`$create_alias` and `$groupidentify` like every other event. Identify's
context properties go into the event properties, not into `$set`, and
identify no longer sends an empty `$set_once`.

An empty or whitespace-only distinct id counts as missing, so the context
id or a personless id is used. `groupIdentify` without a distinct id uses
the context id before `$<groupType>_<groupKey>`, and never takes the
context session. `alias` never takes its target from a context or a
registered property, and its `distinct_id` property is the resolved id.

The `IPostHog` `alias` and `aliasImmediate` signatures accept
`disableGeoip` and `options`, like the client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): ignore reserved exception properties passed to captureException

Following sdk-specs `exception-event-metadata`, properties passed to
`captureException` can no longer set the exception properties the SDK or
PostHog owns: `$exception_list`, `$exception_level`, `$exception_source`,
`$debug_images` and the processor-owned `$exception_*` and
`$cymbal_errors` keys. Other properties are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): add changeset for layered event values

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): regenerate the public API references

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(node): note that before_send edits properties, not the disableGeoip or groups fields

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(node): note ignored exception properties and the cost of a bad registered option

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): regenerate the public API references

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(node)!: make registerOptions and unregisterOptions async

They return a promise, like register() and unregister().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node)!: return a flat message from prepareEventMessage

The result has the same shape as the message passed in: `options` holds the event options, and
`uuid`, `timestamp` and `disableGeoip` are top-level fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(node)!: take an options object in captureException

captureException(error, { distinctId, properties, options, uuid, flags, level }) and the same for
captureExceptionImmediate. `level` sets `$exception_level` (default `error`). The 5.x positional
form still works and logs one deprecation error per process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(mcp): correct the comment on why the sink passes groups

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(examples): pass an options object to captureException

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): add changeset for the captureException options object

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(convex,next,nuxt): stay on posthog-node 5 until their own major

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ai): pass captureException arguments in the form the client takes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): regenerate the public API references

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(convex): declare the flag action return types

The build emits declarations, and with posthog-node 5 from the registry the
inferred types point into that package's own copy of @posthog/core.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore: exempt posthog-node and @posthog/core from the release-age wait

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): normalize exception levels like Python, warn per method and on extra arguments, keep the release cooldown

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…, add captureTracingHeaders (#5457)

* feat(node)!: disable on personal keys, keep event content out of logs, add captureTracingHeaders

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): regenerate the public API references

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(node): qualify the debug log note for server response text

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…pair lone surrogates in properties (#5458)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(node)!: generate event uuids from crypto.getRandomValues

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): generate a missing event uuid from the crypto-backed generator too

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore(node): run the YAML compliance harness against capture v1

Rewrite the YAML adapter for capture v1 only: pass options and caller uuids
unchanged, pick one codec per process, and declare the capture_ai_v1,
event_options and harness 1.23.0 capabilities. Add a CI job per codec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): support the harness reported_drops, log_capture, generated_payloads, property_layers and fail_open features

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(node): name each reported drop once and size padding markers in bytes in the compliance adapter

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): run the compliance harness from release 1.25.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(node): expect $geoip_disable: false from a per-call disableGeoip: false in the v2 adapter test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): run the compliance harness from release 1.26.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): run the Gherkin harness from release 1.26.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(node): pin the YAML harness image by digest and reuse the parsed attempt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs(node): add the 5.x to 6.0 migration guide

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(node): qualify the log guarantee and positional captureException note

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(node): describe the before_send copy, exception levels and captureException warnings in the 6.0 guide

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(node): note that before_send edits no longer reach the sendFeatureFlags request

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

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.

1 participant