Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
508777f
fix(antigravity): show why Remove runtime is unavailable
sambitcreate Oct 7, 2026
b9544a0
fix(antigravity): keep focus in the runtime section as actions change
sambitcreate Oct 7, 2026
a4e524f
fix(antigravity): give agent-run activity rows tensed verbs on every …
sambitcreate Oct 7, 2026
66fed1a
fix(antigravity): name agent approvals like Aiden's own approval cards
sambitcreate Oct 7, 2026
4e2b6da
fix(antigravity): show a retry when the runtime status cannot be read
sambitcreate Oct 7, 2026
b0ecbe2
fix(antigravity): take setup copy's publisher and host from the harness
sambitcreate Oct 7, 2026
a4dcbc4
fix(antigravity): hide the install bar when there is no percentage
sambitcreate Oct 7, 2026
d99fade
fix(antigravity): match the sign-in hint to the runtime's state
sambitcreate Oct 7, 2026
b6df1a0
fix(antigravity): announce install phases in plain words
sambitcreate Oct 7, 2026
f532c42
fix(antigravity): state the runtime download size once
sambitcreate Oct 7, 2026
71c1542
fix(antigravity): style Install as the dialog's primary action
sambitcreate Oct 7, 2026
fb5ecc1
feat(antigravity): give Google Antigravity its own provider mark
sambitcreate Oct 7, 2026
1fa6153
fix(antigravity): describe agent models in the picker's usual form
sambitcreate Oct 7, 2026
ce9e662
fix(antigravity): explain why an agent-backed default can't run a sch…
sambitcreate Oct 7, 2026
fac95b9
fix(antigravity): keep agent models out of Assistant and Bot chat pic…
sambitcreate Oct 7, 2026
b6b8554
fix(mobile): explain which model replies to a Mac-only agent chat
sambitcreate Oct 7, 2026
5a99b01
docs(antigravity): describe Assistant/Bot gating and phone behaviour
sambitcreate Oct 7, 2026
751122f
Merge remote-tracking branch 'origin/main' into feature/pr-371-ui-ux-…
sambitcreate Oct 8, 2026
caff4eb
fix(antigravity): recover runtime focus before the dialog's focus scope
sambitcreate Oct 8, 2026
69f181c
test(schedules): render the provider warning instead of grepping its …
sambitcreate Oct 8, 2026
8a69115
fix(antigravity): explain a signed-out agent model before the API-key…
sambitcreate Oct 8, 2026
4044f40
test(e2e): hold the thinking selector open while clicking a level
sambitcreate Oct 8, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ the same identity map as desktop and iOS.
- Official provider sites: Ant Ling, Fireworks, Groq, Together, Z.AI, OpenCode,
Kimi/Moonshot, and their regional or product aliases.
- Wikimedia Commons: xAI and Grok.
- Google's official one-colour Antigravity icon: Google Antigravity, with the
viewBox fitted to the glyph.
- Lobe Icons static SVG package: Cerebras, replacing an incorrect OpenAI asset
in the supplied folder.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,23 @@ fun AidenChatDetailScreen(
)
}

val macOnlyAgentNotice = chat?.let { currentChat ->
val replyModel = modelCatalog?.providers
?.firstOrNull { it.id == selectedProviderId }
?.models?.firstOrNull { it.id == selectedModelId }
AidenChatModelAuthority.macOnlyAgentNotice(currentChat, modelCatalog, replyModel?.label)
}
if (macOnlyAgentNotice != null) {
Text(
text = macOnlyAgentNotice,
style = MaterialTheme.typography.bodySmall,
color = palette.secondary,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp, vertical = 4.dp)
)
}

