Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/deprecate-gemini-engine.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions docs/adr/34693-add-antigravity-engine-deprecate-gemini.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Native API-key authentication passed on a clean Actions runner in
including real fresh-challenge inference, missing/invalid keys, an unknown
model and controlled endpoint routing. That evidence does not prove AWF or MCP.

Release of Agy and Gemini soft deprecation together requires bounded production
Stable promotion of Agy requires bounded production
conformance through the actual installer, Gemini AWF endpoint, native MCP,
CLI-mounted MCP and staged safe outputs. The manual
`engine-conformance-agy.md` workflow prepares that gate and remains dispatch-only.
Expand All @@ -99,8 +99,10 @@ Both checkers require the exact staged noop receipt. The Gemini-key-only gate
disables the unrelated Copilot threat-detection job directly in each entry point;
its native/shared checks and execution budgets remain mandatory.
Production conformance
has not passed; no Gemini deprecation notice or release changeset is enabled by
this implementation checkpoint.
has not passed. Gemini soft deprecation is now enabled by explicit maintainer
direction, independently of Agy's stable-promotion gate. The informational notice
and release changeset do not claim production conformance or feature equivalence;
Agy remains experimental and Gemini remains supported.

## Consequences

Expand Down
5 changes: 3 additions & 2 deletions docs/src/content/docs/engines/agy.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,9 @@ authentication/model cases. `engine-conformance-agy.md` separately exercises the
production installer, AWF, native MCP, CLI-mounted MCP tools and staged safe
outputs. It remains dispatch-only; Credentials Check calls the feature-branch
`agy-conformance-reusable.lock.yml`, compiled from the same shared probes and
prompt. Release and Gemini deprecation are gated on that production path, not
on mocked tests or native authentication alone.
prompt. Stable promotion is gated on that production path, not on mocked tests
or native authentication alone. Gemini is deprecated in favor of experimental
Agy, but remains supported for workflows outside Agy's capabilities.

`smoke-agy.md` provides an on-demand smoke test using the same shared conformance
suite. Run it with `workflow_dispatch`, `/smoke-agy` in an issue or pull request
Expand Down
9 changes: 8 additions & 1 deletion docs/src/content/docs/engines/gemini.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
---
title: Using Gemini CLI with GitHub Agentic Workflows
description: Select and authenticate Google Gemini CLI as the AI engine for GitHub Agentic Workflows, understand its capabilities and limitations, and start from an example.
description: Maintain workflows using the deprecated Google Gemini CLI engine and evaluate migration to the experimental Agy engine.
---

