OpenWork 0.18.60+ quebrou os modelos gratuitos e o arquivamento: causa raiz, análise do bundle e reversão para 0.18.59 + motor v1
Quando o OpenWork Desktop se atualizou sozinho para a versão 0.18.62, duas coisas quebraram ao mesmo tempo, sem nenhum aviso na interface:
-
Todos os modelos gratuitos (
deepseek-v4.1-flash-free,glm-5.3-flash-free,big-pickle, etc.) — tanto os do OpenCode Zen quanto os gratuitos acessados com chave própria e billing ativo noopencode.ai— passaram a falhar com:json { "type": "error", "error": { "type": "FreeTierError", "message": "OpenCode's free tier can only be used from within OpenCode. Download it at https://opencode.ai and keep building." } } -
O botão de arquivar sessão sumiu da barra lateral, e as conversas arquivadas anteriormente ficaram trancadas em modo somente-leitura.
Quem tentou editar o opencode.jsonc, ligar o "Allow OpenCode Zen models" no painel Den ou voltar apenas o motor de chat para o v1 mantendo o app na 0.18.60+ (como relatado nas issues different-ai/openwork#6408, #6466, #6467 e #6367) bateu na mesma parede: nada disso devolve os modelos.
Depois de três dias inspecionando o bundle do renderer, os bancos SQLite locais e os endpoints HTTP do motor versão por versão, isolei a causa exata de cada sintoma e o procedimento completo de reversão.
1. A Armadilha Principal: Duas Alavancas Independentes (Versão do App vs. Motor de Chat)
A maioria das tentativas de correção falha porque a atualização automática mudou duas variáveis independentes ao mesmo tempo. Elas parecem um bug único de migração, mas respondem a alavancas diferentes:
| Sintoma | O que realmente controla | Por que mexer em config não resolve |
|---|---|---|
Provider opencode some da UI e modelos *-free falham com 403 FreeTierError |
Versão do App Desktop (0.18.59 vs. >= 0.18.60) |
Injetado pelo app desktop na configuração gerenciada a cada boot/reload do motor |
Botão de arquivar desaparece (501) e estado de providers zera |
Motor de Chat (v1 1.18.30 vs. v2 2.0.26) |
Trava no endpoint do preview v2 (/opencode2) e diretório de estado isolado |
Testando os instaladores versão por versão, a fronteira ficou provada:
- A
0.18.59já embarca o binário do motorv2.0.26, e nela o provideropencodee todos os 45 modelos gratuitos funcionam normalmente. - O bloqueio começa estritamente na
0.18.60. Por isso, forçar o motorv1continuando no app0.18.60ou0.18.62devolve apenas o arquivamento — o provideropencodecontinua bloqueado pela versão do aplicativo.
2. Como a 0.18.60+ Bloqueia o Provider opencode (4 Camadas)
A partir da 0.18.60 (com o rollout removeOpencodeZen do PR #6340), o app desktop fecha o acesso ao opencode em quatro lugares simultâneos:
Camada 1 — O Modal da Interface (Connect a provider)
Na 0.18.59, abrir Connect a provider (settings.provider.add) mostra OpenCode (opencode) como a primeira linha da lista, permitindo salvar uma OPENCODE_API_KEY (ock_… / oc_sk_…) ou usar o fallback nativo do Zen ("Free models keep working without a key"). A partir da 0.18.60, o item OpenCode some completamente dessa lista — não há como selecioná-lo nem conectar chave pela UI.
Camada 2 — disabled_providers na Config Gerenciada do Runtime
O app grava "disabled_providers": ["opencode"] nos arquivos gerenciados do motor (%APPDATA%\OpenWork\runtime-opencode-config.json e %APPDATA%\openwork\opencode-v2\state\config\opencode.json).
Camada 3 — Remoção do Bloco providers Gerenciado
Quando a conta está conectada a uma organização no Cloud, o objeto providers gerenciado projeta apenas o AI Gateway da organização (ipr_<id>) e o openwork-free (o modelo fixo por trás da opção "Auto", openai/gpt-6-luna).
Camada 4 — O Plugin provider-filters com Whitelist Vazia
Mesmo que GET /api/provider no motor ainda mostre o provider embutido opencode com enabled: true, a chamada GET /api/config revela a regra injetada em info.plugins[]:
{
"package": "file:///.../state/provider-filters-plugin",
"options": {
"providers": {
"opencode": {
"whitelist": []
}
}
}
}
Como whitelist é um array vazio ([]), o plugin executa rule.whitelist !== undefined && !rule.whitelist.includes(model.id) e remove todos os modelos opencode de GET /api/model. Editar o arquivo JSON à mão não sobrevive: o app reescreve o arquivo no próximo arranque ou reload do motor. E o flag "allowZenModel": true do painel Den, embora continue chegando no /v1/me/desktop-config, simplesmente não é lido pelo desktop.
Por que os modelos "Free" do AI Gateway da organização também falham (403 FreeTierError)
Se o administrador habilitar modelos *-free dentro do AI Gateway (ipr_<id>), eles aparecem no seletor. Porém, ao chamá-los, a requisição passa pelo proxy do Gateway em vez de sair com a credencial local do cliente OpenCode. O portão do opencode.ai recusa qualquer chamada ao tier gratuito que não venha autenticada pelo próprio cliente, devolvendo 403 FreeTierError ("OpenCode's free tier can only be used from within OpenCode").
3. Por que o Motor v2 Bloqueia o Arquivamento de Sessões
A leitura do bundle do renderer (app-dist/assets/app-*.js) mostra a trava exata:
const IT = "Archiving and unarchiving are not available in the OpenCode v2 preview.";
const Rt = Xr(endpointUrl) ? IT : undefined; // Xr(e) testa se o pathname termina em "/opencode2"
E no binário do motor v2, cinco operações devolvem HTTP 501 UnsupportedInV2Preview:
session.archivesession.commandsession.revert.partsession.shellsession.summarize
Como a cópia de histórico v1 → v2 preserva o campo time_archived na tabela session_v2, qualquer conversa que estava arquivada no v1 nasce somente-leitura no v2 ("This session is archived. Restore it before sending."), e o v2 se recusa a desarquivá-la (501).
4. Passo a Passo da Reversão: App 0.18.59 + Motor v1
Enquanto os modelos gratuitos não voltarem a servir numa versão >= 0.18.60, voltar para a 0.18.59 e travar o chat no motor v1 devolve tanto o catálogo completo do opencode quanto o arquivamento.
Passo 1 — Reinstalar o OpenWork 0.18.59
Antes de procurar o instalador fora da máquina no Windows, confira a pasta de cache do updater:
Get-ChildItem "$env:LOCALAPPDATA\@openworkdesktop-updater" -Recurse
Enquanto o instalador openwork-win-x64-0.18.62.exe fica guardado na subpasta pending\, o arquivo installer.exe na raiz de %LOCALAPPDATA%\@openworkdesktop-updater\installer.exe frequentemente ainda é o da versão anterior (0.18.59):
(Get-Item "$env:LOCALAPPDATA\@openworkdesktop-updater\installer.exe").VersionInfo.FileVersion
# -> 0.18.59
Basta executar esse installer.exe para reinstalar a 0.18.59 por cima e ignorar/apagar o executável de pending\.
Passo 2 — Desligar as Atualizações Automáticas
Assim que a 0.18.59 abrir, desative as atualizações automáticas nas configurações para impedir que o app volte sozinho para a 0.18.62.
Passo 3 — Travar o Roteamento de Chat no Motor v1
Garanta que o arquivo %APPDATA%\OpenWork\engine-v2-preview.json tenha chatRouting: false e engineOrigin: "manual":
{
"enabled": true,
"chatRouting": false,
"engineOrigin": "manual"
}
(Nota: "enabled": true sozinho não troca o motor quando "chatRouting": false e "engineOrigin": "manual" estão definidos.)
Passo 4 — Conferir o Estado do Runtime
Versão do binário instalado:
(Get-Item "$env:LOCALAPPDATA\Programs\@openworkdesktop\OpenWork.exe").VersionInfo.FileVersion
# -> 0.18.59
Motor ativo em %APPDATA%\OpenWork\engine-instances.json: o campo bin da instância primary deve terminar em opencode-x86_64-pc-windows-msvc.exe (v1 1.18.30), e não em opencode2-....
Sem filtros injetados em %APPDATA%\OpenWork\runtime-opencode-config.json:
"disabled_providers": []"providers": null(ou{})- Nenhum
provider-filtersna lista"plugin".
Catálogo restaurado: GET http://127.0.0.1:<porta>/api/model (usando a porta e o authProbe de engine-instances.json) volta a devolver todos os 46 modelos opencode (45 gratuitos), e o modal Connect a provider volta a listar OpenCode (OPENCODE_API_KEY) no topo.
5. Três Armadilhas Pós-Downgrade (E Por Que Não São Falhas da Reversão)
Depois de voltar para a 0.18.59 + v1, dois avisos na interface podem dar a impressão de que algo quebrou. A leitura do código no bundle (app-*.js) e do log do motor mostra o que realmente acontece em cada um:
Armadilha 1 — Falso Positivo "OpenWork Connect: Needs attention — The selected provider/model was not found in OpenCode provider catalog."
O que aparece: um aviso persistente no menu da conta / card do Connect, mesmo com o chat e as tools do Cloud MCP funcionando normalmente. Ao recarregar o motor, o aviso some por alguns segundos (checking) e volta.
Por que acontece (rastreio no bundle): o probe de saúde do Connect (_wn) lê w.prefs.defaultModel (openwork.defaultModel no localStorage) e verifica se esse par provider/model existe em /api/model do motor ativo. Porém, não existe botão na interface da 0.18.59 para alterar w.prefs.defaultModel diretamente — escolher um modelo no seletor do composer (DO) altera apenas o modelo da conversa aberta (Fl.getState().setModel) ou, sem conversa aberta, a tabela por workspace (workspace_default_models em %APPDATA%\OpenWork\runtime.sqlite). Além disso, toda vez que o app sincroniza os providers da organização com o OpenWork Cloud, a função N9e roda:
function N9e(cloudOptions, ctx) {
const filtered = XT(cloudOptions, ctx);
if (
ctx.currentDefault &&
(isCloudProvider(ctx.currentDefault.providerID) ||
filtered.some(r => r.providerID === ctx.currentDefault?.providerID && r.modelID === ctx.currentDefault.modelID))
) return null;
const firstCloud = filtered.find(r => isCloudProvider(r.providerID));
return firstCloud ? { providerID: firstCloud.providerID, modelID: firstCloud.modelID } : null;
}
onde isCloudProvider (pd) casa com /^(lpr|ipr)_/i ou "openwork". Se openwork.defaultModel não for um provider de nuvem, N9e força a gravação do primeiro modelo do AI Gateway (ipr_.../gwm_...); e, uma vez gravado ipr_..., N9e retorna null e nunca mais o devolve para um modelo local. Como o motor v1 não carrega providers ipr_* em /api/model, o probe do Connect sempre acusa esse descompasso.
O que fazer: ignorar. É um falso positivo estrutural quando logado no Cloud com o motor v1. Se algum workspace específico abrir conversas novas num modelo morto do Gateway, basta atualizar a linha dele em workspace_default_models no %APPDATA%\OpenWork\runtime.sqlite para {"providerID":"opencode","modelID":"deepseek-v4.1-flash-free"}.
Armadilha 2 — "The free starter model is busy right now. Retrying..." É na Verdade Cota HTTP 429 (FreeUsageLimitError)
O que aparece: os modelos gratuitos param de responder de repente e mostram um spinner:
The free starter model is busy right now. Retrying... To keep working now, connect your own model provider.
Por que acontece (bundle + log do motor): isso não é o bloqueio 403 FreeTierError da 0.18.60. Inspecionando o final de %USERPROFILE%\.local\share\opencode\log\opencode.log, aparece a resposta real do servidor opencode.ai:
level=ERROR message="stream error" providerID=opencode modelID=deepseek-v4.1-flash-free
error.error="AI_APICallError: Free usage limit reached. Free models work in OpenCode (https://opencode.ai); subscribe to OpenCode Go to keep going: https://opencode.ai/go"
Quando o sidecar recebe FreeUsageLimitError (HTTP 429), ele emite um status de retry com action.reason = "free_tier_limit". No bundle do OpenWork (RetryMessage / hVe), qualquer retry com reason === "free_tier_limit" tem o título original ("Free limit reached") reescrito para "The free starter model is busy right now. Retrying…".
Como a cota funciona por dentro (medido em 25/10): o portão do free tier é por IP, não por conta — a chave nova não devolve cota (é o que explica o 403 FreeTierError de quem tenta sondar por fora). O contador é um balde diário em Redis, ip:<ip>:<AAAA-MM-DD>, e modelos com rateLimit próprio trocam a data por um balde do modelo. A cota publicada hoje é de 200 requisições por dia; em 22 e 23/10 o mesmo IP passou 903 e 939 requisições concluídas, então o número é config de servidor que muda sem changelog — a primeira recusa de hoje veio na 215ª requisição free do dia UTC. Existe ainda um segundo teto, invisível: modelos servidos por provider de teste consomem um orçamento de tokens por IP, vitalício, somando entrada, saída, raciocínio e cache. É a leitura que explica a janela de ~5 minutos: beam-free, nemotron-3-ultra-free e nemotron-3.5-lightning-free serviram juntos, cada um com ~50k tokens de contexto, e fecharam juntos depois de ~190k tokens — sem ter recebido requisição no intervalo.
O que fazer:
- Conferir as últimas linhas de
~/.local/share/opencode/log/opencode.logpara confirmar se éFree usage limit reached(429) e nãoFreeTierError(403). - Não perder tempo com chave nova: a cota é do IP. O que devolve acesso é esperar o balde diário (zera à meia-noite UTC, 21h de Brasília) ou o orçamento de tokens do trial.
- Trocar de modelo free compra minutos, não saída: os baldes por modelo abrem janelas curtas (~5 min) e fecham sozinhos, sem consumo no intervalo.
- Conectar sua própria API key do
opencode.ai(com billing ativo) em Connect a provider → OpenCode libera na hora todos os modelos pagos — e não zera o contador dos gratuitos.
Armadilha 3 — Como Contar o Consumo Diário de Cota Free Localmente
Como nem a interface do OpenWork nem o Console do OpenCode exibem um contador de cota restante (e o export JSON da aba Activity do Console trunca silenciosamente em 1.000 linhas com "truncated": true), o único contador fiel é o próprio banco SQLite do motor (%USERPROFILE%\.local\share\opencode\opencode.db no v1, tabela message): contam-se as linhas role = "assistant" de modelos *-free / big-pickle sem campo error no dia UTC. No v1, a requisição recusada também é legível ali — grava finish: null com tokens zerados, enquanto a que passou grava finish: stop. Vale saber o que esse número não mede: a contagem é de requisições, e o segundo teto (tokens do trial) não aparece nela.
6. Resumo do Trade-off (E o Gatilho para Voltar a Atualizar)
Ficar na 0.18.59 + Motor v1 com atualizações automáticas desligadas congela a versão do desktop e abre mão do que só existe no v2 (sync de providers ao vivo, troca de modelo de sessão existente via affordance, edição de skill sem reload do motor). Porém, como o app >= 0.18.60 bloqueia incondicionalmente os modelos gratuitos do opencode, essa é hoje a única combinação que mantém tanto o catálogo opencode quanto o arquivamento funcionando.
O gatilho para voltar a atualizar o app é um só: assim que uma versão >= 0.18.60 devolver o acesso ao provider opencode e aos modelos gratuitos (acompanhado nas issues #6466, #6467 e #6408).