Skip to content

feat(usage): add feature attribution header to API requests#407

Open
pedroheyerdahl wants to merge 2 commits intodevfrom
feat/usage-feature-attribution
Open

feat(usage): add feature attribution header to API requests#407
pedroheyerdahl wants to merge 2 commits intodevfrom
feat/usage-feature-attribution

Conversation

@pedroheyerdahl
Copy link
Contributor

Summary

Adds the X-KILOCODE-FEATURE header to all LLM requests going through kilo-gateway, enabling per-feature attribution of token usage in microdollar_usage. This is Step 7 of the Microdollar Feature Tracking plan.

Problem

There's no way to distinguish which feature generates each token usage record in microdollar_usage. All requests from the CLI, VS Code extension, and cloud features look identical at the gateway level.

Solution

Every LLM request now includes an X-KILOCODE-FEATURE header identifying the calling feature. The value is determined by the KILOCODE_FEATURE environment variable, which callers set before spawning the kilo CLI process.

Changes

packages/kilo-gateway/src/api/constants.ts

  • Added HEADER_FEATURE, DEFAULT_FEATURE, ENV_FEATURE constants

packages/kilo-gateway/src/headers.ts

  • Added getFeatureHeader() — reads process.env.KILOCODE_FEATURE, returns undefined when not set
  • buildKiloHeaders() conditionally includes the feature header only when the env var is set
  • No default value in getFeatureHeader() to prevent misattribution — missing env var = no header = NULL in DB

packages/kilo-gateway/src/server/routes.ts

  • FIM route (/kilo/fim) now includes buildKiloHeaders() + hardcoded [HEADER_FEATURE]: "autocomplete" override
  • Previously this route made raw fetch() calls without any kilo headers

packages/kilo-vscode/src/services/cli-backend/server-manager.ts

  • Added KILOCODE_FEATURE: "vscode-extension" to the spawn env when launching the kilo CLI process

packages/opencode/src/index.ts

  • CLI entry point sets process.env.KILOCODE_FEATURE = "cli" if not already set by a caller

packages/kilo-gateway/src/index.ts

  • Exported new constants and getFeatureHeader

packages/kilo-gateway/src/headers.test.ts (new)

  • 7 tests covering getFeatureHeader() and buildKiloHeaders() feature header behavior

Testing

  • 7/7 automated tests pass (bun test src/headers.test.ts)
  • TypeScript typecheck passes

Introduce X-KILOCODE-FEATURE header for tracking request origin
across different entry points (CLI, VSCode extension, autocomplete).

- Add HEADER_FEATURE, DEFAULT_FEATURE, and ENV_FEATURE constants
- Add getFeatureHeader() helper reading from KILOCODE_FEATURE env var
- Include feature header in buildKiloHeaders() when env var is set
- Set feature to "autocomplete" for FIM/completion requests
- Set KILOCODE_FEATURE="vscode-extension" when spawning CLI from VSCode
- Default to "cli" feature in opencode entry point
- Add unit tests for feature header logic
@kiloconnect
Copy link
Contributor

kiloconnect bot commented Feb 18, 2026

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/index.ts 38 process.argv.includes("serve") matches anywhere in argv, not just the command position. Use process.argv[2] === "serve" for precision.

SUGGESTION

File Line Issue
packages/kilo-vscode/src/services/cli-backend/server-manager.ts 69 Hardcoded "KILOCODE_FEATURE" string instead of using ENV_FEATURE constant from @kilocode/kilo-gateway.
Files Reviewed (7 files)
  • packages/kilo-gateway/src/api/constants.ts - 0 issues
  • packages/kilo-gateway/src/headers.ts - 0 issues
  • packages/kilo-gateway/src/index.ts - 0 issues
  • packages/kilo-gateway/src/server/routes.ts - 0 issues
  • packages/kilo-vscode/src/services/cli-backend/server-manager.ts - 1 issue
  • packages/opencode/src/index.ts - 1 issue
  • packages/opencode/test/kilocode/kilo-gateway-headers.test.ts - 0 issues

Fix these issues in Kilo Cloud

export const HEADER_FEATURE = "X-KILOCODE-FEATURE"

/** Default feature value (CLI usage) */
export const DEFAULT_FEATURE = "cli"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if we should instead of a default use 'unknown' so that when new products roll out and we forget this change, we see it in the data

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, and I thought I had changed that. thanks for catching!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I've found to derive cli feature while keeping misattributed new products as unkjonwn is to check if env var is not set and there's no serve command. does that make sense to you?

…rom command context

Remove the hardcoded DEFAULT_FEATURE ("cli") constant from kilo-gateway
and instead derive the feature value at runtime in opencode. When the
KILOCODE_FEATURE env var is not set, the "serve" command is now tagged
as "unknown" (to surface misconfiguration when a caller forgets to set
the env var), while all other commands default to "cli".
// (any command other than 'serve') is tagged as 'cli'. If 'serve' is spawned without
// the env var, it gets 'unknown' so the misconfiguration is visible in data.
if (!process.env[ENV_FEATURE]) {
const isServe = process.argv.includes("serve")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: process.argv.includes("serve") matches "serve" anywhere in the argument list, not just as the yargs command. If a future flag or argument value happens to be "serve", this would incorrectly tag the process as "unknown" instead of "cli".

A more precise check:

Suggested change
const isServe = process.argv.includes("serve")
const isServe = process.argv[2] === "serve"

This targets the yargs command position directly (argv[0] = node, argv[1] = script, argv[2] = command).

...process.env,
KILO_SERVER_PASSWORD: password,
KILO_CLIENT: "vscode",
KILOCODE_FEATURE: "vscode-extension", // kilocode_change - feature tracking
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: The env var name "KILOCODE_FEATURE" is hardcoded here as a string literal, while other packages use the ENV_FEATURE constant from @kilocode/kilo-gateway. If the env var name ever changes, this would need a manual update.

Consider importing and using the constant for consistency, or leave a comment noting the intentional duplication if importing from kilo-gateway isn't desirable in this package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments