packages/kilo-docs/pages/contributing/architecture/per-message-feedback.md
We have no signal on which assistant responses are helpful and which aren't. Without per-response feedback, we can't:
Aggregate metrics like session completion rate or token cost are too coarse to understand individual response quality. A lightweight thumbs-up/down on each message can help close the feedback loop.
Add a thumbs-up / thumbs-down widget next to the existing copy button on every assistant message. Ratings are sent to Kilo via the existing PostHog telemetry pipeline. The UI is hidden entirely when telemetry is disabled.
| Surface | Approach |
|---|---|
| VS Code extension | Thumbs buttons inline next to the copy button |
| TUI | Keybinds (<leader>= / <leader>-) on the last assistant message |
We deliberately collect fewer identifiers for non-Kilo providers, since those IDs can't be correlated to upstream data and add tracking surface without product benefit. Users of non-Kilo GW models would also not expect or want us to collect that information in Kilo GW from other providers.
Third party providers (Anthropic, OpenAI, local, etc.):
providerID, modelID, variant?, rating, previousRating?
Kilo Gateway turns (providerID starts with "kilo"):
Same fields plus sessionID, messageID, and parentMessageID (= the x-kilo-request header the gateway already saw). This lets backend analysts join feedback against gateway logs to diagnose specific bad responses.
Event name: "Feedback Submitted" — a single event string in both telemetry enum registries so PostHog sees one event regardless of source.
rating and previousRating.[webview button / TUI keybind]
→ existing telemetry proxy or Telemetry.track()
→ POST /telemetry/capture (webview path)
→ Telemetry.track("Feedback Submitted", {…})
→ PostHog
No new server endpoints, no SDK regeneration, no PostHog-side changes. The /telemetry/capture route and both telemetry proxy paths already exist and accept arbitrary event names.
The webview uses providerID.startsWith("kilo") to decide whether to include correlation IDs — this matches the outbound header gating in packages/opencode/src/session/llm.ts. The TUI can use the more precise model.api.npm === "@kilocode/kilo-gateway" check since it has access to the full provider resolution in-process.
MessageV2.Assistant schema so they survive reloads?