fix: version-gate Claude Code/Codex agents to BrowserOS 0.46.0.0#1066
Conversation
The unified picker (#1065) surfaced the agent-harness adapters (Claude Code, Codex) on every BrowserOS version the auto-updating extension runs on, but the harness/ACP backend only ships in BrowserOS 0.46.0.0+. On older builds the agents appeared but could not run. Add Feature.AGENT_HARNESS_SUPPORT (minBrowserOSVersion 0.46.0.0) and gate the two data hooks that feed every surface: useAgentAdapters (AI & Agents settings tabs) and useHarnessAgents (picker ACP targets). When unsupported, both disable their query and return empty — so the home and sidepanel pickers, settings tabs, and recent-agents dock all hide the agents, with no requests to harness endpoints that may not exist.
|
Greptile (@greptileai) review |
|
Claude (@claude) review |
|
Claude finished Nikhil (@shadowfax92)'s task in 1m 20s —— View job Code Review
Overall this is a clean, well-scoped change. The "single choke-point" design — gating at the two data-source hooks rather than in individual UI surfaces — is the right approach, and the compile-time enforcement via the mapped type is a nice safety net. A few things worth looking at before merging: Potential bug:
|
| Input | Expected | ✓ |
|---|---|---|
[0, 45, 9, 9] |
false |
✓ |
[0, 45, 0, 0] |
false |
✓ |
null (unknown version) |
false |
✓ |
[0, 46, 0, 0] (exact boundary) |
true |
✓ |
[0, 47, 0, 0] (above) |
true |
✓ |
One missing edge case: dev mode. resolveStaticFeatureSupport returns true in dev mode (bypassing version checks), but the new test suite uses checkFeatureSupport directly, which skips the static-support path. There's no test asserting that in a dev environment Capabilities.supports(Feature.AGENT_HARNESS_SUPPORT) returns true regardless of version. This is covered implicitly by the existing resolveStaticFeatureSupport tests, so it's not a gap in the logic — just a potential future readability issue if someone looks at only the new checkFeatureSupport tests.
Nit: duplicate capability check pattern
Both hooks repeat the same two lines:
const { supports } = useCapabilities()
const agentsSupported = supports(Feature.AGENT_HARNESS_SUPPORT)This is fine for now (two hooks is not an abstraction threshold), but if a third harness hook is added, consider a shared useAgentHarnessSupported() hook to keep the pattern DRY. Not a blocker.
Summary: The gating logic is correct and the design is sound. The one concrete issue is the error field leaking through the gate on unsupported builds. Everything else is minor.
✅ Tests passed — 1058/1063
|
Greptile SummaryThis PR adds a
Confidence Score: 4/5Safe to merge; the gating logic is correct and no harness requests will fire on unsupported BrowserOS versions. The feature gate and TypeScript enforcement are solid. Both hooks return useAgents.ts — the loading computation in both useAgentAdapters and useHarnessAgents should incorporate capabilitiesLoading from useCapabilities. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Component mounts] --> B[useCapabilities]
B --> C{isLoading?}
C -- yes --> D[agentsSupported = false\nloading = false ⚠️\ndata = empty]
C -- no --> E{BrowserOS >= 0.46.0.0?}
E -- no --> F[agentsSupported = false\nloading = false\ndata = empty — correct]
E -- yes --> G[agentsSupported = true]
G --> H[react-query enabled\nfetch /adapters or /agents]
H --> I{Query done?}
I -- loading --> J[loading = true\ndata = empty]
I -- done --> K[loading = false\ndata = adapters / agents]
|
Greptile SummaryThis PR version-gates the Claude Code and Codex agent-harness adapters behind a new
Confidence Score: 4/5Safe to merge. The version gate is wired at the single data-source layer and the TS exhaustive-map enforces the config entry at compile time; older BrowserOS builds will correctly see no harness agents and make no harness requests. The gating logic and feature config are correct. The one gap is that The Important Files Changed
Sequence DiagramsequenceDiagram
participant UI as UI Surface (picker / settings)
participant HA as useAgentAdapters / useHarnessAgents
participant UC as useCapabilities
participant Cap as Capabilities (singleton)
participant HE as Harness Endpoint
UI->>HA: render
HA->>UC: supports(AGENT_HARNESS_SUPPORT)
UC->>Cap: getStaticSupport / async init
Cap-->>UC: browserOSVersion resolved
alt "BrowserOS < 0.46.0.0 or unknown"
UC-->>HA: false
HA-->>UI: "data=[], loading=false (query disabled)"
note over HE: No request made
else "BrowserOS >= 0.46.0.0"
UC-->>HA: true
HA->>HE: GET /adapters or GET /
HE-->>HA: adapters / agents
HA-->>UI: "data=[...], loading=false"
end
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
packages/browseros-agent/apps/agent/entrypoints/app/agents/useAgents.ts:45-71
**`loading` hides the capabilities-initializing window**
`useCapabilities().isLoading` is destructured away — only `supports` is taken. While capabilities are initializing (the async `init()` in `useCapabilities` hasn't settled yet), `supports(Feature.AGENT_HARNESS_SUPPORT)` returns `false` (the map's default), so `agentsSupported` is `false` and `loading` evaluates to `false && …` = `false`. On a supported BrowserOS (≥ 0.46.0.0) this produces a brief window where the hook returns `loading: false, data: []`, which callers may interpret as "no agents" and render an empty state, before capabilities resolve and the query finally fires. The same pattern applies to `useHarnessAgents`. Including `capabilitiesLoading` in the loading expression would close the gap: `loading: capabilitiesLoading || (agentsSupported && (query.isLoading || urlLoading))`
Reviews (2): Last reviewed commit: "fix: version-gate Claude Code/Codex agen..." | Re-trigger Greptile |
…ies init - loading now folds in useCapabilities().isLoading so consumers don't see a premature 'no agents' empty state during the capabilities-init window on supported BrowserOS (Greptile P2, both hooks). - error is now gated by agentsSupported, so urlError from useAgentServerUrl can't leak an error banner on older builds where harness endpoints are absent — older builds see nothing, per intent (Claude review). - Note why checkFeatureSupport is exported (tests only).
|
Thanks for the review — addressed in d87f3a1:
Also folded |
Summary
Feature.AGENT_HARNESS_SUPPORT(minBrowserOSVersion: 0.46.0.0) and gates the two data hooks that feed every surface:useAgentAdapters(AI & Agents settings tabs) anduseHarnessAgents(picker ACP targets).Design
The gate is applied at the single data-source choke point.
useAgentAdaptersanduseHarnessAgents(apps/agent/entrypoints/app/agents/useAgents.ts) are the only readers of the harness/adaptersand agents-list endpoints, and every UI surface reads through them. Each hook consultsuseCapabilities(); whenAGENT_HARNESS_SUPPORTis unsupported it sets react-queryenabled: falseand returns[](non-loading).The picker builds ACP entries from
agentsand the settings tabs fromadapters, so gating both hooks covers both axes. The pure helpers (adapter-visibility.ts,buildSidepanelChatTargets) stay untouched and pure. Dev mode and BrowserOS ≥ 0.46.0.0 are unchanged. ChatGPT / GitHub Copilot LLM providers are out of scope (already gated by their own server-version features).Test plan
bun run typecheck— all workspaces green (the TS mapped type{ [K in Feature]: FeatureConfig }enforces the new config entry at compile time).bun test apps/agent/lib/browseros/capabilities.test.ts— new gate-boundary test: hidden below0.46.0.0and when the version is unknown, shown at/above.bun test apps/agent/entrypoints/sidepanel/index/sidepanel-chat-targets.test.ts apps/agent/lib/chat/adapter-visibility.test.ts— pure-function tests unchanged (no regressions).bunx biome check— clean.