Skip to main content

Local Telemetry

What This Captures

The local telemetry hook captures Copilot lifecycle events into local JSONL files. It is intended for local analysis and troubleshooting of your own sessions.

The telemetry manifest is at .github/hooks/shared/telemetry.json.

Events currently captured include:

  • session start
  • user prompt submission
  • pre-tool and post-tool use
  • subagent start and stop
  • agent stop and session end
  • pre-compact events

At stop time, telemetry also appends a session summary with model and token usage when available.

Enable Local Telemetry

Telemetry is opt-in. Enable it with either an environment variable or a repository marker file.

Option 1: Environment Variable

export HVE_TELEMETRY=1
$env:HVE_TELEMETRY = "1"

Option 2: Repository Marker File

Create .hve-telemetry at the repository root:

touch .hve-telemetry

Either option enables collection. If both are absent, the hook exits in no-op mode.

Optional: Verbatim Raw Payload Capture

Processed telemetry never stores full prompt text or full tool inputs (see Sensitive Data and Privacy). A separate, explicit opt-in records the first few hook payloads verbatim to raw-input.jsonl for deep diagnostics. It is off by default, even when telemetry is enabled, and is honored only by the Bash collector:

export HVE_TELEMETRY_RAW=1

Leave this unset unless you are actively debugging the hook payload shape, and remove the captured file afterward. Because it stores prompts and tool inputs in the clear, treat any session run with it enabled as potentially sensitive.

View Reports

Generate a report with the script in ~/.hve (created at session start):

bash ~/.hve/generate-report.sh

Which will generate a report.generated.html for viewing.

The generated report path is printed when report generation completes.

The report is self-contained: it embeds every selected JSONL file (session events plus model and token enrichment) inline. Combined cross-project reports over a long history (--all-dirs --date all) can therefore grow to several megabytes. Narrow the scope with a specific --date, or report a single project without --all-dirs, when a smaller file is preferred.

Disable Local Telemetry

Disable collection by removing both enablement gates:

  1. Unset HVE_TELEMETRY
  2. Remove .hve-telemetry from repository root

Where Data Is Written

Default output directory:

<repo>/.copilot-tracking/telemetry

Override with HVE_TELEMETRY_DIR when needed.

Key files and folders:

PathPurpose
sessions-YYYY-MM-DD.jsonlDaily event stream with hook events and session summaries
raw-input.jsonlFirst few hook payloads stored verbatim; written only when HVE_TELEMETRY_RAW=1 is set (Bash collector)
.stacks/Per-session agent stack tracking used for attribution
report.generated.htmlOptional self-contained report output

Data Captured and Storage Schema

This section describes the mechanics of what local telemetry collects and where each class of data comes from.

Collection Pipeline

  1. Copilot lifecycle events invoke the telemetry hook from .github/hooks/shared/telemetry.json.
  2. Shell entry points (telemetry-collector.sh and Invoke-TelemetryCollector.ps1) enforce opt-in gates.
  3. Event payloads are normalized and appended to daily JSONL files.
  4. On stop events, a SessionSummary record is appended with model and token aggregates when available.

Core Record Types

The daily JSONL stream contains two primary record types:

Record TypeTriggerPurpose
Hook event recordsSession/tool/subagent lifecycle eventsTimeline of what happened during a session
SessionSummaryStop event (Stop)Aggregated usage totals and model-level summary

Common Fields

Most hook event records include:

  • ts: Event timestamp (ISO 8601)
  • sid: Session identifier
  • event: Canonical event name (for example, PreToolUse, PostToolUse, SessionStart)
  • cwd: Working directory at capture time

Additional fields are event-specific. Examples:

  • Prompt events: truncated prompt preview
  • Tool events: tool name, selected input keys, response length, inferred agent attribution
  • Subagent events: agent name and display name
  • Stop events: stop reason

Session Summary Fields

When available at stop time, SessionSummary includes:

  • models: Model usage map observed during the session
  • model_usage: Per-model aggregate usage counters
  • input_tokens, output_tokens
  • cache_read_tokens, cache_write_tokens
  • total_nano_aiu
  • turns, messages
  • Optional reasoning_effort, subagent_map, and client

Data Sources by Layer

Data CategorySource
Hook lifecycle eventsCopilot hook payloads routed through collector scripts
Session summaries.copilot/session-state/<sid>/events.jsonl (CLI session state)
Additional model/token enrichment for reportsVS Code debug logs and session-state aggregation during report generation

Event Naming Normalization

The pipeline normalizes different casing variants of event names to canonical names used in stored telemetry records. This keeps mixed-client event surfaces queryable from one schema.

Storage and Retention Behavior

  • Data is stored locally under .copilot-tracking/telemetry by default.
  • Records append to date-partitioned files (sessions-YYYY-MM-DD.jsonl).
  • A small verbatim raw payload sample is stored in raw-input.jsonl only when HVE_TELEMETRY_RAW=1 is explicitly set; see Sensitive Data and Privacy.
  • Per-session agent stack files are maintained under .stacks/ for attribution and cleaned up on session stop.

