Update Git Operations AI client billing policy (#9840)

## Description

Adds the client-side follow-up for the Git Operations AI enterprise/tier
policy:

- Adds `isGitOperationsAiEnabled` to the client GraphQL schema and
`WarpAiPolicy` fragments/models.
- Converts the new policy into the app-level workspace billing model.
- Gates Git Operations AI requests and the “Commit & Pull Request
Generation” setting on the billing policy instead of the previous
hardcoded enterprise client gate.
- Removes the stale TODO that asked for this client-side gate.

This is intended to pair with the server-side schema/policy change in
warpdotdev/warp-server#10798. Keeping this PR as draft until the server
field is available to clients.

## Linked Issue

N/A — client follow-up for the server GraphQL schema/policy PR.

## Screenshots / Videos

N/A — this is policy/schema wiring. The existing setting is hidden when
the new billing policy disables Git Operations AI.

## Testing

- `cargo fmt --manifest-path
/Users/edward/Repos/edward-update-git-ops-ai-schema/Cargo.toml --all --
--check`
- `cargo clippy --manifest-path
/Users/edward/Repos/edward-update-git-ops-ai-schema/Cargo.toml -p
warp_graphql_schema -p warp_graphql -p warp --all-targets --all-features
--tests -- -D warnings`
- `git --no-pager diff --check`

No new integration test added; this is a small billing-policy plumbing
change that reuses existing settings/request gates.

## Agent Mode

- [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode

Agent conversation:
https://staging.warp.dev/conversation/281d784d-6c87-414f-89db-1b29f771f571

Co-Authored-By: Oz <oz-agent@warp.dev>

---------

Co-authored-by: Oz <oz-agent@warp.dev>
Co-authored-by: MaggieShan <sshan.maggie@gmail.com>
This commit is contained in:
Edward Shao
2026-05-05 19:25:30 -04:00
committed by GitHub
parent 4e600af4ab
commit ba40a024df
10 changed files with 27 additions and 12 deletions

View File

@@ -1,8 +1 @@
// TODO(edward): follow-up — gate callers of this module on both
// 1. an `AISettings` opt-out (mirror `is_shared_block_title_generation_enabled`), and
// 2. a customer-type guard (exclude Enterprise unless Warp plan / dogfood),
// matching the pattern in `terminal/share_block_modal.rs::should_send_title_gen_request`.
// `FeatureFlag::GitOperationsInCodeReview` already gates the surrounding UI,
// but does not address AI-specific privacy / opt-out concerns for sending
// diffs to an LLM.
pub(crate) mod api;

View File

@@ -134,8 +134,7 @@ fn show_toast(msg: impl Into<String>, ctx: &mut ViewContext<GitDialog>) {
///
/// Folds the parent feature flag, the user's dedicated per-feature AI toggle
/// (which itself requires active AI / auth / remote-session org policy to
/// allow AI), and an enterprise check with the same Warp-plan exception and
/// dogfood override as `share_block_modal.rs::should_send_title_gen_request`.
/// allow AI), and the current team's Git Operations AI tier policy.
///
/// When this returns `false`, call sites skip AI entirely: commit.rs opens
/// with the manual-type placeholder and pr.rs goes straight to
@@ -143,7 +142,7 @@ fn show_toast(msg: impl Into<String>, ctx: &mut ViewContext<GitDialog>) {
fn should_send_git_ops_ai_request(app: &AppContext) -> bool {
FeatureFlag::GitOperationsInCodeReview.is_enabled()
&& AISettings::as_ref(app).is_git_operations_autogen_enabled(app)
&& UserWorkspaces::as_ref(app).ai_allowed_for_current_team()
&& UserWorkspaces::as_ref(app).is_git_operations_ai_enabled()
}
/// Maps a raw git error string to a user-friendly toast message. Known

View File

@@ -2454,6 +2454,9 @@ impl TypedActionView for AISettingsPageView {
ctx.notify();
}
AISettingsPageAction::ToggleGitOperationsAutogen => {
if !UserWorkspaces::as_ref(ctx).is_git_operations_ai_enabled() {
return;
}
match AISettings::handle(ctx).update(ctx, |settings, ctx| {
settings
.git_operations_autogen_enabled_internal
@@ -3726,7 +3729,7 @@ impl ActiveAIWidget {
&& AISettings::as_ref(app)
.git_operations_autogen_enabled_internal
.is_supported_on_current_platform()
&& UserWorkspaces::as_ref(app).ai_allowed_for_current_team()
&& UserWorkspaces::as_ref(app).is_git_operations_ai_enabled()
}
fn render_next_command_section(

View File

@@ -174,6 +174,7 @@ impl From<GqlWarpAiPolicy> for WarpAiPolicy {
is_code_suggestions_toggleable: gql_warp_ai_policy.is_code_suggestions_toggleable,
is_prompt_suggestions_toggleable: gql_warp_ai_policy.is_prompt_suggestions_toggleable,
is_next_command_enabled: gql_warp_ai_policy.is_next_command_enabled,
is_git_operations_ai_enabled: gql_warp_ai_policy.is_git_operations_ai_enabled,
is_voice_enabled: gql_warp_ai_policy.is_voice_enabled,
}
}

View File

@@ -387,7 +387,7 @@ impl UserWorkspaces {
/// Returns `true` if active AI is allowed for the current workspace, based on billing config.
///
/// In the future, we should store active AI enablement on the policy directly. For now, we
/// proxy whether active AI by checking if prompt suggestions, next command, or code suggestions are enabled.
/// proxy whether active AI by checking whether any active AI feature is enabled.
pub fn is_active_ai_allowed(&self) -> bool {
self.current_team().is_none_or(|team| {
team.billing_metadata
@@ -397,6 +397,7 @@ impl UserWorkspaces {
policy.is_prompt_suggestions_toggleable
|| policy.is_next_command_enabled
|| policy.is_code_suggestions_toggleable
|| policy.is_git_operations_ai_enabled
})
})
}
@@ -454,6 +455,19 @@ impl UserWorkspaces {
})
}
/// Whether Git Operations AI is enabled for the current user, based on the active policies.
/// Note that the value may be incorrect if called before the team's billing metadata has been fetched.
pub fn is_git_operations_ai_enabled(&self) -> bool {
self.current_team()
// If the user has no team, they can toggle Git Operations AI (no restrictions).
.is_none_or(|team| {
team.billing_metadata
.tier
.warp_ai_policy
.is_some_and(|policy| policy.is_git_operations_ai_enabled)
})
}
/// Whether voice input should be toggleable for the current user, based on the active policies.
/// Note that the value may be incorrect if called before the team's billing metadata has been fetched.
/// If voice input support is not compiled into this build, always returns `false`.

View File

@@ -292,6 +292,7 @@ pub struct WarpAiPolicy {
pub is_code_suggestions_toggleable: bool,
pub is_prompt_suggestions_toggleable: bool,
pub is_next_command_enabled: bool,
pub is_git_operations_ai_enabled: bool,
pub is_voice_enabled: bool,
}
#[derive(Clone, Debug, Copy, Serialize, Deserialize)]

View File

@@ -147,6 +147,7 @@ pub struct WarpAiPolicy {
pub is_code_suggestions_toggleable: bool,
pub is_prompt_suggestions_toggleable: bool,
pub is_next_command_enabled: bool,
pub is_git_operations_ai_enabled: bool,
pub is_voice_enabled: bool,
}

View File

@@ -35,6 +35,7 @@ mutation CreateTeam($input: CreateTeamInput!, $request_context: RequestContext!)
isCodeSuggestionsToggleable
isPromptSuggestionsToggleable
isNextCommandEnabled
isGitOperationsAiEnabled
isVoiceEnabled
}
teamSizePolicy {

View File

@@ -36,6 +36,7 @@ query GetWorkspacesMetadataForUser($requestContext: RequestContext!) {
isCodeSuggestionsToggleable
isPromptSuggestionsToggleable
isNextCommandEnabled
isGitOperationsAiEnabled
isVoiceEnabled
}
teamSizePolicy {

View File

@@ -3967,6 +3967,7 @@ type WarpAiPolicy {
acceptedAutosuggestionsLimit: Int!
disablePremiumModels: Boolean!
isCodeSuggestionsToggleable: Boolean!
isGitOperationsAiEnabled: Boolean!
isNextCommandEnabled: Boolean!
isPromptSuggestionsToggleable: Boolean!
isUnlimited: Boolean!