Skip to content

Commit 227257d

Browse files
committed
Add harness user-agent dimension for omnigent meta-harness
Detect the OMNIGENT environment variable and report harness/omnigent as an independent user-agent dimension, parallel to agent detection. This lets the platform see both the meta-harness (omnigent) and the underlying coding agent (e.g. claude-code) in the same request. Co-authored-by: Isaac Signed-off-by: simon <simon.faltum@databricks.com>
1 parent 11f1c6b commit 227257d

6 files changed

Lines changed: 134 additions & 0 deletions

File tree

NEXT_CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
### New Features and Improvements
88

9+
* Added a `harness` user-agent dimension that reports the omnigent meta-harness (detected via the `OMNIGENT` environment variable) independently of agent detection.
10+
911
### Bug Fixes
1012

1113
### Documentation

config/api_client.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,17 @@ func HTTPClientConfigFromConfig(cfg *Config) (httpclient.ClientConfig, error) {
7474
*r = *r.WithContext(ctx) // replace request
7575
return nil
7676
},
77+
func(r *http.Request) error {
78+
// Detect if we are running inside a known agent meta-harness.
79+
provider := useragent.HarnessProvider()
80+
if provider == "" {
81+
return nil
82+
}
83+
// Add the detected harness to the user agent.
84+
ctx := useragent.InContext(r.Context(), useragent.HarnessKey, provider)
85+
*r = *r.WithContext(ctx) // replace request
86+
return nil
87+
},
7788
}
7889

7990
return httpclient.ClientConfig{

useragent/harness.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package useragent
2+
3+
import (
4+
"os"
5+
"sync"
6+
)
7+
8+
// knownHarness describes a meta-harness that orchestrates AI agents (not itself
9+
// an agent). Detected if envVar is set (any value, including empty, counts).
10+
type knownHarness struct {
11+
envVar string
12+
product string
13+
}
14+
15+
// listKnownHarnesses returns the canonical list of agent meta-harnesses.
16+
// Keep in sync with databricks-sdk-py and databricks-sdk-java.
17+
func listKnownHarnesses() []knownHarness {
18+
return []knownHarness{
19+
{envVar: "OMNIGENT", product: "omnigent"}, // https://github.com/omnigent-ai/omnigent
20+
}
21+
}
22+
23+
// lookupHarnessProvider returns the matched harness product, "multiple" if more
24+
// than one matched, or "" if none matched.
25+
func lookupHarnessProvider() string {
26+
var matches []string
27+
for _, h := range listKnownHarnesses() {
28+
if _, ok := os.LookupEnv(h.envVar); ok {
29+
matches = append(matches, h.product)
30+
}
31+
}
32+
switch len(matches) {
33+
case 1:
34+
return matches[0]
35+
case 0:
36+
return ""
37+
default:
38+
return "multiple"
39+
}
40+
}
41+
42+
var (
43+
harnessOnce sync.Once
44+
harnessName string
45+
)
46+
47+
// HarnessProvider returns the detected meta-harness name, cached for the process lifetime.
48+
func HarnessProvider() string {
49+
harnessOnce.Do(func() {
50+
harnessName = lookupHarnessProvider()
51+
})
52+
return harnessName
53+
}

useragent/harness_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package useragent
2+
3+
import (
4+
"testing"
5+
6+
"github.com/databricks/databricks-sdk-go/internal/env"
7+
)
8+
9+
func TestLookupHarnessProvider(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
envs map[string]string
13+
expect string
14+
}{
15+
{
16+
name: "no harness",
17+
envs: nil,
18+
expect: "",
19+
},
20+
{
21+
name: "omnigent",
22+
envs: map[string]string{"OMNIGENT": "1"},
23+
expect: "omnigent",
24+
},
25+
{
26+
name: "omnigent empty value still counts as set",
27+
envs: map[string]string{"OMNIGENT": ""},
28+
expect: "omnigent",
29+
},
30+
{
31+
name: "agent env var does not affect harness",
32+
envs: map[string]string{"CLAUDECODE": "1"},
33+
expect: "",
34+
},
35+
}
36+
37+
for _, tt := range tests {
38+
t.Run(tt.name, func(t *testing.T) {
39+
env.CleanupEnvironment(t)
40+
ClearCache()
41+
for k, v := range tt.envs {
42+
t.Setenv(k, v)
43+
}
44+
got := lookupHarnessProvider()
45+
if got != tt.expect {
46+
t.Errorf("lookupHarnessProvider() = %q, want %q", got, tt.expect)
47+
}
48+
})
49+
}
50+
}
51+
52+
func TestHarnessProviderCached(t *testing.T) {
53+
env.CleanupEnvironment(t)
54+
ClearCache()
55+
t.Setenv("OMNIGENT", "1")
56+
got := HarnessProvider()
57+
if got != "omnigent" {
58+
t.Errorf("HarnessProvider() = %q, want %q", got, "omnigent")
59+
}
60+
// Change env after caching. Cached result should persist.
61+
t.Setenv("OMNIGENT", "")
62+
got = HarnessProvider()
63+
if got != "omnigent" {
64+
t.Errorf("HarnessProvider() after cache = %q, want %q", got, "omnigent")
65+
}
66+
}

useragent/runtime.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ func ClearCache() {
1313
runtimeOnce = sync.Once{}
1414
providerOnce = sync.Once{}
1515
agentOnce = sync.Once{}
16+
harnessOnce = sync.Once{}
1617
}
1718

1819
var runtimeOnce sync.Once

useragent/user_agent.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const (
1616
CicdKey = "cicd"
1717
AuthKey = "auth"
1818
AgentKey = "agent"
19+
HarnessKey = "harness"
1920
)
2021

2122
// WithProduct sets the product name and product version globally.

0 commit comments

Comments
 (0)