Files
orca/src/shared/agent-session-availability.test.ts
T
Brennan Benson 46be7e23ee feat(settings): sign Antigravity chats in from Accounts (#28705)
* feat(accounts): sign Antigravity chats in from the Accounts page

Adds an 'Antigravity chats' row to the Antigravity section of Accounts
(behind the native chat setting, this device only): Sign in starts the
host's chat sign-in, follows it with the Link poll, opens the Google page
once, offers Copy link and Cancel, and shows one status line per phase.
The saved line reflects the sign-in file only, never that it works.

* Keep Antigravity chat sign-in bound to the current view

* feat(settings): put Antigravity chat sign-in under its install row in Chat > Providers and Accounts

The chats' sign-in row now sits directly below the Antigravity install row,
in Settings > Chat > Providers and under the agy section of Accounts, with copy
that says it is separate from the agy account. Its button reads Sign in, Change
account or Retry sign-in by state, and stays visible but disabled while the host
has no runnable server; the install row above says why.

* fix(settings): both Antigravity chat sign-in rows share one sign-in per host and pick up one already running

Settings shows the sign-in row under Chat > Providers and under Accounts at once; each kept its own
flow, so starting one silently cancelled the other and a sign-in nobody watched could not be seen or
cancelled. One entry per host now holds the flow and the saved sign-in for every row, follows the
host's Link poll while a row shows it, and on mount adopts the running flow the host names. Sign in
also waits while an install or Remove runs, read from the same install status.

* fix(settings): a failed sign-in poll re-reads the host so a running sign-in stays cancellable, and only a Remove (not an Update) holds Sign in

* fix(settings): the sign-in rows follow the host's sign-in status through the same loop as install, so an idle page sees another device's sign-in

* refactor(settings): install and sign-in rows share one subscription store; Install and Remove wait for a running sign-in, and Sign in waits for the host's answer

Both stores now follow the host through one generic subscription store
(snapshot, then a frame per newer revision, resubscribe with backoff on loss),
replacing the sign-in store's long-poll of a method the host no longer has.
While a sign-in runs, Install, Update and Remove are disabled (Cancel stays).
The sign-in row keeps Sign in disabled until the host answers, says when the
status could not be read instead of showing signed out, and sends one start or
cancel at a time.

* feat(settings): paste the last sign-in page's address to finish an Antigravity chat sign-in

While a chat sign-in waits on the browser, its row shows a field for the address of the last page after signing in, for when that page doesn't load because the browser is on another computer than the host. Continue sends it to the host, which passes it on to Antigravity; the host's refusal shows under the field. With that, remote hosts offer chat sign-in on the Accounts page too, and the row reads "Finishing sign-in…" while the host completes it.

* test(settings): give each host its own fake sign-in status and stub the new subscription export

The sign-in row tests shared one fake host across every owner, so the first test's finished sign-in made the next row show "Change account" instead of "Sign in", and rows left mounted after that failure doubled the later tests' matches. Each owner now has its own fake host behind the subscription, and every test cleans up its render. The Antigravity accounts section test's client mock also lacked the sign-in exports the status store now reads at import.

* feat(settings): the Antigravity chat sign-in row follows the host's sign-in answer

The host no longer reports whether a sign-in file exists; it reports what the
server last answered (signed in, signed out, or not known yet), and a running
sign-in started by another client arrives without its id.

The row now reads that answer:
- "Signed in on {host}." or "Not signed in on {host}."; no line until the server
  has answered, with Sign in still enabled. Change account shows only when the
  server said signed in.
- A sign-in another client is running reads "Sign-in is in progress in another
  client.", with Sign in disabled and no Cancel, copy link or paste field, since
  only the client that started it can act on it.
- "Finishing sign-in..." also follows a browser sign-in while the host checks it.
- Cancel and paste send only this client's own sign-in; the host's refusals
  show under the row.

* fix(chat): a signed-out Antigravity chat says to sign in under Settings, not what the agent printed

The notice read "Sign in to Antigravity. The agent asked to sign in in a
browser": the second sentence was Orca's own log text for the sign-in prompt the
agent printed, shown as if it were the agent's words, and the notice never said
where to sign in.

The connection now closes with a sign-in prompt error whose message stays in the
logs, so the chat shows only Orca's sign-in sentence. Antigravity gets its own
sentence, in the shape Claude's already has: "Antigravity isn't signed in. Sign
in under Settings → Accounts → Antigravity." Other agents' sign-in words, and
the agent's own words in a sign-in answer, are unchanged.

* fix(chat): a sign-in prompt at chat start keeps Orca's words out of the signed-out notice

A signed-out Antigravity chat still read "... The agent asked to sign in in a
browser". The connection closes with a sign-in prompt error whose message is
Orca's log text, but the request wrapper and session setup re-typed every
sign-in answer by copying its message into a new error. The copy lost the
sign-in prompt type, so its message was quoted as the agent's own words.

Re-typing now keeps a sign-in prompt close as it is, at both the request
wrapper and session setup, so the notice says only where to sign in.

* fix(settings): the Antigravity sign-in row shows the host's answer once a sign-in is cancelled or timed out

After a cancelled or timed-out sign-in the row kept reading "Sign-in
cancelled." even when the host had answered that this computer is not signed
in. A finished sign-in now only stands in until the host answers; then the row
reads "Not signed in on <host>." or "Signed in on <host>.". A running sign-in
still shows its step.

* fix(settings): a wrong pasted sign-in address shows Orca's reason, not the browser's URL bubble

The paste field is a URL input, so the browser's own "Please enter a URL."
check ran before Orca could explain what was wrong with the address. The form
now skips the browser's check and shows the host's refusal sentence.

* style(settings): the Antigravity chat sign-in row reads as a plain row like the install row

The sign-in row sat in its own bordered box, the same bright outline the install
row lost when it became a single grey status line. It now has no box: a
medium-weight "Antigravity chats" title, the description and the grey status
line under it (errors inline in the destructive colour), and its action at the
row's right end. Sign in and Retry are primary buttons, so they stand out like
Install; Change account stays outline and Cancel stays ghost. The paste-the-
address form sits under the row without a box. Copy, keys and behaviour are
unchanged.

* ci: run the PR checks against main after the retarget

* fix(settings): list every sign-in phase in the status line switch

* test(chat): name Antigravity as the chat's agent in its signed-out sentence test

* feat(settings): Antigravity provider row with a ⋯ menu and its own sign-in area

The Providers row's status line now says only the install state. Update,
Reinstall and Remove move into a ⋯ menu beside the switch, shown for a copy
on disk; Remove keeps its confirmation. Sign-in moves into a quiet inset
area under the row, shown once a copy runs, with one sentence and one
action per state. Accounts keeps its sign-in row; its install row gets the
same menu.

* fix(settings): a damaged runtime offers Reinstall as its row's button

A damaged copy's repair is the next step, so it sits where Install does instead of inside the ⋯ menu, which then offers only Remove.
2026-10-18 21:28:43 -07:00

51 lines
2.1 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { readAgentSessionUnavailable, agentSessionSignInCopyId } from './agent-session-availability'
import { agentSessionFailureSentence } from './agent-session-failure-words'
import { AGENT_SESSION_FAILURE_COPY } from './agent-session-failure-copy'
describe("the host's sign-in and CLI verdict", () => {
it.each([undefined, null, {}, { reason: 'future' }, { state: 'notSignedIn' }, 'notSignedIn'])(
'reads an unknown or malformed verdict as unknown %j',
(value) => {
expect(readAgentSessionUnavailable(value)).toBeNull()
}
)
it.each(['managed', 'system'] as const)('keeps %s account context', (account) => {
expect(readAgentSessionUnavailable({ reason: 'notSignedIn', account })).toEqual({
reason: 'notSignedIn',
account
})
})
it('drops account context this build does not know', () => {
expect(readAgentSessionUnavailable({ reason: 'notSignedIn', account: 'future' })).toEqual({
reason: 'notSignedIn'
})
expect(readAgentSessionUnavailable({ reason: 'cliMissing', account: 'system' })).toEqual({
reason: 'cliMissing'
})
})
it.each([
['claude', 'system', 'claudeSystemNotSignedIn'],
['claude', 'managed', 'claudeManagedNotSignedIn'],
['claude', undefined, 'claudeSystemNotSignedIn'],
['codex', 'system', 'codexSystemNotSignedIn'],
['codex', 'managed', 'codexManagedNotSignedIn'],
['codex', undefined, 'codexSystemNotSignedIn']
] as const)('uses the same %s %s fix after sending', (provider, account, key) => {
expect(agentSessionSignInCopyId(provider, account)).toBe(key)
expect(
agentSessionFailureSentence({ kind: 'notSignedIn', account }, 'rejection', { provider })
).toBe(
AGENT_SESSION_FAILURE_COPY[key].replace(
'{{loginCommand}}',
provider === 'claude' ? 'claude auth login' : 'codex login'
)
)
})
it('sends Antigravity to its sign-in in Settings', () => {
expect(
agentSessionFailureSentence({ kind: 'notSignedIn' }, 'row', { agentName: 'antigravity' })
).toBe(AGENT_SESSION_FAILURE_COPY.antigravityNotSignedIn)
})
})