# How the Kimi-Code Telemetry Event System Works: Architecture, Client API, and Naming Conventions

> Explore the kimi-code telemetry event system. Learn about its compile-time registry, TypeScript naming conventions, singleton client, and pluggable sinks for efficient event delivery.

- Repository: [Moonshot AI/kimi-code](https://github.com/MoonshotAI/kimi-code)
- Tags: internals
- Published: 2026-08-16

---

**The kimi-code telemetry system uses a compile-time event registry with strict TypeScript-enforced naming conventions, a singleton `TelemetryClient` for buffering and sanitization, and pluggable sinks for event delivery.**

This deep dive into the [MoonshotAI/kimi-code](https://github.com/MoonshotAI/kimi-code) repository explores how the telemetry event system is architected, how developers should interact with it, and the exact naming conventions that must be followed to maintain type safety and data privacy compliance.

## Core Architecture: Three-Layer Design

The telemetry system separates concerns into distinct layers that work together to ensure type-safe event collection.

### Event Registry

All telemetry events must be declared in the compile-time registry at [`packages/agent-core-v2/src/app/telemetry/events.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/app/telemetry/events.ts). The `telemetryEventDefinitions` constant holds the complete list of valid events, with each entry created through factory functions:

- `defineTelemetryEvent()` — for plain, global events
- `defineAgentTelemetryEvent()` — for agent-scoped events that automatically include `agent_id`

The registry exports helper types (`TelemetryEventName`, `TelemetryEventPayload`, `TelemetryEventProperties`) that let the rest of the codebase infer exact payload shapes from event names. This eliminates string-typing errors and guarantees that every tracked event matches its declared schema.

### Telemetry Client

The `TelemetryClient` singleton in [`packages/telemetry/src/client.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/telemetry/src/client.ts) (lines 20–30) handles runtime event processing:

| Capability | Implementation |
|------------|----------------|
| Context management | `setContext()` / `withContext()` for device and session IDs |
| Event recording | `track(event, properties)` and `trackWithContext()` methods |
| Queueing | In-memory buffer with `MAX_QUEUE_SIZE = 1000`, oldest-events dropped on overflow |
| Sanitization | `sanitizeProperties()` function (lines 89–96) strips non-primitive values |
| Shutdown | `flush()` and `shutdown()` with configurable timeout |

When `track()` is called, the client generates a UUID for `event_id`, populates context identifiers, runs sanitization, wraps the result in a `TelemetryEvent` object, and either queues it or passes it directly to the attached sink.

### Transport and Sink Layer

Concrete delivery implementations live in [`packages/telemetry/src/transport.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/telemetry/src/transport.ts) (HTTP remote transport) and [`packages/telemetry/src/sink.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/telemetry/src/sink.ts) (file-based local buffer). The sink interface allows custom backends without modifying the core client.

## Telemetry Event Naming Conventions

The following conventions are enforced by TypeScript at compile time and documented in the header of [`events.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/events.ts) (lines 11–14).

### Event Names: `snake_case`

Every event identifier must use lowercase snake_case.

- ✅ `turn_started`
- ✅ `tool_call`
- ❌ `turnStarted`
- ❌ `ToolCall`

### Property Keys: `snake_case`

All payload property keys follow the same casing rule.

### Unit Suffixes for Numeric Properties

When a number represents a measurable quantity, append the unit:

| Suffix | Use Case | Example |
|--------|----------|---------|
| `_ms` | Durations | `duration_ms`, `latency_ms` |
| `_count` | Cardinalities | `retry_count`, `token_count` |
| `_bytes` | Data sizes | `payload_bytes`, `heap_bytes` |

### Privacy Rule: No User Content

**Telemetry properties must never contain** raw user input, file paths, personally identifiable information, or any data that could identify an individual. The `sanitizeProperties` function provides runtime defense, but compile-time review is the primary safeguard.

### Agent-Scoped Events

Use `defineAgentTelemetryEvent()` for any event emitted within an agent context. This automatically injects `agent_id` via `AgentTelemetryEventContext` and `agentTelemetryContextProperties`, ensuring consistent attribution without manual key management.

## Code Examples: Recording Telemetry Events

### Basic Global Event

```typescript
import { track } from '#/telemetry/client';

// Fire-and-forget recording of a session lifecycle event
track('session_started', { resumed: true });

```

### Agent-Scoped Event with Full Context

```typescript
import { withContext } from '#/telemetry/client';

const agentClient = withContext({ 
  deviceId: 'dev-123', 
  sessionId: 'sess-456' 
});

agentClient.track('turn_started', {
  turn_id: 7,
  mode: 'agent',
  duration_ms: 125,  // Unit suffix mandatory
});

```

### Custom File Sink Integration

```typescript
import { attachSink } from '#/telemetry/client';
import { FileSink } from '#/telemetry/sink';

const sink = new FileSink('/tmp/telemetry.log');
attachSink(sink);

// All subsequent track() calls persist to disk

```

## Type Safety Enforcement

The system uses `TelemetryEventMeta` in [`events.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/events.ts) to type `properties` as `Readonly<Record<string, string>>` where keys must exactly match the declared payload interface. This produces compile-time errors for:

- Misspelled property names
- Missing required fields
- Incorrect value types
- Violation of unit suffix conventions (via naming lint rules)

## Key Source Files

| File Path | Responsibility |
|-----------|---------------|
| [`packages/agent-core-v2/src/app/telemetry/events.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/app/telemetry/events.ts) | Event registry, naming convention definitions, type helpers |
| [`packages/telemetry/src/client.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/telemetry/src/client.ts) | Core client: queueing, sanitization, context injection |
| [`packages/telemetry/src/types.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/telemetry/src/types.ts) | Primitive types: `TelemetryPrimitive`, `TelemetryProperties`, `TelemetryEvent` |
| [`packages/telemetry/src/sink.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/telemetry/src/sink.ts) | Sink interface and file-based implementation |
| [`packages/telemetry/src/transport.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/telemetry/src/transport.ts) | HTTP transport for server upload |

## Summary

- **Event registration** happens at compile time in [`events.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/events.ts) using `defineTelemetryEvent` or `defineAgentTelemetryEvent`
- **Runtime recording** flows through the `TelemetryClient` singleton with automatic UUID generation, context injection, and primitive-only sanitization
- **Naming conventions** (`snake_case`, unit suffixes, privacy exclusions) are TypeScript-enforced and prevent schema drift
- **Extensible delivery** via the sink pattern supports file, HTTP, or custom backends without core changes

## Frequently Asked Questions

### How do I add a new telemetry event to the codebase?

Define it in [`packages/agent-core-v2/src/app/telemetry/events.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core-v2/src/app/telemetry/events.ts) using `defineTelemetryEvent()` for global events or `defineAgentTelemetryEvent()` for agent-scoped events. The registry entry specifies the exact payload shape; TypeScript will then enforce this shape on all `track()` calls throughout the codebase.

### What happens if I send the wrong property type in a telemetry event?

The `sanitizeProperties` function (lines 89–96 of [`client.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/client.ts)) strips any value that isn't a `TelemetryPrimitive` (`boolean | number | string | undefined | null`). Non-primitives are silently dropped to prevent serialization failures and data leakage. For type safety, the registry should catch most mismatches at compile time.

### Can I include user file paths in telemetry for debugging purposes?

No. The naming conventions explicitly forbid raw user data, file paths, and personally identifiable information in telemetry properties. This is a hard privacy requirement, not merely a style guideline. Use anonymized identifiers or hashes if path correlation is absolutely necessary.

### How does the telemetry queue handle high-volume scenarios?

The client maintains an in-memory queue with a hard limit of 1,000 events (`MAX_QUEUE_SIZE`). When this limit is exceeded, the oldest events are unconditionally dropped. For production deployments, configure a persistent sink (file or HTTP) and call `flush()` or `shutdown(timeout)` during application lifecycle events to minimize data loss.