Sensitive Data and Privacy

Local telemetry writes plaintext JSONL to local disk only. It makes no network calls and the default output directory (.copilot-tracking/telemetry) is gitignored, so data is not committed. The risk is local-disk exposure, not a committed leak. Be aware of what each layer records:

  • Processed stream (sessions-*.jsonl) stores a truncated prompt preview (first 200 characters of each submitted prompt) and, for tool events, only the tool input key names plus selected fields such as file paths and subagent names. It does not store full tool input values (file contents or shell command strings). A secret pasted into the start of a prompt can still appear in the 200-character preview.
  • Verbatim raw dump (raw-input.jsonl) stores the first few hook payloads exactly as received, including the full prompt and the full tool input (file contents being written, shell command strings). It is off by default and only written when HVE_TELEMETRY_RAW=1 is set.
  • User-level locations under ~/.hve and ~/.copilot (honoring HVE_HOME) hold the report generator and directory registry. Generated reports embed the captured JSONL inline.

To reduce exposure: keep HVE_TELEMETRY_RAW unset, avoid pasting secrets into prompts while telemetry is enabled, and remove captured files when you are done (bash ~/.hve/clean-telemetry.sh or delete the telemetry directory).

Generate a Report

Run the report generator directly:

bash .github/hooks/shared/telemetry/generate-telemetry-report.sh --help
bash .github/hooks/shared/telemetry/generate-telemetry-report.sh --date all
bash .github/hooks/shared/telemetry/generate-telemetry-report.sh --open

On Windows (or any PowerShell host) the native equivalent needs no bash:

pwsh .github/hooks/shared/telemetry/Invoke-TelemetryReport.ps1 -Date all
pwsh .github/hooks/shared/telemetry/Invoke-TelemetryReport.ps1 -Open

Cross-Project Reports

Telemetry is captured per project, so each repository keeps its own store under <repo>/.copilot-tracking/telemetry. To view sessions across every project in a single report, each store is recorded once per session in a user-level registry at ~/.hve/telemetry-dirs.txt (honoring HVE_HOME).

Generate a combined, cross-project report with --all-dirs:

bash .github/hooks/shared/telemetry/generate-telemetry-report.sh --all-dirs --date all

The PowerShell generator takes -AllDirs for the same cross-project report:

pwsh .github/hooks/shared/telemetry/Invoke-TelemetryReport.ps1 -AllDirs -Date all

The registry self-populates as you work across repositories, so no manual setup is required. Stale directories (deleted or moved repositories) are pruned automatically when the report runs. Each session is labeled with its originating project in the report, so combined output still reads per project.

NOTE

Registry-driven cleanup is name-constrained. clean-telemetry.sh --all-dirs iterates every path in ~/.hve/telemetry-dirs.txt and, in each directory, removes only a fixed allow-list of artifact names (raw-input.jsonl, report.generated.html, sessions-*.jsonl, and the .stacks/ directory). It never deletes a directory wholesale. A tampered registry can therefore, at most, delete those specific names in an attacker-chosen directory, not arbitrary files. The .stacks/ entry is removed recursively, but symlinked artifacts are unlinked rather than followed, so the target of a symlink is never deleted. The registry lives in the user-owned HVE home (~/.hve, honoring HVE_HOME), so an attacker able to tamper with it already holds the user's filesystem privileges; the risk is low and the blast radius is bounded.

Reports Without the Repository (Extension Users)

When telemetry runs from the VS Code extension rather than this repository, the report generator lives at a version-pinned extension path that is awkward to locate. To bridge this, a cross-project launcher is written into the HVE home directory (~/.hve, honoring HVE_HOME) at session start, next to the registry it reads:

  • ~/.hve/generate-report.sh: for unix shells and Git Bash on Windows
  • ~/.hve/generate-report.ps1: for PowerShell, runs natively (no bash required)

Run the launcher from the HVE home directory without knowing the extension path. It defaults to a combined, cross-project report written to ~/.hve/report.generated.html:

bash ~/.hve/generate-report.sh
bash ~/.hve/generate-report.sh --date all

From PowerShell, run the native launcher:

~/.hve/generate-report.ps1
~/.hve/generate-report.ps1 -Date all

The launchers are regenerated every session, so they self-heal after an extension upgrade. They forward any extra arguments to the report generator.

Troubleshooting

Common issues:

  • No events captured: verify one enablement gate is set and your hook manifest is active.
  • No enrichment data: model and token enrichment depends on available debug logs and session-state data.
  • Report generation fails: ensure python3 is available for enrichment. The bash generator also needs jq; the PowerShell generator (Invoke-TelemetryReport.ps1) does not.

🤖 Crafted with precision by ✨Copilot following brilliant human instruction, then carefully refined by our team of discerning human reviewers.