[Google Gemini CLI](https://geminicli.com/) is a coding agent from Google. GitHub Agentic Workflows runs Gemini CLI in GitHub Actions, adding GitHub event triggers, sandbox controls, and safe outputs for constrained, reviewable automation.

> [!WARNING]
> The `gemini` engine is deprecated in favor of the experimental [`agy` engine](/gh-aw/engines/agy/).
> Existing Gemini workflows remain supported, with no automatic migration or removal date.
> For API-key workflows, review [Agy's migration guidance](/gh-aw/engines/agy/#evaluating-a-gemini-api-key-workflow)
> before changing engines. Keep `engine: gemini` for Google WIF or features Agy does not support,
> including native bash restrictions.
## Selecting Gemini CLI as the AI engine

To select Gemini CLI as the AI engine, with inference hosted and billed through a Google subscription, add this to the workflow frontmatter:
Expand Down
8 changes: 5 additions & 3 deletions docs/src/content/docs/reference/engines.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ Set `engine:` in workflow frontmatter and configure the corresponding authentica
| [GitHub Copilot CLI](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/use-copilot-cli) (default) | `copilot` | [`copilot-requests: write`](/gh-aw/reference/auth/#copilot-requests-write-permission) or [`COPILOT_GITHUB_TOKEN`](/gh-aw/reference/auth/#copilot_github_token) | [Using GitHub Copilot with GitHub Agentic Workflows](/gh-aw/engines/copilot/) |
| [Claude Code](https://www.anthropic.com/index/claude) | `claude` | [`ANTHROPIC_API_KEY`](/gh-aw/reference/auth/#anthropic_api_key) or [Anthropic WIF](/gh-aw/reference/auth/#anthropic-workload-identity-federation-wif) | [Using Claude Code with GitHub Agentic Workflows](/gh-aw/engines/claude/) |
| [OpenAI Codex](https://openai.com/blog/openai-codex) | `codex` | `CODEX_API_KEY` or [`OPENAI_API_KEY`](/gh-aw/reference/auth/#openai_api_key) | [Using OpenAI Codex with GitHub Agentic Workflows](/gh-aw/engines/codex/) |
| [Google Gemini CLI](https://github.com/google-gemini/gemini-cli) | `gemini` | [`GEMINI_API_KEY`](/gh-aw/reference/auth/#gemini_api_key) or [Google WIF](/gh-aw/reference/auth/#google-workload-identity-federation-wif) | [Using Google Gemini with GitHub Agentic Workflows](/gh-aw/engines/gemini/) |
| [Google Gemini CLI](https://github.com/google-gemini/gemini-cli) (deprecated in favor of `agy`) | `gemini` | [`GEMINI_API_KEY`](/gh-aw/reference/auth/#gemini_api_key) or [Google WIF](/gh-aw/reference/auth/#google-workload-identity-federation-wif) | [Using Google Gemini with GitHub Agentic Workflows](/gh-aw/engines/gemini/) |
| [Google Antigravity CLI](https://github.com/google-antigravity/antigravity-cli) (experimental) | `agy` | [`GEMINI_API_KEY`](/gh-aw/reference/auth/#gemini_api_key) only; no ADC/WIF | [Using experimental Agy](/gh-aw/engines/agy/) |
| [Pi](https://www.npmjs.com/package/@earendil-works/pi-coding-agent) | `pi` | Copilot authentication by default; Anthropic or OpenAI/Codex key for a provider-prefixed `model:` | [Using Pi with GitHub Agentic Workflows](/gh-aw/engines/pi/) |

Copilot CLI is the default, so `engine:` can be omitted when using Copilot. Copilot SDK mode is an execution mode of the Copilot engine, not a separate engine; enable it with `engine: copilot` and `copilot-sdk: true`. See [Copilot SDK support](#copilot-sdk-support).

Gemini remains selectable and emits an informational deprecation notice after effective engine resolution, including imports and CLI overrides. The notice does not count as a warning or affect strict-mode success. Evaluate experimental Agy for compatible API-key workflows; retain Gemini for Google WIF or unsupported capabilities. See [migration guidance](/gh-aw/engines/agy/#evaluating-a-gemini-api-key-workflow).

Copilot also supports experimental per-run model and reasoning-effort selection through `engine.model-routing`. See [Model Routing](/gh-aw/reference/model-routing/) for configuration, requirements, known limitations, and troubleshooting.

## Configuration conformance workflows
Expand Down Expand Up @@ -81,8 +83,8 @@ same probes and prompt from `shared/agy-conformance.md`; trigger-bearing
workflows cannot be imported as shared Markdown. Both Agy selectors
have isolated, ref-specific concurrency. The production gate has a ten-minute
job timeout, five-credit budget, and staged safe outputs. Native authentication
has passed; production conformance and Gemini soft deprecation remain pending.
The new engine and deprecation must not be released independently of that gate.
has passed; production conformance remains pending. Gemini is soft-deprecated,
but this does not promote Agy to stable or establish feature equivalence.

## Unsupported engine samples

Expand Down
2 changes: 1 addition & 1 deletion pkg/constants/constants_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ func TestGetEngineOption_AllBuiltInEngines(t *testing.T) {
{string(CopilotEngine), "GitHub Copilot", CopilotGitHubToken, nil},
{string(ClaudeEngine), "Claude", AnthropicAPIKey, []string{}},
{string(CodexEngine), "Codex", OpenAIAPIKey, []string{CodexAPIKey}},
{string(GeminiEngine), "Gemini", GeminiAPIKey, nil},
{string(GeminiEngine), "Gemini (Deprecated)", GeminiAPIKey, nil},
{string(PiEngine), "Pi", CopilotGitHubToken, []string{AnthropicAPIKey, OpenAIAPIKey, CodexAPIKey, GeminiAPIKey}},
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/constants/engine_constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ var EngineOptions = []EngineOption{
},
{
Value: string(GeminiEngine),
Label: "Gemini",
Description: "Google Gemini CLI coding agent",
Label: "Gemini (Deprecated)",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Updated the expected option label to Gemini (Deprecated) and covered it with the existing constants test. Fixed in e0912e8.

Description: "Deprecated Google Gemini CLI; use the experimental agy engine where supported",
SecretName: GeminiAPIKey,
KeyURL: "https://aistudio.google.com/app/apikey",
WhenNeeded: "Gemini engine workflows",
Expand Down
12 changes: 12 additions & 0 deletions pkg/workflow/compiler_orchestrator_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func (c *Compiler) setupEngineAndImports(result *parser.FrontmatterResult, clean
if err := c.runPostEngineValidations(result.Frontmatter, engineSetting, engineConfig, networkPermissions, sandboxConfig, agenticEngine, importsResult); err != nil {
return nil, err
}
c.emitGeminiDeprecationNotice(agenticEngine)
return &engineSetupResult{
engineSetting: engineSetting,
model: model,
Expand All @@ -97,6 +98,17 @@ func (c *Compiler) setupEngineAndImports(result *parser.FrontmatterResult, clean
}, nil
}

func (c *Compiler) emitGeminiDeprecationNotice(engine CodingAgentEngine) {
if engine.GetID() != string(constants.GeminiEngine) {
return
}
if c.batchMode {
c.featureUsage["gemini deprecation"]++
} else if !c.quiet {
fmt.Fprintln(os.Stderr, console.FormatInfoMessageStderr("The gemini engine is deprecated in favor of the experimental agy engine. Use engine: agy where supported; retain engine: gemini for Google WIF or features Agy does not support. See https://github.github.com/gh-aw/engines/agy/"))
}
}

// Import defaults can still change the engine config after this typo check.
// Imported definitions are registered later and must skip early resolution.
func (c *Compiler) validateEarlyEngineSetting(frontmatter map[string]any, engineSetting string, engineConfig *EngineConfig) error {
Expand Down
79 changes: 79 additions & 0 deletions pkg/workflow/compiler_orchestrator_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package workflow
import (
"os"
"path/filepath"
"strings"
"testing"

"github.com/github/gh-aw/pkg/parser"
Expand All @@ -14,6 +15,84 @@ import (
"github.com/stretchr/testify/require"
)

func TestSetupEngineAndImports_GeminiDeprecation(t *testing.T) {
tests := []struct {
name string
engine string
imported bool
override string
batchMode bool
quiet bool
wantEngine string
wantNotices int
wantBatchUse int
wantWarnings int
}{
{name: "string form", engine: "engine: gemini\n", wantEngine: "gemini", wantNotices: 1},
{name: "object form with pin", engine: "engine:\n id: gemini\n version: \"0.39.1\"\n", wantEngine: "gemini", wantNotices: 1},
{name: "imported engine", imported: true, wantEngine: "gemini", wantNotices: 1},
{name: "override to Gemini", engine: "engine: copilot\n", override: "gemini", wantEngine: "gemini", wantNotices: 1, wantWarnings: 1},
{name: "override away from Gemini", engine: "engine: gemini\n", override: "copilot", wantEngine: "copilot", wantWarnings: 1},
{name: "batch mode aggregates notice", engine: "engine: gemini\n", batchMode: true, wantEngine: "gemini", wantBatchUse: 1},
{name: "quiet mode suppresses notice", engine: "engine: gemini\n", quiet: true, wantEngine: "gemini"},
{name: "default engine", wantEngine: "copilot"},
{name: "experimental replacement", engine: "engine: agy\n", wantEngine: "agy"},
{
name: "Google WIF",
engine: `engine:
id: gemini
auth:
type: github-oidc
provider: gcp
workload-identity-provider: projects/123/locations/global/workloadIdentityPools/test/providers/test
service-account: test@example.iam.gserviceaccount.com
project: test-project
`,
wantEngine: "gemini", wantNotices: 1,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir := testutil.TempDir(t, "gemini-deprecation")
engine := tt.engine
if tt.imported {
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "shared.md"), []byte("---\nengine: gemini\n---\n"), 0644))
engine = "imports:\n - shared.md\n"
}
content := "---\non: push\n" + engine + "---\n\n# Test Workflow\n"
frontmatter, err := parser.ExtractFrontmatterFromContent(content)
require.NoError(t, err)
compiler := NewCompiler(WithEngineOverride(tt.override))
compiler.SetStrictMode(true)
compiler.SetBatchMode(tt.batchMode)
compiler.SetQuiet(tt.quiet)
output := testutil.CaptureStderr(t, func() {
result, err := compiler.setupEngineAndImports(frontmatter, filepath.Join(tmpDir, "test.md"), []byte(content), tmpDir)
require.NoError(t, err)
require.NotNil(t, result)
assert.Equal(t, tt.wantEngine, result.agenticEngine.GetID())
assert.Equal(t, tt.wantEngine, result.engineConfig.ID)
if tt.name == "object form with pin" {
assert.Equal(t, "0.39.1", result.engineConfig.Version)
}
if tt.name == "Google WIF" {
require.NotNil(t, result.engineConfig.Auth)
assert.Equal(t, "github-oidc", result.engineConfig.Auth.Type)
assert.Equal(t, "gcp", result.engineConfig.Auth.Provider)
}
})
assert.Equal(t, tt.wantNotices, strings.Count(output, "The gemini engine is deprecated"))
assert.Equal(t, tt.wantBatchUse, compiler.GetExperimentalFeatureUsage()["gemini deprecation"])
assert.Equal(t, tt.wantWarnings, compiler.GetWarningCount())
if tt.wantNotices > 0 {
assert.Contains(t, output, "Use engine: agy where supported")
assert.Contains(t, output, "retain engine: gemini for Google WIF")
}
})
}
}

func TestFrontmatterDeclaresImports(t *testing.T) {
tests := []struct {
name string
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/data/engines/gemini.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
engine:
id: gemini
display-name: Google Gemini CLI
description: Google Gemini CLI with headless mode and LLM gateway support
description: Deprecated Google Gemini CLI; use the experimental agy engine where supported
runtime-id: gemini
provider:
name: google
Expand Down
13 changes: 13 additions & 0 deletions pkg/workflow/engine_catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ func TestEngineCatalog_All(t *testing.T) {
}
}

func TestEngineCatalog_GeminiDeprecated(t *testing.T) {
catalog := NewEngineCatalog(NewEngineRegistry())
for _, def := range catalog.All() {
if def.ID == "gemini" {
assert.Contains(t, def.Description, "Deprecated")
assert.Contains(t, def.Description, "agy")
assert.Equal(t, NewGeminiEngine().GetDescription(), def.Description)
return
}
}
t.Fatal("deprecated Gemini engine must remain selectable")
}

// engineSchemaOneOfVariants parses the main workflow schema and returns the
// type identifiers of each variant in engine_config.oneOf for structural assertions.
func engineSchemaOneOfVariants(t *testing.T) []map[string]any {
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/gemini_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func NewGeminiEngine() *GeminiEngine {
BaseEngine: BaseEngine{
id: "gemini",
displayName: "Google Gemini CLI",
description: "Google Gemini CLI with headless mode and LLM gateway support",
description: "Deprecated Google Gemini CLI; use the experimental agy engine where supported",
experimental: false,
ghSkillAgentName: "gemini-cli",
capabilities: EngineCapabilities{
Expand Down
Loading