Commit Graph
2 Commits
Author SHA1 Message Date
gabrielstein-mem0 8a9915d3fe refactor(telemetry): tighten alias helpers and gate on MEM0_TELEMETRY
Review pass on the PostHog identity-stitching fix:

- Gate _maybe_alias_anon_to_email and _maybeAliasAnonToEmail on telemetry
  being enabled. Previous version still read ~/.mem0/config.json and
  persisted telemetry.aliased_to even when MEM0_TELEMETRY=false, which
  meant re-enabling telemetry later would skip identity merging forever.
  Python checks client_telemetry.posthog; TS exports a new
  isTelemetryEnabled() getter from telemetry.ts.
- Trim capture_identify properties to {$anon_distinct_id, client_source}.
  The full platform metadata duplicated capture_event's payload and is
  not needed for $identify; PostHog enriches the person profile from
  subsequent events anyway.
- Flatten config.ts: drop the four-helper structure (loadNodeModules /
  configPath / loadRawConfig / read+write) into one getNodeFs() + a
  shared loadConfig(). Same behaviour, ~30 fewer lines, easier to read.
- Drop the tautological Mem0AnonIds shape test and the
  void createMockFetch hack at the end of the TS test file.
- Add tests for the new telemetry-disabled gate (Python + TS).

Logic traced end-to-end: 114 Python tests + 590 TS tests pass. Ruff and
Prettier clean. Zero new typecheck errors.
2026-04-30 17:01:58 -07:00
gabrielstein-mem0 789122bbea fix(telemetry): stitch PostHog identities on MemoryClient init
The Python sync/async MemoryClient and the TS MemoryClient were silently
switching their PostHog distinct_id from an anonymous on-disk UUID to the
user's email post-ping, without sending the merge instruction. PostHog
ended up tracking each user as 2-3 disconnected personas (OSS UUID, CLI
anon UUID, email), and only ~4 $create_alias events fired across ~300K
users. The Python CLI does this correctly today; this change ports the
same pattern to MemoryClient.

- mem0/memory/setup.py: make setup_config() idempotent so it backfills a
  top-level user_id for CLI-first users (the CLI writes
  telemetry.anonymous_id, not user_id, which left OSS telemetry silently
  dropped). Add read_anon_ids() and mark_aliased() helpers.
- mem0/memory/telemetry.py: add AnonymousTelemetry.capture_identify() that
  posts a PostHog $identify event with $anon_distinct_id. Add "$identify"
  to _LIFECYCLE_EVENTS so it bypasses the 90% sampling drop.
- mem0/client/main.py: at the end of __init__ for both MemoryClient and
  AsyncMemoryClient, fire $identify for each unaliased anon ID and
  persist telemetry.aliased_to so the alias only happens once per
  (anon_id, email) pair, ever. All telemetry calls swallow exceptions.
- mem0-ts/src/client/telemetry.ts: mirror with captureIdentify().
- mem0-ts/src/client/config.ts (new): node-only readMem0AnonIds() and
  markMem0Aliased() using async dynamic imports of fs/path/os, gated on
  process.versions.node so browsers no-op cleanly.
- mem0-ts/src/client/mem0.ts: call _maybeAliasAnonToEmail() inside
  _initializeClient after ping resolves the email.

Tests cover the matrix (OSS-only, CLI-only, both, already-aliased) plus
failure modes (malformed JSON, read-only fs, posthog errors, browser
env). Telemetry never raises.
2026-04-30 16:47:52 -07:00