// 1:1 Parity iOS Glass Composer
AidenComposerView(
draft = draft,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ import sbtbiswas.AidenOnTheGo.models.AidenProviderArtwork

object AidenProviderIconResolver {
val supportedSlugs = setOf(
"amazon-bedrock", "ant-ling", "anthropic", "apple-foundation-models",
"amazon-bedrock", "ant-ling", "anthropic", "antigravity",
"apple-foundation-models",
"azure-openai-responses", "cerebras", "claude", "cloudflare-ai-gateway",
"cloudflare-workers-ai", "concentrate", "deepseek", "fireworks",
"github-copilot", "google", "google-vertex", "grok", "groq",
Expand All @@ -51,6 +52,7 @@ object AidenProviderIconResolver {
"amazon-bedrock" to R.drawable.ic_provider_amazon_bedrock,
"ant-ling" to R.drawable.ic_provider_ant_ling,
"anthropic" to R.drawable.ic_provider_anthropic,
"antigravity" to R.drawable.ic_provider_antigravity,
"apple-foundation-models" to R.drawable.ic_provider_apple_foundation_models,
"azure-openai-responses" to R.drawable.ic_provider_azure_openai_responses,
"cerebras" to R.drawable.ic_provider_cerebras,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,14 @@ object AidenAgentActivityPresentation {
"browser_recording_start" to Pair("Starting browser recording", "Started browser recording"),
"browser_recording_stop" to Pair("Stopping browser recording", "Stopped browser recording"),
"vcc_recall" to Pair("Recalling chat history", "Recalled chat history"),
"compact_context" to Pair("Compacting context", "Compacted context")
"compact_context" to Pair("Compacting context", "Compacted context"),
// Activity an ACP agent (for example, Google Antigravity) ran itself.
"delete_file" to Pair("Deleting", "Deleted"),
"move_file" to Pair("Moving", "Moved"),
"web_fetch" to Pair("Fetching web page", "Fetched web page"),
"agent_subagents" to Pair("Running subagents", "Ran subagents"),
"agent_tool" to Pair("Using an agent tool", "Used an agent tool"),
"agent_context_rebuilt" to Pair("Starting a fresh agent session", "Started a fresh agent session")
)

/** Exactly one compaction: its line carries every metric. Repeated compactions
Expand Down Expand Up @@ -1733,6 +1740,25 @@ data class AidenChatModelSelection(
)

object AidenChatModelAuthority {
/** Agent-backed providers (mirroring the Mac's ACP harness list) run only in
* chats open on the Mac, so the host leaves them out of the phone's catalog. */
val macOnlyAgentProviders: Map<String, String> = mapOf("antigravity" to "Google Antigravity")

/** For a chat the Mac last answered with a Mac-only agent model, which model
* replies from this phone use instead. Null when the chat's own model is
* available here, or for Bot chats, whose model is fixed. */
fun macOnlyAgentNotice(chat: AidenChat, catalog: AidenModelCatalog?, replyModelLabel: String?): String? {
if (chat.isBotChat) return null
val providerId = chat.providerId ?: return null
val agentLabel = macOnlyAgentProviders[providerId] ?: return null
if (catalog?.providers?.any { it.id == providerId } == true) return null
return if (replyModelLabel != null) {
"This chat used $agentLabel, which runs only on your Mac. Replies from here use $replyModelLabel."
} else {
"This chat used $agentLabel, which runs only on your Mac. Choose a model for replies from here."
}
}

fun resolvedSelection(
chat: AidenChat,
catalog: AidenModelCatalog?,
Expand Down
16 changes: 16 additions & 0 deletions android/app/src/main/res/drawable/ic_provider_antigravity.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by android/scripts/generate-provider-logos.py from
renderer/assets/provider-logos/antigravity.svg. Do not edit by hand. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="24dp"
android:height="24dp"
android:viewportWidth="136"
android:viewportHeight="136"
tools:ignore="VectorPath">
<group android:translateX="-21.6" android:translateY="-22">
<path
android:fillColor="#FF000000"
android:pathData="M144.248 149.062C151.748 154.688 162.998 150.938 152.685 140.625C121.748 110.625 128.31 28.125 89.8727 28.125C51.4352 28.125 57.9977 110.625 27.0602 140.625C15.8102 151.875 27.9977 154.688 35.4977 149.062C64.5602 129.375 62.6852 94.6875 89.8727 94.6875C117.06 94.6875 115.185 129.375 144.248 149.062Z" />
</group>
</vector>
42 changes: 42 additions & 0 deletions android/app/src/test/java/sbtbiswas/AidenOnTheGo/AidenChatTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,48 @@ class AidenChatTest {
assertEquals("Form fill 1 filled · 1 not attempted · stopped early", AidenAgentActivityPresentation.line(step))
}

@Test
fun testAgentRunActivityMatchesMacPresentationLanguage() {
// Persisted labels as Google Antigravity steps record them; the Mac and
// iOS presentation tests expect the same lines.
data class Case(
val toolName: String,
val label: String,
val status: AidenAgentStepStatus,
val target: String?,
val detail: String?,
val line: String
)
val expected = listOf(
Case("delete_file", "Delete file", AidenAgentStepStatus.RUNNING, "old.ts", null, "Deleting old.ts"),
Case("delete_file", "Delete file", AidenAgentStepStatus.COMPLETED, "old.ts", null, "Deleted old.ts"),
Case("move_file", "Move file", AidenAgentStepStatus.COMPLETED, "src/a.ts", null, "Moved src/a.ts"),
Case("web_fetch", "Fetch web page", AidenAgentStepStatus.COMPLETED, null, null, "Fetched web page"),
Case("agent_subagents", "Run subagents", AidenAgentStepStatus.COMPLETED, null, null, "Ran subagents"),
Case("agent_tool", "Use agent tool", AidenAgentStepStatus.RUNNING, null, null, "Using an agent tool"),
Case("agent_context_rebuilt", "Started a fresh agent session from this chat", AidenAgentStepStatus.COMPLETED, null, null, "Started a fresh agent session"),
Case("run_command", "Run command", AidenAgentStepStatus.COMPLETED, null, "a command", "Ran a command")
)
expected.forEachIndexed { index, case ->
val step = AidenAgentStep(
id = "tool-$index",
order = index,
kind = AidenAgentStep.Kind.TOOL,
toolCallId = "call-$index",
toolName = case.toolName,
label = case.label,
status = case.status,
startedAt = 1000.0,
updatedAt = 2000.0,
finishedAt = if (case.status == AidenAgentStepStatus.COMPLETED) 2000.0 else null,
contentOffset = 0,
target = case.target,
detail = case.detail
)
assertEquals(case.toolName, case.line, AidenAgentActivityPresentation.line(step))
}
}

@Test
fun testAgentActivityPresentation() {
val readStep = AidenAgentStep(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class AidenModelPreferenceTest {
fun azureProviderIdentityKeepsItsArtwork() {
assertEquals("azure-openai-responses", sbtbiswas.AidenOnTheGo.features.shared.AidenProviderIconResolver.slug("azure"))
assertEquals("azure-openai-responses", sbtbiswas.AidenOnTheGo.features.shared.AidenProviderIconResolver.slug("azure-openai-responses"))
assertEquals("antigravity", sbtbiswas.AidenOnTheGo.features.shared.AidenProviderIconResolver.slug("antigravity"))
}

@get:Rule
Expand Down Expand Up @@ -73,6 +74,32 @@ class AidenModelPreferenceTest {
messages = emptyList(), createdAt = Instant.EPOCH, updatedAt = Instant.EPOCH, revision = "r1"
)

@Test
fun desktopStartedAgentChatSaysWhichModelRepliesFromThisPhone() {
// The Mac omits agent-backed providers from the phone's catalog.
val inventory = catalog(catalogWithoutRemembered)
val chat = workspaceChat(providerId = "antigravity", modelId = "gemini-3.8-flash")
val resolved = AidenChatModelAuthority.resolvedSelection(
chat = chat,
catalog = inventory,
selectedProviderId = chat.providerId,
selectedModelId = chat.modelId,
selectedThinkingLevel = null
)
assertEquals("google", resolved.providerId)
assertEquals(
"This chat used Google Antigravity, which runs only on your Mac. Replies from here use Gemini Flash.",
AidenChatModelAuthority.macOnlyAgentNotice(chat, inventory, "Gemini Flash")
)
assertEquals(
"This chat used Google Antigravity, which runs only on your Mac. Choose a model for replies from here.",
AidenChatModelAuthority.macOnlyAgentNotice(chat, inventory, null)
)
// A chat on a model this phone can use, or a Bot chat, says nothing.
assertNull(AidenChatModelAuthority.macOnlyAgentNotice(workspaceChat("google", "gemini-flash"), inventory, "Gemini Flash"))
assertNull(AidenChatModelAuthority.macOnlyAgentNotice(chat.copy(botId = "bot-1"), inventory, "Gemini Flash"))
}

@Test
fun rememberedHostChoiceWinsOverChatPairWhileTheHostStillOffersIt() {
val resolved = AidenChatModelAuthority.resolvedSelection(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class AidenProviderIconTest {
fun modelAndCustomProviderIdsResolveToTheirLogo() {
assertEquals("claude", resolver.slug("anthropic", "claude-sonnet-4"))
assertEquals("anthropic", resolver.slug("anthropic"))
assertEquals("antigravity", resolver.slug("antigravity"))
assertEquals("grok", resolver.slug("xai", "grok-4"))
assertEquals("google", resolver.slug(" Gemini "))
assertEquals("ollama", resolver.slug("custom:ollama-2"))
Expand Down
7 changes: 5 additions & 2 deletions docs/antigravity.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ Antigravity keeps its own conversation memory.
- **Rewinding or editing:** if you rewind, edit a message, or fork the chat, Aiden starts a fresh Antigravity session with a summary of the conversation. A "Started a fresh agent session" row marks when this happens.
- **Restarts:** after a restart, Aiden resumes the previous session.

Antigravity is available only in chats open on this computer. It is not available for Bots, scheduled tasks, Aiden Live, Telegram, subagents, or responses started from the iOS or Android app.
Antigravity is available only in chats open on this computer. It is not available for Bots, Assistant chats, scheduled tasks, Aiden Live, Telegram, subagents, or responses started from the iOS or Android app.

- **Assistant and Bot chats:** their model pickers leave Antigravity out. If one already has it selected, the composer explains why it can't send.
- **On the phone:** a chat Antigravity answered on your Mac shows its activity as usual. The phone's model list omits Antigravity, so a note above the composer says which model replies from the phone will use.

## Privacy and network

- **Credentials:** they stay in a private Antigravity profile inside Aiden's app data. Aiden never reads or changes `~/.gemini`.
- **No background network:** Aiden contacts the network for Antigravity only when you install it, sign in, refresh models, or chat with it. There are no background update checks; a new runtime version arrives with a new Aiden release.
- **Sign out:** this removes Antigravity's credentials.
- **Remove runtime:** this deletes the downloaded files; your sign-in and chats are kept.
- **Remove runtime:** this deletes the downloaded files; your sign-in and chats are kept. It is unavailable while an Antigravity chat is responding, and the dialog says so.

To hide the provider entirely, start Aiden with `AIDEN_DISABLE_ANTIGRAVITY=1`.
33 changes: 33 additions & 0 deletions ios/AidenOnTheGo/Features/Remote/AidenChatFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,28 @@ enum AidenChatModelAuthority {
)
}

/// Agent-backed providers (mirroring the Mac's ACP harness list) run only in
/// chats open on the Mac, so the host leaves them out of the phone's catalog.
static let macOnlyAgentProviders: [String: String] = ["antigravity": "Google Antigravity"]

/// For a chat the Mac last answered with a Mac-only agent model, which model
/// replies from this phone use instead. Nil when the chat's own model is
/// available here, or for Bot chats, whose model is fixed.
static func macOnlyAgentNotice(
chat: AidenChat,
catalog: AidenModelCatalog?,
replyModelLabel: String?
) -> String? {
guard !chat.isBotChat,
let providerId = chat.providerId,
let agentLabel = macOnlyAgentProviders[providerId],
catalog?.providers.contains(where: { $0.id == providerId }) != true else { return nil }
if let replyModelLabel {
return String(localized: "This chat used \(agentLabel), which runs only on your Mac. Replies from here use \(replyModelLabel).")
}
return String(localized: "This chat used \(agentLabel), which runs only on your Mac. Choose a model for replies from here.")
}

/// The per-host remembered choice applies only while the host's current
/// inventory still offers it as a visible provider/model pair. A missing
/// or hidden pair returns nil so the caller falls back to the chat's pair
Expand Down Expand Up @@ -1454,6 +1476,9 @@ final class AidenChatViewModel {
return selectedModel?.acceptsImageInput ?? true
}
var selectedModelDisplayLabel: String { selectedModel?.label ?? selectedModelId ?? "Model unavailable" }
var macOnlyAgentNotice: String? {
AidenChatModelAuthority.macOnlyAgentNotice(chat: chat, catalog: catalog, replyModelLabel: selectedModel?.label)
}

func transcribeMacSpeech(_ pcm16: Data) async throws -> String {
guard !isReadOnlyFixture else { throw AidenRemoteClientError.invalidResponse }
Expand Down Expand Up @@ -5019,6 +5044,14 @@ struct AidenChatDetailView: View {
openTasks: { progressSheet = .tasks },
openAgents: { progressSheet = .agents }
)
if let notice = model.macOnlyAgentNotice {
Text(notice)
.font(.footnote)
.foregroundStyle(palette.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 4)
.padding(.bottom, 6)
}
AidenComposerView(
model: model,
autoStartVoice: autoStartVoice,
Expand Down
1 change: 1 addition & 0 deletions ios/AidenOnTheGo/Features/Shared/AidenProviderIcon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ enum AidenProviderIconResolver {
"amazon-bedrock",
"ant-ling",
"anthropic",
"antigravity",
"apple-foundation-models",
"azure-openai-responses",
"cerebras",
Expand Down
7 changes: 7 additions & 0 deletions ios/AidenOnTheGo/Models/AidenChat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,13 @@ enum AidenAgentActivityPresentation {
"browser_recording_stop": ("Stopping browser recording", "Stopped browser recording"),
"vcc_recall": ("Recalling chat history", "Recalled chat history"),
"compact_context": ("Compacting context", "Compacted context"),
// Activity an ACP agent (for example, Google Antigravity) ran itself.
"delete_file": ("Deleting", "Deleted"),
"move_file": ("Moving", "Moved"),
"web_fetch": ("Fetching web page", "Fetched web page"),
"agent_subagents": ("Running subagents", "Ran subagents"),
"agent_tool": ("Using an agent tool", "Used an agent tool"),
"agent_context_rebuilt": ("Starting a fresh agent session", "Started a fresh agent session"),
]

/// Exactly one compaction: its line carries every metric. Repeated compactions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"images": [
{
"filename": "antigravity.svg",
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
},
"properties": {
"preserves-vector-representation": true,
"template-rendering-intent": "template"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ its Xcode asset catalog so native provider surfaces use the same identity map.
- Official provider sites: Ant Ling, Fireworks, Groq, Together, Z.AI, OpenCode,
Kimi/Moonshot, and their regional or product aliases.
- Wikimedia Commons: xAI and Grok.
- Google's official one-colour Antigravity icon: Google Antigravity, with the
viewBox fitted to the glyph.
- Lobe Icons static SVG package: Cerebras, replacing an incorrect OpenAI asset
in the supplied folder.

Expand Down
Loading
Loading