feat(telemetry): anonymous opt-out usage telemetry (stack, cost, deploy-shape, CLI usage, call funnel)#160
Open
nicolotognoni wants to merge 4 commits into
Open
feat(telemetry): anonymous opt-out usage telemetry (stack, cost, deploy-shape, CLI usage, call funnel)#160nicolotognoni wants to merge 4 commits into
nicolotognoni wants to merge 4 commits into
Conversation
…all id)
Add anonymous, opt-out, fail-safe usage telemetry so maintainers can see how the
SDK is used. No PII or call content is ever collected.
Events (schema v3):
- sdk_initialized: carrier, tunnel
- feature_used: engine + provider, and for pipeline agents the composed stack —
stt/tts/llm provider and a sanitized model token (e.g. deepgram-nova-3,
anthropic-claude-opus-4-8)
- agent_configured: built-in vs custom tool counts (never names) + integration
category (openclaw/mcp/hermes/other/none)
- call_completed: outcome, error_code (closed code, never the message),
engine/provider/carrier, raw latency_ms + duration_seconds, and total cost_usd
Identifiers are random and PII-free: a per-process run_id and a persistent
anonymous install_id (random UUID stored locally to count active installs, never
a hardware fingerprint, never created when opted out). A two-layer allowlist
coerces any off-list value to "other"; custom/fine-tuned model names collapse to
{vendor}-other, so customer brands can never reach the wire.
Opt out with telemetry=False / { telemetry: false }, PATTER_TELEMETRY_DISABLED=1,
or DO_NOT_TRACK=1; auto-disabled in CI/test. Inspect locally with
PATTER_TELEMETRY_DEBUG=1; redirect with PATTER_TELEMETRY_ENDPOINT.
Tests: Python 2103 passed, TypeScript 1688 passed; Python/TS schema parity
verified byte-for-byte.
…als (schema v4)
Extend anonymous usage telemetry with high-value, privacy-safe signals (all
enums/bools/buckets, no PII; presence-only env probes never read a var's value):
- sdk_initialized: invoked_by_agent (claude/cursor/copilot/...), container,
serverless, cloud, package_manager, days_since_install_bucket, and
previous_sdk_version (upgrade funnel via a local state file).
- agent_configured: noise_reduction, turn_detection, preambles_used,
per_tool_timeouts_set, llm_fallback_configured.
- call_completed: turn_count_bucket.
New telemetry/environment.{py,ts} (deploy-shape probes); install-id gains the
version funnel + days-since-install bucket. events.* gains a boolean-dimension
category; schema bumped v3 -> v4; the Vercel/Cloudflare relay allowlists updated
to match. README now carries an honest anonymous/opt-out telemetry note.
Telemetry never breaks construction: feature-adoption derivation is fault-isolated
(array-guarded) so invalid input still raises the correct validation error.
Tests: Python 2106, TypeScript 1691 passed; Python/TS schema parity byte-for-byte;
live relay -> Axiom verified for every new field.
…(schema v5) Round out anonymous usage telemetry to OSS-CLI parity (Next.js / Astro / Homebrew) so the maintainers get a clear picture of how the SDK is used — still no PII, every field a coarse enum / bucket / bool. New events: - cli_command: which `getpatter` CLI command ran (the command NAME only, never arguments or flag values). The `getpatter telemetry` control command never emits telemetry itself. - first_run: sent once per install (the run that creates the local state) to mark activation; carries the same anonymous deploy-shape dims as sdk_initialized and is never sent when opted out. - call_started: emitted when a call connects, pairing with call_completed for a connect->complete funnel and a failure-rate denominator. New dimension: direction (inbound / outbound) on call_started and call_completed — a core usage split that was previously invisible. User control: `getpatter telemetry status | disable | enable` (parity with `next telemetry disable`) writes a persisted, machine-level opt-out marker (~/.getpatter/telemetry-disabled) honoured by the consent resolver at precedence #3 (after the env kill-switches, before the in-code flag). events.* gains the direction + cli_command enums and three event names; schema bumped v4 -> v5; the Vercel/Cloudflare relay allowlists updated to match. README restructured to the OSS-standard "Anonymous Telemetry" disclosure (what we collect / what we never collect / not PII / opt-out incl. CLI + debug); docs/telemetry.mdx + CHANGELOG updated. Fail-safe throughout: every new emit is wrapped so it can never block or break a call, SDK construction, or the CLI. Python<->TS verified byte-for-byte (7 events, 23 dimensions, schema 5). Tests: Python 2113, TypeScript 1696 passed (telemetry: Python 48 / TS 37).
…iom-internal # Conflicts: # CHANGELOG.md
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
Adds anonymous, opt-out usage telemetry to both SDKs (Python
getpatter+ TypeScriptgetpatter) so the maintainers can see, in aggregate, how the SDK is actually used — which engines, providers, models, and carriers people choose, on which platforms, at which versions — and prioritise accordingly. It follows the open-source norm (Next.js, Astro, Homebrew): on by default, completely anonymous, trivially disabled, and fail-safe so it can never block, slow, or break a live phone call.No PII or call content is ever collected. Every field is a coarse enum, bucket, or bool, enforced by a two-layer allowlist (key and value) in the SDK and re-validated server-side by the collector.
Events
sdk_initializedfirst_runcli_commandgetpatterCLI rundashboard/eval/telemetry/other) — never arguments or flagsfeature_usedanthropic-claude-haiku-4-5,deepgram-nova-3)agent_configuredcall_startedcall_completedfor a connect→complete funnelcall_completedPrivacy posture
Never collected: phone numbers, transcripts, audio, prompts, tool arguments, API keys, customer identifiers, IPs (dropped at the collector), hostnames, file paths, or any free text. Custom / self-hosted model names and custom tool names are structurally impossible to send — they collapse to a vendor bucket or
otherbefore anything leaves the process. The two identifiers (run_id,install_id) are random UUIDs, never derived from hardware.Opt out — any one of
Patter(telemetry=False)/new Patter({ telemetry: false })getpatter telemetry disable(persisted, machine-level; re-enable withgetpatter telemetry enable, inspect withgetpatter telemetry status)PATTER_TELEMETRY_DISABLED=1, or the cross-tool standardDO_NOT_TRACK=1Auto-disabled in CI/test. Inspect-without-send:
PATTER_TELEMETRY_DEBUG=1prints each event to stderr and sends nothing. Redirect to your own collector withPATTER_TELEMETRY_ENDPOINT.Implementation notes
telemetry/module in each SDK:events(schema + allowlist),consent,client(bounded buffer, short timeouts, swallow-all, best-effort exit flush),stack(vendor + PII-safe model sanitizer),environment(presence-only probes),install_id,call_metrics.docs/telemetry.mdxdocuments every field.Test plan
tsc --noEmitcleanotherand never reach the wire rawDO_NOT_TRACK, in-code flag, persisted CLI marker, CI/test auto-disable, debug inspect-only) verified to produce zero egress