fix(native-chat): link Antigravity sign-in instructions to settings (#30970)

* fix(native-chat): link Antigravity sign-in notices to settings

* test(native-chat): use typed failure sentences in settings link coverage

* fix(native-chat): put Antigravity settings link in sign-in sentence
This commit is contained in:
Brennan Benson
2026-10-19 20:39:10 -07:00
committed by GitHub
parent c4a60815b2
commit 036fc58ecb
16 changed files with 147 additions and 15 deletions
@@ -86,7 +86,7 @@ function NoticeRow({ notice }: { notice: NativeChatComposerNotice }): React.JSX.
isHostWarning && 'text-destructive'
)}
>
{notice.text}
{notice.content ?? notice.text}
</p>
{notice.action ? (
<Button
@@ -8,6 +8,7 @@ import type { AgentJournalStatusItem } from '../../../../shared/agent-session-jo
import { TooltipProvider } from '@/components/ui/tooltip'
import { MessageRow } from './NativeChatMessageRow'
import { NativeChatNoticeRow } from './NativeChatNoticeRow'
import { useAppStore } from '@/store'
import { i18n } from '@/i18n/i18n'
import { agentSessionFailureWords } from '../../../../shared/agent-session-failure-words'
import { agentSessionFailureFact } from '../../../../shared/agent-session-failure'
@@ -541,3 +542,62 @@ describe('Codex installation in notice rows', () => {
}
)
})
it('opens Antigravity accounts from its signed-out notice, and offers no link for other failures', () => {
const original = useAppStore.getState()
const openSettingsPage = vi.fn()
const openSettingsTarget = vi.fn()
useAppStore.setState({ openSettingsPage, openSettingsTarget })
try {
renderStatus(
{
kind: 'status',
tone: 'error',
...agentSessionFailureWords(
{ kind: 'notSignedIn' },
{ agentName: 'Antigravity', surface: 'row' }
)
},
null,
false,
false,
'Antigravity'
)
const link = screen.getByRole('link', { name: 'Settings → Accounts → Antigravity' })
expect(link.parentElement).toHaveTextContent(
"Antigravity isn't signed in. Sign in under Settings → Accounts → Antigravity. Then send your message again."
)
expect(screen.queryByRole('button')).toBeNull()
fireEvent.click(link)
expect(openSettingsTarget).toHaveBeenCalledExactlyOnceWith({
pane: 'accounts',
repoId: null,
sectionId: 'accounts-antigravity'
})
expect(openSettingsPage).toHaveBeenCalledOnce()
cleanup()
for (const [agentName, kind] of [
['Antigravity', 'startFailed'],
['Claude', 'notSignedIn']
] as const) {
renderStatus(
{
kind: 'status',
tone: 'error',
...agentSessionFailureWords({ kind }, { agentName, surface: 'row' })
},
null,
false,
false,
agentName
)
expect(screen.queryByRole('link')).toBeNull()
cleanup()
}
} finally {
useAppStore.setState({
openSettingsPage: original.openSettingsPage,
openSettingsTarget: original.openSettingsTarget
})
}
})
@@ -28,6 +28,7 @@ import {
import { readWholeAgentSessionFailureFact } from '../../../../shared/agent-session-failure'
import { agentSessionFailureSentence } from '../../../../shared/agent-session-failure-words'
import { sayAgentSessionFailureTranslated } from './agent-session-failure-words-text'
import { nativeChatAntigravitySignInContent } from './native-chat-antigravity-settings'
import { formatUsageLimitResetClock } from '../../../../shared/agent-session-usage-limit'
const HOST_STATUS_WORDS: Record<AgentSessionHostStatusPresentation, () => string> = {
@@ -134,6 +135,7 @@ export function NativeChatNoticeRow({
const continueOffered =
orcaStop?.turnItemId !== undefined && orcaStop.turnItemId === orcaStopView.offeredTurnItemId
const failure = readWholeAgentSessionFailureFact(block.failure)
const antigravitySignInContent = nativeChatAntigravitySignInContent(failure?.kind, agentName)
const usageLimitContinue =
block.usageLimitTurnItemId !== undefined &&
block.usageLimitTurnItemId === orcaStopView.usageLimitTurnItemId
@@ -190,7 +192,9 @@ export function NativeChatNoticeRow({
>
<div className="flex items-start gap-2">
{Icon ? <Icon aria-hidden="true" className="mt-0.5 size-4 shrink-0" /> : null}
<p className="min-w-0 flex-1 whitespace-pre-wrap break-words">{text}</p>
<p className="min-w-0 flex-1 whitespace-pre-wrap break-words">
{antigravitySignInContent ?? text}
</p>
{continueOffered ? (
<NativeChatInterruptedContinueButton onContinue={orcaStopView.continueNow} />
) : usageLimitContinue ? (
@@ -0,0 +1,40 @@
import { Trans } from 'react-i18next'
import { i18n, translate } from '@/i18n/i18n'
import { useAppStore } from '@/store'
import { agentSessionSignInFor } from '../../../../shared/agent-session-sign-in'
export function nativeChatAntigravitySignInContent(
failureKind: string | undefined,
agentName: string | undefined
): React.JSX.Element | undefined {
if (failureKind !== 'notSignedIn' || agentSessionSignInFor(agentName)?.agent !== 'antigravity') {
return undefined
}
return (
<Trans
i18n={i18n}
defaults={translate(
'components.native-chat.antigravitySignIn.sentence',
"Antigravity isn't signed in. Sign in under <settings>Settings → Accounts → Antigravity</settings>. Then send your message again."
)}
components={{
settings: (
<a
href="#accounts-antigravity"
className="text-chat-foreground underline underline-offset-2 hover:text-chat-foreground-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
onClick={(event) => {
event.preventDefault()
const { openSettingsTarget, openSettingsPage } = useAppStore.getState()
openSettingsTarget({
pane: 'accounts',
repoId: null,
sectionId: 'accounts-antigravity'
})
openSettingsPage()
}}
/>
)
}}
/>
)
}
@@ -7,6 +7,8 @@ import { isClipboardTextTooLargeError } from '../../../../shared/clipboard-text'
export type NativeChatComposerNoticeContent = {
text: string
/** Inline presentation of the sentence when it contains navigation. */
content?: React.ReactNode
/** Text from outside Orca (a host or system error), shown apart and copyable. */
errorText?: string
}
@@ -1,3 +1,4 @@
import { nativeChatAntigravitySignInContent } from './native-chat-antigravity-settings'
import { translate } from '@/i18n/i18n'
import type { StructuredAgentSessionLaunchLifecycle } from '@/lib/structured-agent-session-launch'
import { agentSessionRefusalCauseParts } from '../../../../shared/agent-session-refusal-notice'
@@ -53,7 +54,9 @@ function nativeChatLaunchNotice({
// An argument problem already says the start failed; the generic lead would repeat it.
const saysStartFailure =
failure?.code === 'agent_session_operation_invalid' && failure.details?.argumentProblem
const signInContent = nativeChatAntigravitySignInContent(failure?.details?.reason, agentLabel)
return {
content: signInContent,
key: 'launch',
kind: 'error',
text: cause ? (saysStartFailure ? cause : joinSentences([message, cause])) : message,
@@ -64,10 +67,12 @@ function nativeChatLaunchNotice({
onClick: claudeSignIn.signIn,
disabled: claudeSignIn.signingIn
}
: {
label: translate('auto.components.native.chat.NativeChatLaunchRetry.retry', 'Retry'),
onClick: onRetry
}
: signInContent
? undefined
: {
label: translate('auto.components.native.chat.NativeChatLaunchRetry.retry', 'Retry'),
onClick: onRetry
}
}
}
@@ -13,6 +13,7 @@ import type { AgentSessionWriteRefusal } from '../../../../shared/agent-session-
import { isStructuredAgentSessionStartFailureRow } from '../../../../shared/structured-agent-session-start-failure-row-key'
import { sayAgentSessionFailureTranslated } from './agent-session-failure-words-text'
import { notSignedInSentence } from '../../../../shared/agent-session-availability-sentences'
import { nativeChatAntigravitySignInContent } from './native-chat-antigravity-settings'
import type { NativeChatComposerNotice } from './native-chat-composer-notice'
/** Why the chat's latest start failed, unless a turn has run since. */
@@ -109,6 +110,7 @@ export function useNativeChatAvailabilityNotice(input: {
{ kind: 'notSignedIn', account: unavailable.account },
sayAgentSessionFailureTranslated
),
content: nativeChatAntigravitySignInContent(unavailable.reason, input.agent),
onDismiss: () => setDismissal({ key, dismissed: true })
}
}
+1 -1
View File
@@ -2892,7 +2892,7 @@
"agentCommandNotSignedIn": "Sign in to {{agent}} with `{{loginCommand}}` on the computer running this chat.",
"agentNotSignedIn": "Sign in to {{agent}}.",
"answerUnconfirmed": "Your answer was recorded but the agent did not confirm it.",
"antigravityNotSignedIn": "Antigravity isn't signed in. Sign in under Settings → Accounts → Antigravity.",
"antigravityNotSignedIn": "Antigravity isn't signed in. Sign in under Settings → Accounts → Antigravity. Then send your message again.",
"argumentsMissingValue": "Saved Arguments need a value for {{option}}.",
"argumentsMultipleValues": "Saved Arguments give {{option}} more than one value.",
"argumentsPositionalPrompt": "Saved Arguments include a prompt.",
+4 -1
View File
@@ -18360,7 +18360,7 @@
"claudeManagedNotSignedIn": "This Claude account isn't signed in. Sign in again in Claude Accounts settings.",
"codexSystemNotSignedIn": "Codex isn't signed in. Run `{{loginCommand}}`.",
"codexManagedNotSignedIn": "This Codex account isn't signed in. Sign in again in Codex Accounts settings.",
"antigravityNotSignedIn": "Antigravity isn't signed in. Sign in under Settings → Accounts → Antigravity.",
"antigravityNotSignedIn": "Antigravity isn't signed in. Sign in under Settings → Accounts → Antigravity. Then send your message again.",
"cliMissing": "{{agent}} wasn't found on the computer running this chat. Install it, or check its Command in Settings → Agents.",
"signInFirst": "Sign in first.",
"signInThenRunCommand": "Sign in, then run /{{command}} again.",
@@ -19082,6 +19082,9 @@
"unconfirmed": "Couldn't confirm whether {{agent}} received Continue. Check the chat before trying again.",
"usageLimitExplanation": "Asks the agent to continue where it left off. Use it once the limit resets."
},
"antigravitySignIn": {
"sentence": "Antigravity isn't signed in. Sign in under <settings>Settings → Accounts → Antigravity</settings>. Then send your message again."
},
"claudeSignIn": {
"signingIn": "Signing in…"
},
+4 -1
View File
@@ -18046,7 +18046,7 @@
"claudeManagedNotSignedIn": "Esta cuenta de Claude no ha iniciado sesión. Vuelve a iniciar sesión en la configuración de Cuentas de Claude.",
"codexSystemNotSignedIn": "Codex no ha iniciado sesión. Ejecuta `{{loginCommand}}`.",
"codexManagedNotSignedIn": "Esta cuenta de Codex no ha iniciado sesión. Vuelve a iniciar sesión en la configuración de Cuentas de Codex.",
"antigravityNotSignedIn": "Antigravity no ha iniciado sesión. Inicia sesión en Ajustes → Cuentas → Antigravity.",
"antigravityNotSignedIn": "Antigravity no ha iniciado sesión. Inicia sesión en Ajustes → Cuentas → Antigravity. Después, vuelve a enviar tu mensaje.",
"cliMissing": "No se encontró {{agent}} en el equipo que ejecuta este chat. Instálelo o revise su Comando en Ajustes → Agentes.",
"signInFirst": "Inicie sesión primero.",
"signInThenRunCommand": "Inicie sesión y vuelva a ejecutar /{{command}}.",
@@ -18764,6 +18764,9 @@
"replaceDescription": "Reanudar “{{title}}” aquí detendrá lo que {{agent}} está haciendo en este chat.",
"replace": "Reemplazar"
},
"antigravitySignIn": {
"sentence": "Antigravity no ha iniciado sesión. Inicia sesión en <settings>Ajustes → Cuentas → Antigravity</settings>. Después, vuelve a enviar tu mensaje."
},
"claudeSignIn": {
"signingIn": "Iniciando sesión…"
}
+4 -1
View File
@@ -18155,7 +18155,7 @@
"claudeManagedNotSignedIn": "Ce compte Claude n’est pas connecté. Reconnectez-vous dans les paramètres des Comptes Claude.",
"codexSystemNotSignedIn": "Codex n’est pas connecté. Exécutez `{{loginCommand}}`.",
"codexManagedNotSignedIn": "Ce compte Codex n’est pas connecté. Reconnectez-vous dans les paramètres des Comptes Codex.",
"antigravityNotSignedIn": "Antigravity n’est pas connecté. Connectez-vous dans Paramètres → Comptes → Antigravity.",
"antigravityNotSignedIn": "Antigravity n’est pas connecté. Connectez-vous dans Paramètres → Comptes → Antigravity. Puis renvoyez votre message.",
"cliMissing": "{{agent}} est introuvable sur l’ordinateur qui exécute cette conversation. Installez-le ou vérifiez sa Commande dans Paramètres → Agents.",
"signInFirst": "Connectez-vous d'abord.",
"signInThenRunCommand": "Connectez-vous, puis relancez /{{command}}.",
@@ -18873,6 +18873,9 @@
"replaceDescription": "Reprendre « {{title}} » ici arrêtera ce que {{agent}} fait dans ce chat.",
"replace": "Remplacer"
},
"antigravitySignIn": {
"sentence": "Antigravity n’est pas connecté. Connectez-vous dans <settings>Paramètres → Comptes → Antigravity</settings>. Puis renvoyez votre message."
},
"claudeSignIn": {
"signingIn": "Connexion…"
}
+4 -1
View File
@@ -18086,7 +18086,7 @@
"claudeManagedNotSignedIn": "この Claude アカウントにサインインしていません。設定の Claude アカウントで再度サインインしてください。",
"codexSystemNotSignedIn": "Codex にサインインしていません。`{{loginCommand}}` を実行してください。",
"codexManagedNotSignedIn": "この Codex アカウントにサインインしていません。設定の Codex アカウントで再度サインインしてください。",
"antigravityNotSignedIn": "Antigravity にサインインしていません。[設定] → [アカウント] → [Antigravity] でサインインしてください。",
"antigravityNotSignedIn": "Antigravity にサインインしていません。設定 → アカウント → Antigravityでサインインしてから、メッセージをもう一度送信してください。",
"cliMissing": "このチャットを実行しているコンピューターで {{agent}} が見つかりませんでした。インストールするか、[設定] → [Agent] の [コマンド] を確認してください。",
"signInFirst": "先にサインインしてください。",
"signInThenRunCommand": "サインインしてから、/{{command}} をもう一度実行してください。",
@@ -18804,6 +18804,9 @@
"replaceDescription": "ここで「{{title}}」を再開すると、このチャットで {{agent}} が行っている処理が停止します。",
"replace": "置き換える"
},
"antigravitySignIn": {
"sentence": "Antigravity にサインインしていません。<settings>設定 → アカウント → Antigravity</settings>でサインインしてから、メッセージをもう一度送信してください。"
},
"claudeSignIn": {
"signingIn": "サインイン中…"
}
+4 -1
View File
@@ -18086,7 +18086,7 @@
"claudeManagedNotSignedIn": "이 Claude 계정에 로그인하지 않았습니다. Claude 계정 설정에서 다시 로그인하세요.",
"codexSystemNotSignedIn": "Codex에 로그인하지 않았습니다. `{{loginCommand}}`을 실행하세요.",
"codexManagedNotSignedIn": "이 Codex 계정에 로그인하지 않았습니다. Codex 계정 설정에서 다시 로그인하세요.",
"antigravityNotSignedIn": "Antigravity에 로그인되어 있지 않습니다. 설정 → 계정 → Antigravity에서 로그인하세요.",
"antigravityNotSignedIn": "Antigravity에 로그인되어 있지 않습니다. 설정 → 계정 → Antigravity에서 로그인한 후 메시지를 다시 보내세요.",
"cliMissing": "이 채팅을 실행하는 컴퓨터에서 {{agent}}를 찾을 수 없습니다. 설치하거나 설정 → 에이전트에서 명령을 확인하세요.",
"signInFirst": "먼저 로그인하세요.",
"signInThenRunCommand": "로그인한 다음 /{{command}}을(를) 다시 실행하세요.",
@@ -18804,6 +18804,9 @@
"replaceDescription": "여기서 “{{title}}”을(를) 재개하면 이 채팅에서 {{agent}}이(가) 하고 있는 작업이 중지됩니다.",
"replace": "바꾸기"
},
"antigravitySignIn": {
"sentence": "Antigravity에 로그인되어 있지 않습니다. <settings>설정 → 계정 → Antigravity</settings>에서 로그인한 후 메시지를 다시 보내세요."
},
"claudeSignIn": {
"signingIn": "로그인 중…"
}
+4 -1
View File
@@ -18051,7 +18051,7 @@
"claudeManagedNotSignedIn": "此 Claude 账户尚未登录。请在 Claude 账户设置中重新登录。",
"codexSystemNotSignedIn": "Codex 尚未登录。请运行 `{{loginCommand}}`。",
"codexManagedNotSignedIn": "此 Codex 账户尚未登录。请在 Codex 账户设置中重新登录。",
"antigravityNotSignedIn": "Antigravity 尚未登录。请在设置 → 账户 → Antigravity 中登录。",
"antigravityNotSignedIn": "尚未登录 Antigravity。请在设置 → 账户 → Antigravity中登录,然后重新发送消息。",
"cliMissing": "在运行此聊天的计算机上找不到 {{agent}}。请安装它,或检查设置 → 智能体中的命令。",
"signInFirst": "请先登录。",
"signInThenRunCommand": "请登录,然后重新运行 /{{command}}。",
@@ -18769,6 +18769,9 @@
"replaceDescription": "在此恢复“{{title}}”将停止 {{agent}} 在此聊天中正在进行的工作。",
"replace": "替换"
},
"antigravitySignIn": {
"sentence": "尚未登录 Antigravity。请在<settings>设置 → 账户 → Antigravity</settings>中登录,然后重新发送消息。"
},
"claudeSignIn": {
"signingIn": "正在登录…"
}
@@ -35,7 +35,8 @@ export function notSignedInSentence(
: surface === 'rejection' &&
context.messageSubmitted !== false &&
signIn?.agent !== 'claude' &&
signIn?.agent !== 'codex'
signIn?.agent !== 'codex' &&
signIn?.agent !== 'antigravity'
? say(signIn ? 'thenSendAgain' : 'signInThenSend')
: undefined
const detail = fact.detail?.audience === 'person' ? fact.detail.text : undefined
+1 -1
View File
@@ -47,7 +47,7 @@ export const AGENT_SESSION_FAILURE_COPY = {
codexManagedNotSignedIn:
"This Codex account isn't signed in. Sign in again in Codex Accounts settings.",
antigravityNotSignedIn:
"Antigravity isn't signed in. Sign in under Settings → Accounts → Antigravity.",
"Antigravity isn't signed in. Sign in under Settings → Accounts → Antigravity. Then send your message again.",
agentCommandNotSignedIn:
'Sign in to {{agent}} with `{{loginCommand}}` on the computer running this chat.',
interactiveAgentNotSignedIn: