Skip to content

Commit abd8312

Browse files
authored
Add meta-harness user-agent dimension for omnigent (#836)
## Why [Omnigent](https://github.com/omnigent-ai/omnigent) is a meta-harness that orchestrates AI coding agents (Claude Code, Codex, Cursor, and others). It is not itself an agent, so it does not belong in the existing `agent/<name>` user-agent dimension. We want telemetry that surfaces both the meta-harness and the underlying agent at the same time. ("meta-harness" matches omnigent's own self-description; "harness" alone is how the field labels the underlying agents like Claude Code.) ## Changes Before: the SDK reported only `agent/<name>` (e.g. `agent/claude-code`) for the detected AI coding agent. Now: the SDK also reports an independent `meta-harness/<name>` dimension. When the `OMNIGENT` environment variable is present (omnigent stamps `OMNIGENT=1` into every agent process, see omnigent-ai/omnigent#656), the user agent gains a `meta-harness/omnigent` segment. Because it is a separate dimension from agent detection, running Claude Code under omnigent yields both `agent/claude-code` and `meta-harness/omnigent`, and omnigent never trips the agent "multiple" logic. Implementation mirrors the existing agent dimension, all in `core/UserAgent.java`: - New `metaHarnessProvider` cache field, a `KnownMetaHarness` model + `listKnownMetaHarnesses()` table, a presence-based `lookupMetaHarnessProvider()`, and a cached `metaHarnessProvider()` (double-checked locking, mirroring `agentProvider()`). - `asString()` appends the `meta-harness/<name>` segment right after the agent segment. ## Test plan - [x] `mvn -pl databricks-sdk-java test -Dtest=UserAgentTest` (5 new tests) - [x] Detection tests: present / absent / empty-value-still-counts / cached - [x] Independence test: with both `OMNIGENT` and `CLAUDECODE` set, the UA contains both `agent/claude-code` and `meta-harness/omnigent`, and does not contain `agent/multiple` - [x] `spotless:apply` clean (no reformatting) --------- Signed-off-by: simon <simon.faltum@databricks.com>
1 parent d5c7799 commit abd8312

3 files changed

Lines changed: 115 additions & 0 deletions

File tree

NEXT_CHANGELOG.md

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

55
### New Features and Improvements
66

7+
- Added a `meta-harness` user-agent dimension that reports the omnigent meta-harness (detected via the `OMNIGENT` environment variable) independently of agent detection.
8+
79
### Breaking Changes
810

911
### Bug Fixes

databricks-sdk-java/src/main/java/com/databricks/sdk/core/UserAgent.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ public static String asString() {
133133
if (!agent.isEmpty()) {
134134
segments.add(String.format("agent/%s", agent));
135135
}
136+
String metaHarness = metaHarnessProvider();
137+
if (!metaHarness.isEmpty()) {
138+
segments.add(String.format("meta-harness/%s", metaHarness));
139+
}
136140
// Concurrent iteration over ArrayList must be guarded with synchronized.
137141
synchronized (otherInfo) {
138142
segments.addAll(
@@ -174,6 +178,8 @@ private static List<CicdProvider> listCiCdProviders() {
174178

175179
protected static volatile String agentProvider = null;
176180

181+
protected static volatile String metaHarnessProvider = null;
182+
177183
protected static Environment env = null;
178184

179185
// Represents an environment variable with its name and expected value
@@ -362,6 +368,47 @@ private static String agentProvider() {
362368
return agentProvider;
363369
}
364370

371+
// Describes a single meta-harness: the env var that identifies it and the
372+
// product name reported in the user agent.
373+
private static class KnownMetaHarness {
374+
private final String envVar;
375+
private final String product;
376+
377+
KnownMetaHarness(String envVar, String product) {
378+
this.envVar = envVar;
379+
this.product = product;
380+
}
381+
}
382+
383+
// Canonical list of known meta-harnesses, detected by presence.
384+
// Keep in sync with databricks-sdk-go and databricks-sdk-py.
385+
// OMNIGENT is set by the omnigent meta-harness (https://github.com/omnigent-ai/omnigent).
386+
private static List<KnownMetaHarness> listKnownMetaHarnesses() {
387+
return Arrays.asList(new KnownMetaHarness("OMNIGENT", "omnigent"));
388+
}
389+
390+
// Looks up the active meta-harness by env var presence, independent of the agent.
391+
private static String lookupMetaHarnessProvider(Environment env) {
392+
for (KnownMetaHarness h : listKnownMetaHarnesses()) {
393+
if (env.get(h.envVar) != null) {
394+
return h.product;
395+
}
396+
}
397+
return "";
398+
}
399+
400+
// Thread-safe lazy initialization of meta-harness detection
401+
private static String metaHarnessProvider() {
402+
if (metaHarnessProvider == null) {
403+
synchronized (UserAgent.class) {
404+
if (metaHarnessProvider == null) {
405+
metaHarnessProvider = lookupMetaHarnessProvider(env());
406+
}
407+
}
408+
}
409+
return metaHarnessProvider;
410+
}
411+
365412
private static Environment env() {
366413
if (env == null) {
367414
env =

databricks-sdk-java/src/test/java/com/databricks/sdk/core/UserAgentTest.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ void tearDown() {
1818
private void setupAgentEnv(Map<String, String> envMap) {
1919
UserAgent.agentProvider = null;
2020
UserAgent.cicdProvider = null;
21+
UserAgent.metaHarnessProvider = null;
2122
UserAgent.env = new Environment(envMap, new ArrayList<>(), System.getProperty("os.name"));
2223
}
2324

2425
private void cleanupAgentEnv() {
2526
UserAgent.env = null;
2627
UserAgent.agentProvider = null;
2728
UserAgent.cicdProvider = null;
29+
UserAgent.metaHarnessProvider = null;
2830
}
2931

3032
@Test
@@ -617,4 +619,68 @@ public void testAgentProviderCached() {
617619
Assertions.assertTrue(UserAgent.asString().contains("agent/cursor"));
618620
Assertions.assertFalse(UserAgent.asString().contains("agent/claude-code"));
619621
}
622+
623+
@Test
624+
public void testMetaHarnessProviderOmnigent() {
625+
setupAgentEnv(
626+
new HashMap<String, String>() {
627+
{
628+
put("OMNIGENT", "1");
629+
}
630+
});
631+
Assertions.assertTrue(UserAgent.asString().contains("meta-harness/omnigent"));
632+
}
633+
634+
@Test
635+
public void testMetaHarnessProviderNoHarness() {
636+
setupAgentEnv(new HashMap<>());
637+
Assertions.assertFalse(UserAgent.asString().contains("meta-harness/"));
638+
}
639+
640+
@Test
641+
public void testMetaHarnessProviderEmptyValueStillSet() {
642+
// Empty string still counts as "set" for presence-only detection,
643+
// matching the agent dimension and databricks-sdk-go semantics.
644+
setupAgentEnv(
645+
new HashMap<String, String>() {
646+
{
647+
put("OMNIGENT", "");
648+
}
649+
});
650+
Assertions.assertTrue(UserAgent.asString().contains("meta-harness/omnigent"));
651+
}
652+
653+
@Test
654+
public void testMetaHarnessProviderIndependentOfAgent() {
655+
// Under omnigent both OMNIGENT and the agent marker are set: report both
656+
// dimensions, and the meta-harness must not trip the agent "multiple" logic.
657+
setupAgentEnv(
658+
new HashMap<String, String>() {
659+
{
660+
put("OMNIGENT", "1");
661+
put("CLAUDECODE", "1");
662+
}
663+
});
664+
String userAgent = UserAgent.asString();
665+
Assertions.assertTrue(userAgent.contains("agent/claude-code"));
666+
Assertions.assertTrue(userAgent.contains("meta-harness/omnigent"));
667+
Assertions.assertFalse(userAgent.contains("agent/multiple"));
668+
}
669+
670+
@Test
671+
public void testMetaHarnessProviderCached() {
672+
// Set up with omnigent.
673+
setupAgentEnv(
674+
new HashMap<String, String>() {
675+
{
676+
put("OMNIGENT", "1");
677+
}
678+
});
679+
Assertions.assertTrue(UserAgent.asString().contains("meta-harness/omnigent"));
680+
681+
// Change env after caching. Cached result should persist.
682+
UserAgent.env =
683+
new Environment(new HashMap<>(), new ArrayList<>(), System.getProperty("os.name"));
684+
Assertions.assertTrue(UserAgent.asString().contains("meta-harness/omnigent"));
685+
}
620686
}

0 commit comments

Comments
 (0)