Terminal e providers
Cada card de terminal é um processo real de um provider — Claude, Codex, Cursor, Antigravity, OpenCode, Cline, Command Code ou um shell puro.
Cada card de terminal é um PTY de verdade, rodando um provider ou um shell puro. O Stellar não simula terminal: o que aparece no card é o processo.
Providers
| Provider | O que roda |
|---|---|
claude |
Claude Code |
codex |
Codex |
cursor |
Cursor |
antigravity |
Antigravity (Gemini) |
opencode |
OpenCode |
cline |
Cline |
commandcode |
Command Code |
bash |
um shell, sem agente por trás |
Os últimos dois não são código: são dado. O catálogo embutido declara
binário, como o id de sessão entra, qual flag carrega prompt de sistema, onde
fica o config de MCP, esforço e modelo — e os argumentos são sintetizados a
partir dessa declaração. É o que permite declarar os seus: um
providers.json na pasta de configuração registra um CLI novo sem reiniciar
o app (ver Onde ficam os dados).
O provider se escolhe na criação do card. Ele não é herdado de quem despachou: uma task que não declara provider é recusada, de propósito — prender a cadeia inteira no provider de quem começou seria uma decisão silenciosa demais.
Instalado não é pronto
O binário resolver no PATH responde uma pergunta só. A disponibilidade tem
quatro respostas, e cada uma é do tamanho da prova que a sustenta:
| Resposta | O que sustenta |
|---|---|
missing |
o binário não resolve no PATH |
not-ready |
instalado, e o probe declarado respondeu que não está pronto (credencial faltando, por exemplo) |
ready |
instalado, e o probe respondeu que está |
unknown |
instalado, e o app não sabe — nenhum probe foi declarado, ou ele não chegou a responder |
unknown é o default de um provider que não declara nada, e nunca vira
“pronto” por otimismo. O caso que forçou isso está medido: um CLI instalado e
sem credencial aparecia como disponível, o spawn “dava certo” e o card ficava
calado para sempre. O probe mora na declaração do provider porque “pronto”
varia por harness — e o que o app lê é o campo que o próprio tool imprime, não
o exit code.
Zoom sem reflow
O zoom do canvas é ótico: o texto escala como imagem, sem disparar
fit() nem reflow de colunas e linhas no meio da sessão. O PTY só é
redimensionado quando você arrasta a borda física do card.
Sessão e retomada
Todo card pode ser retomado com o id de sessão do provider, mas não do mesmo jeito — e a diferença que importa é entre impor e retomar.
Impor é criar a sessão já com o id escolhido pelo Stellar. Só dois
providers sabem: claude, com --session-id, e cursor, com
--new-session-id. No cursor a flag de impor não aparece no --help, e por
um tempo o app usou --resume para os dois papéis — que pedia para retomar
uma sessão que ainda não existia.
Retomar é apontar para uma sessão que já existe, descoberta no store do
provider: cline (--id), codex (resume <id> — um subcomando, não uma
flag), opencode (--session), commandcode (--resume) e antigravity
(--conversation). Nenhum desses aceita um id desconhecido: um id imposto
seria ignorado em silêncio.
O registro guarda dois ids distintos: o resumeId pedido no spawn e o
sessionId descoberto em runtime. Quando o provider impõe um id diferente do
pedido, é o segundo que vale.
E a imposição é conferida, não presumida. Depois do spawn, o app lê o
store declarado pelo provider e procura o id imposto ali. Se não achar, a
declaração foi contradita por medição e o watcher de sessão volta a valer —
em vez de o card apontar para sempre para uma sessão que não existe. A
checagem existe porque canImposeSessionId: true é uma afirmação num arquivo
de configuração, e nada a conferia: foi assim que o defeito do cline (a flag
era de retomar) e o do cursor (a flag estava errada) passaram.
Onde não há nem imposição nem sessão descobrível, o registro fica ausente, honestamente, em vez de inventar um id.
Cota e morte de agente
Providers morrem por cota no meio do trabalho. Antes de concluir que o agente falhou, leia o scrollback do card: morte por cota se parece com morte por bug quando ninguém olha.