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: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "0.47.0",
"version": "0.48.0",
"description": "Build and deploy web apps and agents",
"author": {
"name": "Vercel",
Expand Down
2 changes: 1 addition & 1 deletion .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "0.47.0",
"version": "0.48.0",
"description": "Build and deploy web apps and agents",
"author": {
"name": "Vercel",
Expand Down
2 changes: 1 addition & 1 deletion .kimi-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vercel-plugin",
"version": "0.47.0",
"version": "0.48.0",
"description": "Comprehensive Vercel ecosystem plugin — relational knowledge graph, skills for every major product, specialized agents, and Vercel conventions. Turns any AI agent into a Vercel expert.",
"keywords": [
"vercel",
Expand Down
2 changes: 1 addition & 1 deletion .plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vercel-plugin",
"version": "0.47.0",
"version": "0.48.0",
"description": "Comprehensive Vercel ecosystem plugin — relational knowledge graph, skills for every major product, specialized agents, and Vercel conventions. Turns any AI agent into a Vercel expert.",
"author": {
"name": "Vercel",
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,36 +126,42 @@ What is collected:
- `dau:active_today`: sent at most once per UTC day when the plugin runs.
- `plugin:first_use`: sent once per local user profile the first time the plugin successfully reports telemetry.
- `plugin:version`: sent with telemetry batches so usage can be grouped by plugin version.
- `plugin:install_id`: the locally stored random installation UUID.
- `plugin:agent_harness`: each distinct detected agent harness category observed per installation per UTC day.

Each telemetry event contains only:

- `id`: a random event UUID.
- `event_time`: the event timestamp.
- `key`: one of the event names listed above.
- `value`: currently `"1"`.
- `value`: `"1"` for counters, the plugin version, the random installation UUID, or the detected harness, depending on the event key.

The request also sends HTTP headers used by the telemetry bridge:

- `x-vercel-plugin-topic-id: dau`
- `x-vercel-plugin-session-id`: a random UUID generated for that telemetry request.
- `x-vercel-plugin-version`: the plugin version embedded at build time.

Prompt text, bash commands, tool-call contents, file paths, project names, account IDs, and skill-injection details are not collected.
The installation ID is generated on the first telemetry-enabled plugin session and reused for that local installation. It is not derived from device, account, project, or user information. The harness value identifies Claude Code (including Claude Cowork), Cursor, Codex, GitHub Copilot, Kimi Code, or Grok using [`detect-agent`](https://github.com/vercel/detect-agent). A detected but unsupported or custom harness is reported as `other`; `unknown` means no harness was detected. Raw custom harness names are never sent.

Prompt text, bash commands, tool-call contents, file paths, project names, account IDs, harness versions, and skill-injection details are not collected.
Comment thread
Melkeydev marked this conversation as resolved.

How it is tracked:

- Events are sent to Vercel's public telemetry bridge at `https://telemetry.vercel.com/api/vercel-plugin/v1/events`.
- The bridge only forwards events from plugin versions `0.40.0` and newer.
- Local throttle files are stored under `~/.config/vercel-plugin/`:
- `dau-stamp` prevents sending `dau:active_today` more than once per UTC day.
- `harness-stamp-<harness>` prevents sending the same `plugin:agent_harness` value more than once per UTC day.
- `first-use-stamp` prevents sending `plugin:first_use` more than once.
- `installation-id` stores the random installation UUID. It is used only by plugin telemetry and is not written to `active-session.json`.
- Stamp files are written only after the telemetry bridge returns a successful response, so failed sends can retry later.
- `active-session.json` is refreshed on session start with the plugin version and expiry timestamp. It lets Vercel CLI telemetry identify commands run while a recent Vercel plugin session marker is present. It contains no prompt text, file paths, project names, account IDs, tool-call contents, or skill-injection details.

Behavior:

- Unset `VERCEL_PLUGIN_TELEMETRY`: telemetry is enabled.
- `VERCEL_PLUGIN_TELEMETRY=off`: disables all telemetry, including `dau:active_today` and `plugin:first_use`.
- `VERCEL_PLUGIN_TELEMETRY=off`: disables all telemetry, including `dau:active_today` and `plugin:first_use`, and does not create an installation ID if one does not already exist.

Where to set `VERCEL_PLUGIN_TELEMETRY`:

Expand Down
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 80 additions & 1 deletion hooks/session-start-profiler-platform.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { describe, expect, test } from "bun:test";
import { detectSessionStartPlatform } from "./src/session-start-profiler.mts";
import {
detectAgentHarness,
detectSessionStartPlatform,
normalizeDetectedAgentHarness,
} from "./src/session-start-profiler.mts";

describe("session-start-profiler platform detection", () => {
test("test_session_start_profiler_does_not_infer_cursor_from_cursor_project_dir_alone", () => {
Expand All @@ -25,4 +29,79 @@ describe("session-start-profiler platform detection", () => {
),
).toBe("claude-code");
});

test("normalizes supported detect-agent names", () => {
expect(normalizeDetectedAgentHarness("cursor")).toBe("cursor");
expect(normalizeDetectedAgentHarness("cursor-cli")).toBe("cursor");
expect(normalizeDetectedAgentHarness("github-copilot")).toBe("github-copilot");
expect(normalizeDetectedAgentHarness("kimi")).toBe("kimi");
expect(normalizeDetectedAgentHarness("grok")).toBe("grok");
expect(normalizeDetectedAgentHarness("codex_cli")).toBe("codex");
expect(normalizeDetectedAgentHarness("claude_code")).toBe("claude-code");
expect(normalizeDetectedAgentHarness("cowork")).toBe("claude-code");
});

test("distinguishes no detection from detected but unapproved agents", () => {
expect(normalizeDetectedAgentHarness(undefined)).toBe("unknown");
for (const name of [
"gemini_cli",
"cline",
"antigravity",
"augment-cli",
"open_code",
"goose",
"junie",
"pi",
"replit",
"kiro",
"openclaw",
"devin",
"custom-agent@1",
]) {
expect(normalizeDetectedAgentHarness(name)).toBe("other");
}
});

test("uses Cursor hook fields before detect-agent", async () => {
let detectorCalled = false;
const harness = await detectAgentHarness(
{ cursor_version: "1.0.0" },
async () => {
detectorCalled = true;
return { isAgent: true, agent: { name: "claude_code" } };
},
);

expect(harness).toBe("cursor");
expect(detectorCalled).toBe(false);
});

test("uses detect-agent for non-Cursor hooks", async () => {
expect(
await detectAgentHarness({}, async () => ({
isAgent: true,
agent: { name: "grok" },
})),
).toBe("grok");
});

test("returns unknown only when detect-agent finds no agent", async () => {
expect(
await detectAgentHarness({}, async () => ({ isAgent: false })),
).toBe("unknown");
expect(
await detectAgentHarness({}, async () => ({
isAgent: true,
agent: { name: "devin" },
})),
).toBe("other");
});

test("falls back to unknown when detect-agent rejects", async () => {
expect(
await detectAgentHarness({}, async () => {
throw new Error("detector failed");
}),
).toBe("unknown");
});
});
Loading
Loading