# Inference

how an agent thinks — which provider (Claude or Codex), which model, and whose login pays: one shared login, or each person's own.

**Has:** a provider · a default model · whose login (shared | per person) · a login state, on the agent when it is shared and on each agent-account when it is per person. An agent has one; losing the agent loses the login with it.

## Provider and model

- An agent thinks through Claude or Codex, chosen on its screen; anything else is refused and nothing is saved.
- Choosing a provider, on the agent's screen or in New agent, shows that provider's models at once. If the current model is not one of them, the provider's default is selected (Claude: Sonnet 5; Codex: GPT-5.6 Sol).
  - e.g. New agent, pick Codex → the model list is Codex's, and Sol is selected.
- An agent is never saved with another provider's model; the API refuses the pair and keeps what was there. A model no longer on offer is shown by its id, marked retired, until changed.
  - e.g. a stale form sends Codex + `sonnet` → refused; the agent keeps its settings.
  - e.g. agent on Codex, switch to Claude → the model list becomes Claude's, before saving.
- Claude offers Opus 5, Sonnet 5 and Haiku 4.5; Codex offers GPT-5.6 Sol, Terra and Luna.
- The model chosen in the Hub is what a new conversation starts on, and a person can switch within that provider's models in the channel.

## Whose login

- An agent runs on one shared login or on each person's own, chosen on its screen; anything else is refused.
- A turn on a shared agent runs on the agent's login; a turn on a per-person agent runs on the speaker's own subscription.
- An unattended Skill run on a per-person agent runs on the subscription of the person whose item it is, and otherwise on the agent's login.

## Signing in

- A person signs an agent in from its own channel with `!connect claude` or `!connect codex`, never from the Hub.
- On Claude the agent offers a sign-in link and takes the code back in a private dialog, never in the channel.
- On Codex the agent sends the person a sign-in link and a one-time code privately, then says whether it went through.
  - e.g. nothing arrives within ten minutes → the agent says the code may have expired and offers a fresh one.
- A login counts as signed in only when the credential really changed and the provider itself agrees; anything else is reported as a failure.
- A login is held with the agent that uses it, and per person on a per-person agent; it never reaches the Hub.
- A first Codex login works on an agent, or for a person, that has never had one.
- A turn runs on the subscription that was signed in, never on a provider API key.
- `!disconnect claude` signs out your own login on a per-person agent and the agent's on a shared one; the next message offers a fresh login.
- Asked in the channel, an agent names the account it answers on — the speaker's own on a per-person agent.

## When there is no login

- A person with no login is offered one instead of the agent spending a turn; a shared agent asks in the channel when its own login is not working.
- A turn that fails for want of a login says exactly that and names what to send for this agent's provider, and the login is marked expired or in error.
- After a provider switch, what counts as signed in is each login on the new provider; logins on the old one are kept, untouched, for a switch back. A turn already running finishes on the provider it started with; the next turn uses the new one and starts a fresh session, and a thread's own model choice from the old provider is dropped. A late report from the old provider never changes the new badge.
  - e.g. a per-person agent with two Claude logins switched to Codex → "Nobody signed in yet"; switched back → "2 signed in" again, nobody signed in twice.
  - e.g. a Claude turn finishes after the switch → Codex still reads "Not signed in".
- Every login badge — card, header, Inference box, Accounts tab — uses the same words: "Signed in", "Not signed in", "Sign-in failed", "Sign-in expired". On a per-person agent it counts the tenant's members only, on the current provider: "2 signed in · 1 failed · 3 not yet", with expired counted as failed. "Connected" is for connections only.
  - e.g. nobody yet → "Nobody signed in yet"; one expired, nobody else → "1 failed · 0 signed in"; someone who left the tenant is not counted.
- A per-person agent is never shown as failed on one person's account: it reads as how many people are signed in, failed and not yet. A shared agent shows its one login's state.
  - e.g. two people signed in, one failed → "2 signed in · 1 failed", never "Login failed".
