Skip to content

feat: record user agent in AI Bridge interceptions#21769

Closed
pawbana wants to merge 2 commits intographite-base/21769from
pb/aibridge-user-agent-recording
Closed

feat: record user agent in AI Bridge interceptions#21769
pawbana wants to merge 2 commits intographite-base/21769from
pb/aibridge-user-agent-recording

Conversation

@pawbana
Copy link
Contributor

@pawbana pawbana commented Jan 29, 2026

Depends on: coder/aibridge#158

Copy link
Contributor Author

pawbana commented Jan 29, 2026

@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch from ab6f2da to a38f534 Compare January 29, 2026 15:51
@pawbana pawbana changed the title chore: record user agent in interception metadata feat: record user agent in AI Bridge interceptions Jan 29, 2026
@pawbana pawbana changed the base branch from main to graphite-base/21769 February 2, 2026 11:37
@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch from a38f534 to 9b520cc Compare February 2, 2026 11:37
@pawbana pawbana changed the base branch from graphite-base/21769 to pb/aibridge-user-agent-db February 2, 2026 11:37
@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch from 9b520cc to b92ddf2 Compare February 3, 2026 17:13
@pawbana pawbana force-pushed the pb/aibridge-user-agent-db branch from 9286dc9 to d52be35 Compare February 3, 2026 17:13
@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch from b92ddf2 to af367a7 Compare February 3, 2026 17:24
@pawbana pawbana force-pushed the pb/aibridge-user-agent-db branch 2 times, most recently from 441ee12 to f287ce3 Compare February 3, 2026 17:26
@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch from af367a7 to 5762143 Compare February 3, 2026 17:26
@pawbana pawbana force-pushed the pb/aibridge-user-agent-db branch from f287ce3 to b6f4775 Compare February 3, 2026 17:38
@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch 2 times, most recently from 9270649 to 2a4df49 Compare February 3, 2026 17:43
@pawbana pawbana marked this pull request as ready for review February 3, 2026 17:56
Copy link
Contributor

@coder-tasks coder-tasks bot left a comment

Choose a reason for hiding this comment

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

Code Review

Reviewed the implementation of user agent recording in AI Bridge interceptions. The changes look solid overall - the dual approach of storing both the parsed Client field and the raw UserAgent in metadata provides good flexibility for analytics and debugging.

Summary

Strengths:

  • Comprehensive test coverage with both "all optional values set" and "no optional values set" test cases
  • Proper handling of nullable fields using sql.NullString
  • Consistent API schema updates across swagger, codersdk, and TypeScript types
  • Good separation between structured Client field and raw UserAgent metadata

⚠️ Issues Found: 2 minor issues

Issues

  1. Inconsistent user agent handling in test - The test expects "unknown" as the client value but doesn't verify the actual user agent parsing logic
  2. Missing nil check in translator - The proto translator doesn't validate that req.UserAgent is non-empty before adding to metadata

Questions

  • The PR depends on coder/aibridge#158 - has that been merged? The aibridge.InterceptionRecord struct needs to have Client and UserAgent fields for this to compile.
  • What's the parsing logic that converts UserAgentClient? Is that handled in the aibridge dependency or elsewhere in this codebase?

require.True(t, intc0.EndedAt.Valid)
require.False(t, intc0.EndedAt.Time.Before(intc0.StartedAt), "EndedAt should not be before StartedAt")
require.Less(t, intc0.EndedAt.Time.Sub(intc0.StartedAt), 5*time.Second)
require.True(t, intc0.Client.Valid)
Copy link
Contributor

Choose a reason for hiding this comment

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

The test expects "unknown" as the client value, but the test setup sends "userAgent123" as the User-Agent header. This suggests there's user agent parsing logic that should be tested more explicitly.

Consider:

  1. Testing with a real user agent string that would parse to a known client (e.g., "claude-code/1.0.0")
  2. Adding a test case that verifies the parsing logic works correctly
  3. Documenting what the expected format is and what "unknown" means

This would make the test more meaningful and verify the actual user agent parsing behavior.

