Remove dead analytics and telemetry scaffolding

This commit is contained in:
2026-04-04 02:51:35 +08:00
parent a95f0a540a
commit dccd151718
16 changed files with 118 additions and 2278 deletions

View File

@@ -1,13 +0,0 @@
export function bootstrapTelemetry(): void {}
export function isTelemetryEnabled(): boolean {
return false
}
export async function initializeTelemetry(): Promise<null> {
return null
}
export async function flushTelemetry(): Promise<void> {
return
}

View File

@@ -12,17 +12,10 @@
*/
import { createHash } from 'crypto'
import { sep } from 'path'
import {
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
type AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED,
logEvent,
} from '../../services/analytics/index.js'
import type {
LoadedPlugin,
PluginError,
PluginManifest,
} from '../../types/plugin.js'
import type { PluginManifest } from '../../types/plugin.js'
import {
isOfficialMarketplaceName,
parsePluginIdentifier,
@@ -80,17 +73,6 @@ export function getTelemetryPluginScope(
return 'user-local'
}
/**
* How a plugin arrived in the session. Splits self-selected from org-pushed
* — plugin_scope alone doesn't (an official plugin can be user-installed OR
* org-pushed; both are scope='official').
*/
export type EnabledVia =
| 'user-install'
| 'org-policy'
| 'default-enable'
| 'seed-mount'
/** How a skill/command invocation was triggered. */
export type InvocationTrigger =
| 'user-slash'
@@ -107,24 +89,6 @@ export type InstallSource =
| 'ui-suggestion'
| 'deep-link'
export function getEnabledVia(
plugin: LoadedPlugin,
managedNames: Set<string> | null,
seedDirs: string[],
): EnabledVia {
if (plugin.isBuiltin) return 'default-enable'
if (managedNames?.has(plugin.name)) return 'org-policy'
// Trailing sep: /opt/plugins must not match /opt/plugins-extra
if (
seedDirs.some(dir =>
plugin.path.startsWith(dir.endsWith(sep) ? dir : dir + sep),
)
) {
return 'seed-mount'
}
return 'user-install'
}
/**
* Common plugin telemetry fields keyed off name@marketplace. Returns the
* hash, scope enum, and the redacted-twin columns. Callers add the raw
@@ -165,10 +129,7 @@ export function buildPluginTelemetryFields(
/**
* Per-invocation callers (SkillTool, processSlashCommand) pass
* managedNames=null — the session-level tengu_plugin_enabled_for_session
* event carries the authoritative plugin_scope, and per-invocation rows can
* join on plugin_id_hash to recover it. This keeps hot-path call sites free
* of the extra settings read.
* managedNames=null to keep hot-path call sites free of the extra settings read.
*/
export function buildPluginCommandTelemetryFields(
pluginInfo: { pluginManifest: PluginManifest; repository: string },
@@ -182,47 +143,6 @@ export function buildPluginCommandTelemetryFields(
)
}
/**
* Emit tengu_plugin_enabled_for_session once per enabled plugin at session
* start. Supplements tengu_skill_loaded (which still fires per-skill) — use
* this for plugin-level aggregates instead of DISTINCT-on-prefix hacks.
* A plugin with 5 skills emits 5 skill_loaded rows but 1 of these.
*/
export function logPluginsEnabledForSession(
plugins: LoadedPlugin[],
managedNames: Set<string> | null,
seedDirs: string[],
): void {
for (const plugin of plugins) {
const { marketplace } = parsePluginIdentifier(plugin.repository)
logEvent('tengu_plugin_enabled_for_session', {
_PROTO_plugin_name:
plugin.name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED,
...(marketplace && {
_PROTO_marketplace_name:
marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED,
}),
...buildPluginTelemetryFields(plugin.name, marketplace, managedNames),
enabled_via: getEnabledVia(
plugin,
managedNames,
seedDirs,
) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
skill_path_count:
(plugin.skillsPath ? 1 : 0) + (plugin.skillsPaths?.length ?? 0),
command_path_count:
(plugin.commandsPath ? 1 : 0) + (plugin.commandsPaths?.length ?? 0),
has_mcp: plugin.manifest.mcpServers !== undefined,
has_hooks: plugin.hooksConfig !== undefined,
...(plugin.manifest.version && {
version: plugin.manifest
.version as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
}),
})
}
}
/**
* Bounded-cardinality error bucket for CLI plugin operation failures.
* Maps free-form error messages to 5 stable categories so dashboard
@@ -257,33 +177,3 @@ export function classifyPluginCommandError(
}
return 'unknown'
}
/**
* Emit tengu_plugin_load_failed once per error surfaced by session-start
* plugin loading. Pairs with tengu_plugin_enabled_for_session so dashboards
* can compute a load-success rate. PluginError.type is already a bounded
* enum — use it directly as error_category.
*/
export function logPluginLoadErrors(
errors: PluginError[],
managedNames: Set<string> | null,
): void {
for (const err of errors) {
const { name, marketplace } = parsePluginIdentifier(err.source)
// Not all PluginError variants carry a plugin name (some have pluginId,
// some are marketplace-level). Use the 'plugin' property if present,
// fall back to the name parsed from err.source.
const pluginName = 'plugin' in err && err.plugin ? err.plugin : name
logEvent('tengu_plugin_load_failed', {
error_category:
err.type as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
_PROTO_plugin_name:
pluginName as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED,
...(marketplace && {
_PROTO_marketplace_name:
marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED,
}),
...buildPluginTelemetryFields(pluginName, marketplace, managedNames),
})
}
}

View File

@@ -1,39 +0,0 @@
import { getSkillToolCommands } from '../../commands.js'
import {
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
type AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED,
logEvent,
} from '../../services/analytics/index.js'
import { getCharBudget } from '../../tools/SkillTool/prompt.js'
/**
* Logs a tengu_skill_loaded event for each skill available at session startup.
* This enables analytics on which skills are available across sessions.
*/
export async function logSkillsLoaded(
cwd: string,
contextWindowTokens: number,
): Promise<void> {
const skills = await getSkillToolCommands(cwd)
const skillBudget = getCharBudget(contextWindowTokens)
for (const skill of skills) {
if (skill.type !== 'prompt') continue
logEvent('tengu_skill_loaded', {
// _PROTO_skill_name routes to the privileged skill_name BQ column.
// Unredacted names don't go in additional_metadata.
_PROTO_skill_name:
skill.name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED,
skill_source:
skill.source as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
skill_loaded_from:
skill.loadedFrom as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
skill_budget: skillBudget,
...(skill.kind && {
skill_kind:
skill.kind as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
}),
})
}
}