Skip to content

Commit 816d517

Browse files
authored
Merge pull request #40 from vercel/fix/telemetry-opt-out
This is making Bash tool calls, Prompts opt out by default
2 parents b95178c + 969eae3 commit 816d517

14 files changed

Lines changed: 101 additions & 68 deletions

.plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vercel-plugin",
3-
"version": "0.32.0",
3+
"version": "0.32.1",
44
"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.",
55
"author": {
66
"name": "Vercel Labs",

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,14 @@ After installing, skills and context are injected automatically. You can also in
113113

114114
The plugin has two separate telemetry controls:
115115

116-
- `~/.claude/vercel-plugin-telemetry-preference` controls prompt text only.
116+
- `~/.claude/vercel-plugin-telemetry-preference` controls raw content telemetry only.
117117
- `VERCEL_PLUGIN_TELEMETRY=off` disables all telemetry.
118118

119119
Behavior:
120120

121-
- `echo 'enabled' > ~/.claude/vercel-plugin-telemetry-preference` keeps default base telemetry on and also allows prompt text telemetry.
122-
- `echo 'disabled' > ~/.claude/vercel-plugin-telemetry-preference` keeps prompt text off, but base telemetry remains on by default.
123-
- `VERCEL_PLUGIN_TELEMETRY=off` disables all telemetry, including prompt text, session metadata, tool events, bash command telemetry, and skill-injection telemetry.
121+
- `echo 'enabled' > ~/.claude/vercel-plugin-telemetry-preference` keeps default base telemetry on and also allows raw content telemetry for prompt text and full bash commands.
122+
- `echo 'disabled' > ~/.claude/vercel-plugin-telemetry-preference` keeps prompt text and full bash commands off, but base telemetry remains on by default.
123+
- `VERCEL_PLUGIN_TELEMETRY=off` disables all telemetry, including prompt text, full bash command telemetry, session metadata, tool names, and skill-injection telemetry.
124124

125125
Where to set `VERCEL_PLUGIN_TELEMETRY=off`:
126126

hooks/posttooluse-telemetry.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
// hooks/src/posttooluse-telemetry.mts
44
import { readFileSync } from "fs";
5-
import { trackBaseEvents } from "./telemetry.mjs";
5+
import { trackContentEvents } from "./telemetry.mjs";
66
function parseStdin() {
77
try {
88
const raw = readFileSync(0, "utf-8").trim();
@@ -32,7 +32,7 @@ async function main() {
3232
);
3333
}
3434
if (entries.length > 0) {
35-
await trackBaseEvents(sessionId, entries);
35+
await trackContentEvents(sessionId, entries);
3636
}
3737
process.stdout.write("{}");
3838
process.exit(0);

hooks/pretooluse-skill-inject.mjs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
} from "./patterns.mjs";
3434
import { resolveVercelJsonSkills, isVercelJsonPath, VERCEL_JSON_SKILLS } from "./vercel-config.mjs";
3535
import { createLogger, logDecision } from "./logger.mjs";
36-
import { trackBaseEvents } from "./telemetry.mjs";
36+
import { trackBaseEvents, trackContentEvents } from "./telemetry.mjs";
3737
import { selectManagedContextChunk } from "./vercel-context.mjs";
3838
var MAX_SKILLS = 3;
3939
var DEFAULT_INJECTION_BUDGET_BYTES = 18e3;
@@ -606,13 +606,14 @@ function run() {
606606
const runtimeEnvBefore = captureRuntimeEnvSnapshot();
607607
if (sessionId) {
608608
const toolEntries = [
609-
{ key: "tool_call:tool_name", value: toolName },
610-
{ key: "tool_call:target", value: toolTarget }
609+
{ key: "tool_call:tool_name", value: toolName }
611610
];
612611
if (toolName === "Bash") {
613-
toolEntries.push({ key: "tool_call:command", value: toolInput.command || "" });
614-
} else {
615-
toolEntries.push({ key: "tool_call:file_path", value: toolInput.file_path || "" });
612+
trackContentEvents(sessionId, [
613+
{ key: "tool_call:target", value: toolTarget },
614+
{ key: "tool_call:command", value: toolInput.command || "" }
615+
]).catch(() => {
616+
});
616617
}
617618
trackBaseEvents(sessionId, toolEntries).catch(() => {
618619
});

hooks/setup-telemetry.mjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function main() {
1010
const telemetryOverride = getTelemetryOverride();
1111
if (telemetryOverride === "off") {
1212
process.stdout.write(
13-
"Telemetry is fully disabled via VERCEL_PLUGIN_TELEMETRY=off.\nRemove or change that env var before changing the prompt-sharing preference.\n"
13+
"Telemetry is fully disabled via VERCEL_PLUGIN_TELEMETRY=off.\nRemove or change that env var before changing the content-sharing preference.\n"
1414
);
1515
process.exit(0);
1616
}
@@ -21,16 +21,16 @@ function main() {
2121
}
2222
if (existing === "enabled" || existing === "disabled" || existing === "asked") {
2323
process.stdout.write(
24-
`Prompt telemetry is currently ${existing}. This setting only controls prompt telemetry.
25-
To change prompt sharing, edit ~/.claude/vercel-plugin-telemetry-preference. To disable all telemetry, set VERCEL_PLUGIN_TELEMETRY=off in the environment that launches your agent.
24+
`Content telemetry is currently ${existing}. This setting controls prompt text and full bash command sharing only.
25+
To change content sharing, edit ~/.claude/vercel-plugin-telemetry-preference. To disable all telemetry, set VERCEL_PLUGIN_TELEMETRY=off in the environment that launches your agent.
2626
`
2727
);
2828
process.exit(0);
2929
}
3030
process.stdout.write(
3131
[
3232
"The Vercel plugin collects anonymous usage data such as skill injection patterns and tools used by default.",
33-
"Ask the user if they would also like to share their prompt text to help improve skill matching. This choice only controls prompt telemetry.",
33+
"Ask the user if they would also like to share raw content telemetry, including prompt text and full bash commands, to help improve skill matching and command understanding. This choice only controls content telemetry.",
3434
"To disable all telemetry, set VERCEL_PLUGIN_TELEMETRY=off in the environment that launches your agent.",
3535
"If they agree, run `echo 'enabled' > ~/.claude/vercel-plugin-telemetry-preference`.",
3636
"If they decline, run `echo 'disabled' > ~/.claude/vercel-plugin-telemetry-preference`.",

hooks/src/posttooluse-telemetry.mts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
#!/usr/bin/env node
22

33
import { readFileSync } from "node:fs";
4-
import { resolve } from "node:path";
5-
import { trackBaseEvents } from "./telemetry.mjs";
4+
import { trackContentEvents } from "./telemetry.mjs";
65

76
function parseStdin(): Record<string, unknown> | null {
87
try {
@@ -15,7 +14,7 @@ function parseStdin(): Record<string, unknown> | null {
1514
}
1615

1716
async function main(): Promise<void> {
18-
// Base telemetry — enabled by default unless VERCEL_PLUGIN_TELEMETRY=off
17+
// Content telemetry — opt-in only unless VERCEL_PLUGIN_TELEMETRY=off disables all telemetry
1918

2019
const input = parseStdin();
2120
if (!input) {
@@ -65,7 +64,7 @@ async function main(): Promise<void> {
6564
}
6665

6766
if (entries.length > 0) {
68-
await trackBaseEvents(sessionId, entries);
67+
await trackContentEvents(sessionId, entries);
6968
}
7069

7170
process.stdout.write("{}");

hooks/src/pretooluse-skill-inject.mts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import { resolveVercelJsonSkills, isVercelJsonPath, VERCEL_JSON_SKILLS } from ".
5858
import type { VercelJsonRouting } from "./vercel-config.mjs";
5959
import { createLogger, logDecision } from "./logger.mjs";
6060
import type { Logger } from "./logger.mjs";
61-
import { trackBaseEvents } from "./telemetry.mjs";
61+
import { trackBaseEvents, trackContentEvents } from "./telemetry.mjs";
6262
import { selectManagedContextChunk } from "./vercel-context.mjs";
6363

6464
const MAX_SKILLS = 3;
@@ -967,12 +967,12 @@ function run(): string {
967967
if (sessionId) {
968968
const toolEntries: Array<{ key: string; value: string }> = [
969969
{ key: "tool_call:tool_name", value: toolName },
970-
{ key: "tool_call:target", value: toolTarget },
971970
];
972971
if (toolName === "Bash") {
973-
toolEntries.push({ key: "tool_call:command", value: (toolInput.command as string) || "" });
974-
} else {
975-
toolEntries.push({ key: "tool_call:file_path", value: (toolInput.file_path as string) || "" });
972+
trackContentEvents(sessionId, [
973+
{ key: "tool_call:target", value: toolTarget },
974+
{ key: "tool_call:command", value: (toolInput.command as string) || "" },
975+
]).catch(() => {});
976976
}
977977
trackBaseEvents(sessionId, toolEntries).catch(() => {});
978978
}

hooks/src/setup-telemetry.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ function main(): void {
1111
const telemetryOverride = getTelemetryOverride();
1212
if (telemetryOverride === "off") {
1313
process.stdout.write(
14-
"Telemetry is fully disabled via VERCEL_PLUGIN_TELEMETRY=off.\nRemove or change that env var before changing the prompt-sharing preference.\n",
14+
"Telemetry is fully disabled via VERCEL_PLUGIN_TELEMETRY=off.\nRemove or change that env var before changing the content-sharing preference.\n",
1515
);
1616
process.exit(0);
1717
}
@@ -25,15 +25,15 @@ function main(): void {
2525

2626
if (existing === "enabled" || existing === "disabled" || existing === "asked") {
2727
process.stdout.write(
28-
`Prompt telemetry is currently ${existing}. This setting only controls prompt telemetry.\nTo change prompt sharing, edit ~/.claude/vercel-plugin-telemetry-preference. To disable all telemetry, set VERCEL_PLUGIN_TELEMETRY=off in the environment that launches your agent.\n`,
28+
`Content telemetry is currently ${existing}. This setting controls prompt text and full bash command sharing only.\nTo change content sharing, edit ~/.claude/vercel-plugin-telemetry-preference. To disable all telemetry, set VERCEL_PLUGIN_TELEMETRY=off in the environment that launches your agent.\n`,
2929
);
3030
process.exit(0);
3131
}
3232

3333
process.stdout.write(
3434
[
3535
"The Vercel plugin collects anonymous usage data such as skill injection patterns and tools used by default.",
36-
"Ask the user if they would also like to share their prompt text to help improve skill matching. This choice only controls prompt telemetry.",
36+
"Ask the user if they would also like to share raw content telemetry, including prompt text and full bash commands, to help improve skill matching and command understanding. This choice only controls content telemetry.",
3737
"To disable all telemetry, set VERCEL_PLUGIN_TELEMETRY=off in the environment that launches your agent.",
3838
"If they agree, run `echo 'enabled' > ~/.claude/vercel-plugin-telemetry-preference`.",
3939
"If they decline, run `echo 'disabled' > ~/.claude/vercel-plugin-telemetry-preference`.",

hooks/src/telemetry.mts

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ export function getOrCreateDeviceId(): string {
8181
// ---------------------------------------------------------------------------
8282

8383
/**
84-
* Prompt-level telemetry (opt-in): requires explicit user consent.
85-
* Gates collection of prompt:text — actual user prompt content.
84+
* Content-level telemetry (opt-in): requires explicit user consent.
85+
* Gates collection of raw content such as prompt:text and bash:command.
8686
*/
8787
export function getTelemetryOverride(env: NodeJS.ProcessEnv = process.env): "off" | null {
8888
const value = env.VERCEL_PLUGIN_TELEMETRY?.trim().toLowerCase();
@@ -99,10 +99,10 @@ export function isBaseTelemetryEnabled(env: NodeJS.ProcessEnv = process.env): bo
9999
}
100100

101101
/**
102-
* Prompt-level telemetry (opt-in): requires explicit user consent.
102+
* Content-level telemetry (opt-in): requires explicit user consent.
103103
* VERCEL_PLUGIN_TELEMETRY=off disables it entirely.
104104
*/
105-
export function isPromptTelemetryEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
105+
export function isContentTelemetryEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
106106
const override = getTelemetryOverride(env);
107107
if (override === "off") return false;
108108

@@ -115,6 +115,13 @@ export function isPromptTelemetryEnabled(env: NodeJS.ProcessEnv = process.env):
115115
}
116116
}
117117

118+
/**
119+
* Backward-compatible alias for older callers that still refer to prompt telemetry.
120+
*/
121+
export function isPromptTelemetryEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
122+
return isContentTelemetryEnabled(env);
123+
}
124+
118125
// ---------------------------------------------------------------------------
119126
// Always-on base telemetry (session, tool, skill injection events)
120127
// ---------------------------------------------------------------------------
@@ -150,11 +157,11 @@ export async function trackBaseEvents(
150157
}
151158

152159
// ---------------------------------------------------------------------------
153-
// Opt-in telemetry (prompt text)
160+
// Opt-in telemetry (raw content)
154161
// ---------------------------------------------------------------------------
155162

156-
export async function trackEvent(sessionId: string, key: string, value: string): Promise<void> {
157-
if (!isPromptTelemetryEnabled()) return;
163+
export async function trackContentEvent(sessionId: string, key: string, value: string): Promise<void> {
164+
if (!isContentTelemetryEnabled()) return;
158165

159166
const event: TelemetryEvent = {
160167
id: randomUUID(),
@@ -166,11 +173,11 @@ export async function trackEvent(sessionId: string, key: string, value: string):
166173
await send(sessionId, [event]);
167174
}
168175

169-
export async function trackEvents(
176+
export async function trackContentEvents(
170177
sessionId: string,
171178
entries: Array<{ key: string; value: string }>,
172179
): Promise<void> {
173-
if (!isPromptTelemetryEnabled() || entries.length === 0) return;
180+
if (!isContentTelemetryEnabled() || entries.length === 0) return;
174181

175182
const now = Date.now();
176183
const events: TelemetryEvent[] = entries.map((entry) => ({
@@ -182,3 +189,17 @@ export async function trackEvents(
182189

183190
await send(sessionId, events);
184191
}
192+
193+
/**
194+
* Backward-compatible aliases for older callers that still refer to prompt telemetry.
195+
*/
196+
export async function trackEvent(sessionId: string, key: string, value: string): Promise<void> {
197+
await trackContentEvent(sessionId, key, value);
198+
}
199+
200+
export async function trackEvents(
201+
sessionId: string,
202+
entries: Array<{ key: string; value: string }>,
203+
): Promise<void> {
204+
await trackContentEvents(sessionId, entries);
205+
}

hooks/src/user-prompt-submit-telemetry.mts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
#!/usr/bin/env node
22
/**
3-
* UserPromptSubmit hook: prompt telemetry opt-in + prompt text tracking.
3+
* UserPromptSubmit hook: content telemetry opt-in + prompt text tracking.
44
*
55
* Fires on every user message. Two responsibilities:
66
*
77
* 1. Track prompt:text telemetry (awaited) for every prompt >= 10 chars
8-
* when prompt telemetry is enabled. This runs independently of skill
8+
* when content telemetry is enabled. This runs independently of skill
99
* matching so prompts are never silently dropped.
1010
*
1111
* 2. On the first message of a session where the user hasn't recorded a
12-
* prompt telemetry preference, return additionalContext asking the model
12+
* content telemetry preference, return additionalContext asking the model
1313
* to prompt the user for opt-in. Writes "asked" immediately so the user
1414
* is never re-prompted. session-end-cleanup converts "asked" → "disabled".
1515
*
1616
* Note: Base telemetry is enabled by default, but users can disable all
1717
* telemetry with VERCEL_PLUGIN_TELEMETRY=off. This hook only gates prompt
18-
* text collection when telemetry is otherwise enabled.
18+
* text and full bash command collection when telemetry is otherwise enabled.
1919
*
2020
* Input: JSON on stdin with { session_id, prompt }
2121
* Output: JSON on stdout with { hookSpecificOutput: { hookEventName, additionalContext } } or {}
@@ -25,7 +25,7 @@ import type { SyncHookJSONOutput } from "@anthropic-ai/claude-agent-sdk";
2525
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
2626
import { homedir, tmpdir } from "node:os";
2727
import { join, dirname } from "node:path";
28-
import { getTelemetryOverride, isPromptTelemetryEnabled, trackEvents } from "./telemetry.mjs";
28+
import { getTelemetryOverride, isContentTelemetryEnabled, trackContentEvents } from "./telemetry.mjs";
2929

3030
const PREF_PATH = join(homedir(), ".claude", "vercel-plugin-telemetry-preference");
3131
const MIN_PROMPT_LENGTH = 10;
@@ -55,8 +55,8 @@ async function main(): Promise<void> {
5555
const telemetryOverride = getTelemetryOverride();
5656

5757
// Prompt text tracking — opt-in only
58-
if (isPromptTelemetryEnabled() && sessionId && prompt.length >= MIN_PROMPT_LENGTH) {
59-
await trackEvents(sessionId, [
58+
if (isContentTelemetryEnabled() && sessionId && prompt.length >= MIN_PROMPT_LENGTH) {
59+
await trackContentEvents(sessionId, [
6060
{ key: "prompt:text", value: prompt },
6161
]).catch(() => {});
6262
}
@@ -106,13 +106,13 @@ async function main(): Promise<void> {
106106
const message = [
107107
"After responding to the user's message, use the AskUserQuestion tool to ask about telemetry.",
108108
"Use this exact question configuration:",
109-
'- question: "The Vercel plugin collects anonymous usage data such as skill injection patterns and tools used by default. Would you like to also share your prompt text to help improve skill matching? This choice only controls prompt telemetry."',
109+
'- question: "The Vercel plugin collects anonymous usage data such as skill injection patterns and tools used by default. Would you like to also share raw content telemetry, including your prompt text and full bash commands, to help improve skill matching and command understanding? This choice only controls content telemetry."',
110110
'- header: "Telemetry"',
111-
'- options: [{"label": "Share prompts", "description": "Share prompt text in addition to default anonymous usage data"}, {"label": "No thanks", "description": "Keep prompt text private. This only disables prompt telemetry"}]',
111+
'- options: [{"label": "Share content", "description": "Share prompt text and full bash commands in addition to default anonymous usage data"}, {"label": "No thanks", "description": "Keep prompt text and full bash commands private. This only disables content telemetry"}]',
112112
"- multiSelect: false",
113113
"",
114114
"After the user responds:",
115-
"- If they chose \"Share prompts\", run: `echo 'enabled' > ~/.claude/vercel-plugin-telemetry-preference`",
115+
"- If they chose \"Share content\", run: `echo 'enabled' > ~/.claude/vercel-plugin-telemetry-preference`",
116116
"- If they chose \"No thanks\" or anything else, run: `echo 'disabled' > ~/.claude/vercel-plugin-telemetry-preference`",
117117
].join("\n");
118118

0 commit comments

Comments
 (0)