Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ this one does not look.
`gemdb.admin.garbage_collect()`, `gemdb.admin.backup()` and `gemdb.sessions.all()` now raise a
security error; **Collect Garbage Now** and **Stop a Database Session…** cover the first and
last.
- **Usage data now records which editor GemDB runs in** (VS Code, Cursor, …), so a
fork's users are not mistaken for VS Code users on an old release. `USAGE_DATA.md` lists it.

### Fixed

Expand Down
8 changes: 6 additions & 2 deletions USAGE_DATA.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ VS Code automatically attaches its own common properties to every event:
`common.isnewappinstall`, `common.sqmid`, `common.devDeviceId`,
`common.telemetryclientversion`.

GemDB adds three properties of its own to every event:
GemDB adds five properties of its own to every event:

- `extensionMode`, which is `production` for an installed extension and
`development` or `test` when a GemDB developer is running it from source.
Expand All @@ -34,6 +34,10 @@ GemDB adds three properties of its own to every event:
- `firstSeen` — the ordinary case: GemDB recorded this as a new install.
- `reinstall` — GemDB's own files were reinstalled while the database
survived.
- `appName` and `appUriScheme`, which name the editor GemDB runs in, as its
name and its URL scheme (e.g. "Cursor" and `cursor`). Editors built on VS
Code report the VS Code version they are based on, so this is what tells
them apart.

`common.vscodemachineid` is a pseudonymous identifier VS Code generates per
installation. It is not tied to your name or email, but under GDPR it counts
Expand Down Expand Up @@ -98,5 +102,5 @@ delete the matching records.

## Changes

Last updated: 2026-10-05. Changes to this notice will be published in this
Last updated: 2026-10-07. Changes to this notice will be published in this
repository.
8 changes: 8 additions & 0 deletions docs/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ take the earliest event of that kind for its `common.vscodemachineid`.
| `extensionMode` | `production`, `development`, `test` | Only `production` is a real user. Filter out the others in every report. |
| `installDay` | a date, e.g. `2026-09-15` | Roughly when GemDB was first used on this machine. |
| `installDaySource` | `firstSeen`, `reinstall` | `reinstall`: GemDB was reinstalled but the user's database was still there. |
| `appUriScheme` | e.g. `vscode`, `vscode-insiders`, `cursor`, `windsurf` | Which editor GemDB runs in. Editors built on VS Code report the VS Code version they are based on in `common.vscodeversion`, so group by this before reading versions. Missing on events from 1.5.4 and earlier. |
| `appName` | e.g. `Visual Studio Code`, `Cursor` | The same editor, by its display name. Missing on events from 1.5.4 and earlier. |

In a remote window (Dev Container, SSH, WSL), `appUriScheme` and `appName`
describe the editor on the user's desktop, not the remote machine, so a remote
window reports the same values as a local one. `common.remotename` says whether
the window was remote. When the editor runs in a web browser, as with
`code serve-web`, they describe that web version of the editor instead.

VS Code adds its own `common.*` properties too (machine id, OS, VS Code
version, extension version). [USAGE_DATA.md](../USAGE_DATA.md) lists them.
Expand Down
2 changes: 2 additions & 0 deletions src/__mocks__/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ const FAKE_COMMON_PROPERTIES: Record<string, string> = { 'common.fake': 'yes' };
/** Two-line insurance: nothing here reads `env` today, but `activate()` does. */
export const env = {
machineId: 'fake-machine-id',
appName: 'Visual Studio Code',
uriScheme: 'vscode',
remoteName: undefined as string | undefined,
clipboard: {
writeText: (text: string): Promise<void> => {
Expand Down
19 changes: 19 additions & 0 deletions src/__tests__/telemetryDocs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,22 @@ describe('docs/telemetry.md', () => {
expect(undocumented, `document these in ${DOC_PATH}`).toEqual([]);
});
});

// The same `beforeAll` is what makes this assertion mean "every event": it
// drives all seven `report*` functions, and the suite above fails if that
// list ever falls behind `EVENT`. Asserting it off one event — activation,
// say — would pass for a `report*` that built its own properties object
// instead of going through `send`.
describe('every event', () => {
it('names the editor GemDB runs in', () => {
// Forks report their base VS Code version, so these are what tells a
// Cursor user from a VS Code user on an old release.
expect(__telemetry.length).toBeGreaterThan(0);
for (const event of __telemetry) {
expect(event.properties, event.name).toMatchObject({
appUriScheme: 'vscode',
appName: 'Visual Studio Code',
});
}
});
});
14 changes: 12 additions & 2 deletions src/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,16 @@ let reporter: TelemetryReporter | undefined;
* Properties stamped on every event, established once at activation.
*
* `extensionMode`, plus `installDay` and `installDaySource` (see
* `resolveInstallDay`). VS Code's own `common.*` properties are mixed in by
* the extension host and are not repeated here.
* `resolveInstallDay`), plus `appUriScheme` and `appName`, which name the
* editor GemDB runs in. Forks (Cursor, Windsurf) report their *base*
* VS Code version in `common.vscodeversion`, and no `common.*` property names
* the app, so without these a fork's users read as VS Code users stuck on an
* old release. Sent as raw strings, not mapped to a list, so a new fork shows
* up without a release. In a remote window they are still the desktop app's:
* the desktop builds the remote extension host's init data from its own
* product info (`remoteExtensionHost.ts`) and the server never overrides
* `appName` or `appUriScheme`. VS Code's own `common.*`
* properties are mixed in by the extension host and are not repeated here.
*/
let baseProperties: Record<string, string> = {};

Expand Down Expand Up @@ -217,6 +225,8 @@ export function initTelemetry(context: vscode.ExtensionContext, databaseExists:
extensionMode: extensionModeName(context.extensionMode),
installDay,
installDaySource,
appUriScheme: vscode.env.uriScheme,
appName: vscode.env.appName,
};
// Disposal flushes queued events, so the subscription is load-bearing.
reporter = new TelemetryReporter(CONNECTION_STRING);
Expand Down
Loading