Comment on lines 136 to 141
if in.UserAgent != "" {
if _, ok := metadata[MetadataUserAgentKey]; ok {
s.logger.Warn(ctx, fmt.Sprintf("interception metadata contains user agent key, will be overwritten"))
}
metadata[MetadataUserAgentKey] = in.UserAgent
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The code overwrites the metadata key if it already exists with a warning, but this only happens if in.UserAgent != "". Consider being consistent:

Either:

  1. Always check for key conflicts (even with empty user agent), or
  2. Skip the entire block if user agent is empty

Current code has a gap: if UserAgent is empty, the existing metadata key would be preserved silently.

Suggested change
if in.UserAgent != "" {
if _, ok := metadata[MetadataUserAgentKey]; ok {
s.logger.Warn(ctx, fmt.Sprintf("interception metadata contains user agent key, will be overwritten"))
}
metadata[MetadataUserAgentKey] = in.UserAgent
}
if in.UserAgent != "" {
if _, ok := metadata[MetadataUserAgentKey]; ok {
s.logger.Warn(ctx, "interception metadata contains user agent key, will be overwritten")
}
metadata[MetadataUserAgentKey] = in.UserAgent
}

This change makes the conditional more explicit and groups the warning with the overwrite operation.

@pawbana pawbana force-pushed the pb/aibridge-user-agent-db branch from b6f4775 to e7fa5ea Compare February 3, 2026 18:04
@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch from 2a4df49 to c1deb9a Compare February 3, 2026 18:04
Copy link
Contributor

@dannykopping dannykopping left a comment

Choose a reason for hiding this comment

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

LGTM!


const (
InterceptionLogMarker = "interception log"
MetadataUserAgentKey = "aib-request-user-agent"
Copy link
Contributor

Choose a reason for hiding this comment

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

aib will be meaningless to most folks.

Suggested change
MetadataUserAgentKey = "aib-request-user-agent"
MetadataUserAgentKey = "request_user_agent"


metadata := metadataToMap(in.GetMetadata())

if in.UserAgent != "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

You could just log this as its own key in the structured log output I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted to keep the same schema the same between log and DB.
I could add it as separate field and even store it in separate column instead of keeping it as part of metadata column.

expected codersdk.AIBridgeInterception
}{
{
name: "all_optional_values_set",
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for adding these 👍


metadata := metadataToMap(in.GetMetadata())

if in.UserAgent != "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is related to my other comment, but this contains the User-Agent header, which can be something like copilot/0.0.403 (client/cli linux v24.11.1), which is more relevant as metadata. Couldn't we just add it directly to actor.Metadata in aibridge? That would let us skip the new UserAgent field in InterceptionRecord, the proto changes, and the server-side metadata injection here entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In case AI Bridge is running outside of coder I'd rather keep current setup.

@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch 2 times, most recently from 880c6b1 to 524db0c Compare February 5, 2026 16:06
@pawbana pawbana force-pushed the pb/aibridge-user-agent-db branch from e7fa5ea to 4c30f20 Compare February 5, 2026 16:06
@github-actions github-actions bot added the stale This issue is like stale bread. label Feb 13, 2026
@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch from 524db0c to 7c672c5 Compare February 16, 2026 15:58
@pawbana pawbana force-pushed the pb/aibridge-user-agent-db branch from 4c30f20 to 6778eb9 Compare February 16, 2026 15:58
@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch from 7c672c5 to 63f0a27 Compare February 16, 2026 16:04
@pawbana pawbana force-pushed the pb/aibridge-user-agent-db branch from 6778eb9 to 1a7d40b Compare February 16, 2026 16:04
@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch from 63f0a27 to 3d8e453 Compare February 16, 2026 16:33
@github-actions github-actions bot removed the stale This issue is like stale bread. label Feb 17, 2026
@pawbana pawbana force-pushed the pb/aibridge-user-agent-recording branch from 3d8e453 to 670801f Compare February 17, 2026 10:03
@pawbana pawbana force-pushed the pb/aibridge-user-agent-db branch from 8206db2 to 42b467a Compare February 17, 2026 10:03
@pawbana pawbana changed the base branch from pb/aibridge-user-agent-db to graphite-base/21769 February 17, 2026 13:46
@pawbana
Copy link
Contributor Author

pawbana commented Feb 17, 2026

Merged in: #21839

@pawbana pawbana closed this Feb 17, 2026
@github-actions github-actions bot locked and limited conversation to collaborators Feb 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants