# Guia de desenvolvimento [简体中文](./development.md) | [English](./development_en.md) | [日本語](./development_ja.md) | [한국어](./development_ko.md) | [Русский](./development_ru.md) | [Español](./development_es.md) | Português (Brasil) Este guia é para quem quer executar o AnyModelBot a partir do código-fonte, empacotá-lo ou contribuir. Se você só quer usar o app, baixe um instalador na página de [Releases](https://github.com/modelflare-ai/AnyModelBot/releases); veja o [README](../README_pt.md#-baixar-e-executar). - Um bot é uma persona, um modelo padrão e a memória dele. As contas são globais (Configurações › Canais de chat) e compartilhadas por todos os bots, com quantas contas você quiser por serviço. Feito para uso pessoal, por uma única pessoa. - Os modelos vêm de uma conta Modelflare ou das suas próprias chaves de API oficiais. [Modelflare](https://modelflare.dev/?utm_source=anymodelbot&utm_campaign=amb_readme): na primeira execução, você entra na sua conta Modelflare (ou cria uma) e os modelos são listados automaticamente. As requisições passam pelo gateway [magpie](https://github.com/yetone/magpie) incluído no app. - Três formas de executar, uma única base de código: **app de desktop (Electron)**, **linha de comando `npm start`**, **Docker**. ## Estrutura do repositório ``` src/ core/ tipos de mensagem compartilhados, fila e deduplicação de sessões, runtime (/model /reset, streaming), o aviso após um reinício, admissão (proprietário + lista de permitidos), caixa de saída persistente delivery/ entrega de mensagens proativas: horário de silêncio, limites diários, resumos, a janela de 24 horas do WeChat e lembretes de renovação agent/ modo agente: host e ponte do DSH, aprovações, predefinições de permissão, memória e gestor de contexto, diretórios de sessão do DSH prompts/ todos os prompts que o modelo do agente lê: persona, regras de chat, guia de memória, a primeira mensagem de um segmento, habilidades tools/ recursos que o agente usa: pesquisa na web (search.ts), geração de imagens / vídeos (media/), uso do computador (computer-use/), Windows PowerShell (pwsh/), Conectar apps — conectores de apps integrados e MCP personalizado (apps/), contas existentes do Notion / Feishu (accounts/) llm/ cliente compatível com OpenAI (interface de acesso aos modelos) magpie/ supervisor do magpie: diretórios isolados, porta livre, segredo gerado, verificações de saúde, reinício após falha, encerramento limpo channels/ feishu/ (conexão persistente + CardKit) weixin/ (iLink; aviso MIT em LICENSE.openclaw-weixin) messenger/ (Telegram, Slack, Discord, compartilhando um único caminho de envio e recebimento) tasks/ tarefas em segundo plano e regras: execuções, etapas, custo, retenção e limpeza, agendamentos botkit/ framework de bots do catálogo: formato do pacote, verificações, empacotamento e assinatura, catálogo, instalação, rotas estáticas da interface (veja docs/bot-framework_en.md) skills/ habilidades que os próprios usuários instalam (pacotes ZIP) store/ SQLite (node:sqlite, sem build nativo) domain/ Estruturas de dados e regras puras: configurações do bot, horário de silêncio, agendamentos, registros de bots do catálogo, etapas do agente; sem I/O, depende só de i18n. store depende só deste (a direção das dependências é verificada por test/architecture.unit.test.ts) account/ conta Modelflare: login PKCE via loopback, rotação de tokens, saldo e lembretes, chaves de orçamento por bot, capacidades dos modelos e preços de referência web/ servidor do console: server.ts inicia, autentica e despacha; routes/ reúne os endpoints por área (API JSON + chat em streaming via SSE); http.ts tem os utilitários compartilhados cli/ assistente de primeira execução init, weixin-login providers/ de onde vêm os modelos: modelflare.ts (rotas, validação de chave GET /v1/models, roteamento de modelos e atribuição), modelflare-domains.ts (teste de velocidade das rotas), official.ts (tabela de provedores de API oficiais, endereços fixos) updates/ atualizações: check.ts (verificação de nova versão), desktop.ts (download e reinício no desktop) guide.ts progresso da lista de primeiros passos i18n/ i18n do servidor: erros, textos de status, menu da bandeja, mensagens de sistema dos bots (messages/.ts) diagnostics.ts solução de problemas: sondagens, final do log, relatório de diagnóstico com dados sensíveis removidos version.ts versão do app (package.json) main.ts ponto de entrada da linha de comando electron/ processo principal do desktop (inicializador + bandeja) ui/ interface do console (Preact + htm, sem etapa de build) design/ design system: tokens/, base/, components/, personagens dos bots bots/ locales/ textos da interface: zh-CN/ e en/, um arquivo por namespace css/ lib/ components/ screens/ assets/ main.js dsh-runtime/ manifesto de dependências do runtime de agente DSH e plugins do AnyModelBot (plugins/) scripts/ setup / build-magpie / fetch-cua-driver / prepare-dsh / bundle-dsh / electron-smoke / check-readme e outros vendor/ submódulos git: magpie (compilado no build), deepseek-harness, kimi-computer-use site/ site (site estático em Astro); veja site/README.md docs/ guia de desenvolvimento, notas de privacidade, recursos do README (docs/assets/) ``` ## Executar a partir do código-fonte (linha de comando) Requer Node.js 22.13+ e Go 1.26.8+ (para compilar o magpie a partir do código-fonte). ```bash npm install npm run setup # baixa o submódulo vendor/magpie e o compila em build/bin/ npm run init # opcional: configuração inicial no terminal (entrar no Modelflare, credenciais do Feishu) npm start # inicia tudo: gateway + bots + console em http://127.0.0.1:3600/ ``` Os dados ficam em `./data` por padrão (altere com `AMB_DATA_DIR`). As demais variáveis de ambiente estão listadas em `.env.example`. ## App de desktop (Electron) ```bash npm run electron:dev # executa em modo de desenvolvimento npm run electron:smoke # smoke test (usa xvfb-run automaticamente no Linux sem interface gráfica) npm run dist # empacota para a plataforma atual (o magpie vai em resources/magpie/) npm run dist:linux | dist:mac | dist:win npm run test:dsh-bundle # teste de integração real do DSH no Electron com o build/dsh empacotado (rode prepare:dsh + bundle:dsh antes) ``` - Runtime de agente DSH: `scripts/prepare-dsh.mjs` instala `dsh-runtime/` para a plataforma de destino, e `scripts/bundle-dsh.mjs` o empacota em `resources/dsh/dsh.asar` (todo o JavaScript, em um único arquivo) mais `resources/dsh/node_modules/` (só pacotes nativos, cerca de 55 arquivos). O processo principal do Electron e os processos filhos `ELECTRON_RUN_AS_NODE` leem o asar diretamente, sem extração na primeira execução, então o instalador só descompacta algumas centenas de arquivos (no Windows, a extração e a verificação antivírus arquivo por arquivo são a principal causa de instalações lentas). Em desenvolvimento, `dsh-runtime/` continua sendo usado diretamente. - PowerShell para comandos do agente no Windows: dentro do sandbox, o Windows PowerShell 5.1 leva de 20 a 30 segundos por comando, e o PowerShell 7, cerca de 1 segundo. O app escolhe, nesta ordem: um PowerShell 7 funcional já presente no sistema (testado uma vez em segundo plano do jeito que o DSH o executa; nada é baixado) → o PowerShell 7 privado do app → o Windows PowerShell 5.1. Quando não há um 7 funcional e o modo agente está ativado, o app baixa em segundo plano o zip oficial do PowerShell 7.6.9 (LTS) para `%LOCALAPPDATA%\AnyModelBot\pwsh\`: sem direitos de administrador, sem gravações no registro, sem alterações no PATH, e o seu próprio PowerShell fica intacto. A versão e o SHA256 dos zips x64 / arm64 estão fixados em `src/tools/pwsh/private.ts` (atualize-os com `node scripts/pwsh-hashes.mjs `). Após o download, o hash é verificado; após a extração, a assinatura Authenticode da Microsoft é verificada; e ele só é usado depois de passar em um teste no estilo do DSH (versão, ler/gravar um arquivo temporário, iniciar um programa externo). Instalações que falharam ou ficaram pela metade nunca são usadas e são limpas na próxima inicialização. Durante a instalação, a tela de chat e Configurações → Geral mostram o progresso, assim como `/status`; os comandos continuam usando o 5.1 até ele ficar pronto, e os processos de agente ociosos passam a usá-lo em segundo plano, sem reiniciar o app. Se o agente não conseguir iniciá-lo em tempo de execução (removido ou posto em quarentena pelo antivírus, bloqueado por política), o app volta para a opção anterior e explica o motivo; em Configurações → Solução de problemas, é possível instalar, reinstalar ou voltar ao padrão do sistema. Nada é baixado em sistemas anteriores ao Windows 10 1607 / Server 2016. O app de desktop baixa pelo proxy do sistema (pilha de rede do Chromium); a versão de linha de comando usa o fetch do Node (versões mais novas do Node respeitam `HTTPS_PROXY` quando `NODE_USE_ENV_PROXY=1`). `AMB_PWSH_MIRROR` troca para um prefixo de espelho (o hash e a assinatura continuam sendo verificados), e `AMB_PWSH_AUTO_INSTALL=0` desativa a instalação automática. - Os comandos do agente têm acesso à rede (o sandbox só restringe gravações) e usam o seu proxy: `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY` (e suas formas em minúsculas) e as variáveis de certificados e de índices de pacotes (`SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`, `NODE_EXTRA_CA_CERTS`, `PIP_INDEX_URL`, `npm_config_registry`, `GOPROXY`, …) são repassadas do ambiente do app; quando o ambiente não define nenhum proxy, o app de desktop consulta o proxy do sistema (configurações do sistema e PAC) sempre que inicia o processo do agente e o repassa como `http://host:port` (veja `src/agent/proxy-env.ts`). O loopback (`127.0.0.1`, `localhost`, `::1`) é sempre adicionado a `NO_PROXY` e nunca passa por proxy. - Pasta de dados: `/data` (macOS `~/Library/Application Support/anymodel bot/data`, Windows `%APPDATA%\anymodel bot\data`, Linux `~/.config/anymodel bot/data`). Os logs ficam em `logs/app.log`, dentro da pasta de dados. - Instância única; por padrão, fechar a janela a esconde na bandeja do sistema (em Configurações → Geral, dá para fazer o app sair em vez disso). Menu da bandeja: Abrir / Abrir no navegador / Abrir ao fazer login (macOS, Windows) / Abrir pasta de dados / Sair. - No macOS, a janela não tem barra de título e os botões de semáforo ficam no topo da barra lateral; a largura mínima da janela é 860. - O console escuta apenas em 127.0.0.1 e gera um token de acesso aleatório a cada inicialização, para que outros programas locais não possam chamar a API de gerenciamento diretamente. - Ao sair, os canais param primeiro, depois o magpie. Se uma execução anterior foi encerrada à força e deixou um processo do magpie para trás, a próxima inicialização o limpa (Linux/macOS). Publicação de versões: escreva a entrada desta versão em `CHANGELOG.md`, `CHANGELOG_en.md`, `CHANGELOG_ja.md`, `CHANGELOG_ko.md`, `CHANGELOG_ru.md`, `CHANGELOG_es.md` e `CHANGELOG_pt.md` (mudanças que os usuários vão notar, não o site nem detalhes internos; a página do site em inglês omite as linhas que mencionam WeChat ou Feishu), atualize `version` em `package.json`, faça o commit e envie uma tag `v`. Sem essa entrada, o workflow de release falha logo no início. Com ela, a entrada vira as notas da GitHub Release e aparece na página de registro de alterações do site. A janela Novidades do app também a lê: `CHANGELOG*.md` vai nos instaladores e na imagem Docker (`src/changelog.ts`), e as notas de uma versão ainda não instalada são lidas da tag dessa versão quando o usuário as abre. `.github/workflows/release.yml` faz o build em cada um dos seis runners de SO / arquitetura e publica os instaladores mais `SHA256SUMS` em uma GitHub Release. Os secrets do repositório necessários para assinatura e notarização estão descritos no comentário no início desse arquivo. A mesma tag também dispara `.github/workflows/docker.yml`, que publica a imagem x64 + arm64 em `ghcr.io/modelflare-ai/anymodel-bot` (com as tags `` e `latest`). Em uma tag, os instaladores para macOS precisam ter assinatura Developer ID e notarização: após o empacotamento, o .app e o .app dentro do DMG são verificados com `spctl` e ferramentas afins, e a execução falha caso contrário. Os builds de release têm as ferramentas de desenvolvedor desativadas; para um pacote de depuração, use `npm run dist:debug` ou marque `debug_build` ao executar o Release manualmente (os nomes de arquivo levam `-debug-`, isso é recusado em tags, e `scripts/check-build-flavor.mjs` verifica o resultado empacotado). Toda nova versão precisa atualizar o site, a documentação relevante e os arquivos README em todos os idiomas. Rode `npm --prefix site run sync` para sincronizar o conteúdo do site. Depois de publicar a GitHub Release e a imagem Docker, siga as [instruções de implantação do site](../site/README.md) e confirme que as páginas `/download`, `/changelog` e `/bots` no ar mostram a versão, os links de download e as descrições correspondentes. A publicação só está concluída depois que o site no ar também for atualizado. As atualizações dentro do app começam na 0.1.29 e usam `electron-updater` apenas nos apps empacotados para macOS, Windows e Linux AppImage. `ui.autoDownloadUpdates` tem padrão `true` e é controlado em Configurações → Geral. Os downloads apenas disparam uma notificação; sair normalmente não instala atualizações. A instalação explícita verifica chats ativos e tarefas em segundo plano antes de reiniciar. Usuários da 0.1.28 ou anterior precisam instalar manualmente, uma vez, uma versão com o atualizador. O GitHub gera DMGs para macOS mais ZIPs de atualização e publica metadados `latest-${arch}` por arquitetura e blockmaps. As releases só ficam públicas depois que todos os arquivos são enviados. A CLI, o Docker e as execuções de desenvolvimento não empacotadas continuam sendo atualizados manualmente. ## Docker Os usuários executam a imagem publicada: `docker run -d --name amb -p 3600:3600 -v amb-data:/data ghcr.io/modelflare-ai/anymodel-bot`. Para gerá-la você mesmo a partir do código-fonte: ```bash git submodule update --init --depth 1 vendor/magpie docker build -t anymodel-bot . docker run -d --name amb -p 3600:3600 -v amb-data:/data anymodel-bot docker exec amb cat /data/ui-token # abra http://:3600/?token= ``` Dentro do contêiner, o console escuta em 0.0.0.0, então o acesso por token é obrigatório. Não exponha a porta 3600 diretamente à internet. O login do WeChat por QR code fica em Configurações › Canais de chat. ## Modelos (Modelflare) - A primeira execução mostra um único botão, “Entrar / Criar conta no Modelflare”: entre no navegador do sistema (dá para criar a conta na hora) e clique em Continue para autorizar. De volta ao app, uma chave de API exclusiva é emitida para este computador e conectada → escolha um modelo padrão → comece a conversar. O Docker e as implantações remotas não conseguem receber o retorno do navegador: depois de entrar, o navegador cai em um endereço `127.0.0.1` que não carrega, e você cola esse link de volta na tela de primeira execução (`POST /amb/api/account/login/complete`, com as mesmas verificações de state e PKCE). Em instalações antigas que informaram uma chave de API sem entrar, a chave é removida do dispositivo na atualização, e é preciso entrar. A página de retorno no navegador mostra primeiro “Concluindo o login” e só diz “Você entrou” depois que a troca de token, a leitura da conta, a chave do dispositivo e a conexão com o gateway deram certo (caso contrário, mostra o motivo); o app de desktop registra um link `anymodelbot://`, a página tenta abrir o app uma vez ao carregar, o botão “Abrir AnyModelBot” permite tentar de novo, e “Você voltou” só aparece depois que o app de fato recebeu esse link. A página para usar sua própria chave de API também permite entrar diretamente com uma conta Modelflare. Na primeira conexão, enquanto o modelo padrão global ainda está vazio, um modelo de primeiro uso é escolhido uma única vez pela regra em `src/account/first-model.ts` (suporta chamadas de ferramentas; a reserva por requisição e uma tarefa representativa cabem no crédito de teste; DeepSeek tem preferência); se nenhum se qualificar, nada é pré-selecionado e o usuário escolhe. - Método de login: código de autorização OIDC + PKCE (RFC 7636), como app nativo conforme a RFC 8252. O app é um cliente público (`client_id` é `anymodel-bot`; ele não tem nem precisa de client secret), e o resultado da autorização é entregue em uma porta temporária em `127.0.0.1`. Você pode revogar o acesso a qualquer momento na seção de apps autorizados (“Authorized apps”) da sua página de perfil do Modelflare; o app percebe e volta para a tela de login. - A credencial da conta (refresh token) é armazenada em `modelflare-account.json` na pasta de dados (modo 600) e, assim como as chaves de API do gateway integrado, é protegida apenas pelas permissões do arquivo; o chaveiro do sistema não é usado (um arquivo criptografado pelo desktop 0.1.30 ou anterior é regravado sem criptografia na primeira vez em que é lido). A interface recebe apenas o nome da conta. “Sair” revoga a autorização deste computador, e o Modelflare exclui as chaves que emitiu para ele e para os bots dele no login. - Saldo: não fica sempre na tela. Clique no avatar no canto inferior esquerdo da barra lateral para vê-lo ou abra Configurações → Modelos. Os dois mostram o saldo da carteira. “Recarregar” abre o site do Modelflare, e o saldo é atualizado quando você volta para a janela. Quando o saldo fica abaixo do limite do lembrete (1 dólar americano por padrão; ajustável ou desativável), o app avisa você uma vez e também pode pedir a um bot que mande uma mensagem para você no WeChat / Feishu. - Com login feito, Configurações → Modelos também mostra o uso por dia e por modelo, as notificações do Modelflare e o seu link de convite. - Orçamentos de bot (requer login): Configurações do bot → Orçamento. Quando ativado, o bot usa a própria chave (um provedor separado no gateway), limitada ao valor em dólares que você definir; quando desativado, volta a usar a chave compartilhada. - Esforço de raciocínio: escolha-o no painel do seletor de modelos ou em Configurações → Modelos (Automático / Baixo / Médio / Alto). É uma configuração global e vale a partir do próximo turno. Automático não envia nenhum parâmetro e usa o padrão do modelo. Só afeta modelos que o suportam: modelos de raciocínio na Responses API e modelos Chat ou Claude que o perfil do modelo marca como de raciocínio (Claude 4.6 e posteriores usam pensamento adaptativo com `output_config.effort`; modelos anteriores usam `budget_tokens`). - Geração de imagens / vídeos: no modo agente, os bots ganham quatro ferramentas — `media_models` (modelos disponíveis, padrões, faixas de parâmetros, preços de referência), `media_docs` (lê em tempo real o formato de requisição do modelo em docs.modelflare.dev), `generate_image` e `generate_video` — mais uma habilidade de mídia incluída. Os formatos de parâmetros variam por modelo, então o bot escreve o corpo da requisição a partir da documentação; imagens de referência no espaço de trabalho são escritas como `workspace:path`. A chave nunca sai do app: o app chama o Modelflare com a chave do bot, salva os resultados em `media/` no espaço de trabalho do bot e os entrega no chat. - Confirmação antes de gerar: a imagem ou o vídeo é gerado na hora. Um modelo diferente do padrão, ou um pedido vago, não é motivo para perguntar: o bot adota a interpretação mais razoável, gera e diz em uma linha o que usou. O chat pergunta qual modelo usar (com prompt, parâmetros, preço de referência e o motivo da pergunta) apenas quando o custo estimado passa de $0.5 (a partir do preço de referência, do número de imagens, da duração e da resolução do vídeo; um vídeo cujo custo não dá para estimar também conta). Você pode confirmar, trocar de modelo ou cancelar — responda com um número ou o nome do modelo no WeChat, ou clique no Feishu / no console web. Sem resposta em 10 minutos, nada é gerado. Para confirmar sempre: configurações do bot → Imagens e vídeos → Confirmar antes de gerar → “Sempre perguntar”. - Pesquisa na web: o `web_search` do agente não usa a pesquisa integrada do DeepSeek (ela exige uma chave DeepSeek); o plugin `amb-web-search` repassa cada pesquisa ao app. Por padrão, ela é grátis e não exige configuração: tenta o MCP público da Exa, o Bing e o DuckDuckGo, nessa ordem, começando pelo último que funcionou, e pula por 10 minutos o que falhar (limite de requisições, verificação anti-bot, Bing retornando resultados sem relação). Em Configurações → Rede e acesso → Pesquisa na web, dá para trocar para Tavily, Exa, Bocha ou Zhipu (com chave) ou para o seu próprio SearXNG; quando configurado, ele é tentado primeiro, e a pesquisa grátis assume se ele falhar. As chaves são armazenadas no banco de dados local, como os tokens dos canais, e nunca chegam ao processo do agente. O app de desktop pesquisa pelo proxy do sistema; a versão de linha de comando usa o fetch do Node (que respeita `HTTPS_PROXY` com `NODE_USE_ENV_PROXY=1`). - Contas de canais de chat (`src/core/routes.ts`): as contas de WeChat, Feishu, Telegram, Slack e Discord também são globais. Você as adiciona em Configurações › Canais de chat e pode dar apelidos a elas. Uma mensagem vai para o bot que essa pessoa escolheu com `/bot`; se não houver, para o bot padrão da conta; e, por fim, para o primeiro bot. `/bot` lista os bots; enviar `/bot name` uma vez troca de bot e memoriza a escolha, então o chat fica com aquele bot. O prompt de sistema de cada bot lista os outros bots e como trocar, para que ele saiba responder quando perguntado. Proprietários e listas de permitidos pertencem à conta, então qualquer pessoa permitida em uma conta pode falar com qualquer bot. A chave de uma conta é um namespace: canais anteriores a essa mudança mantêm o id antigo do bot como chave, então logins, cotas, listas de permitidos e filas não mudam de lugar, e o bot padrão continua o mesmo. Excluir um bot não afeta as contas. - Conectar apps (`src/tools/apps/service.ts`): uma única lista global (menu do avatar › Conectar apps). Por padrão, todo bot pode usar todos os apps conectados; você pode restringir isso por bot e por ferramenta (configurações do bot › Apps). Um app tem um único cartão na página Apps e pode guardar várias contas, então você pode pedir a qualquer bot “veja o meu primeiro Feishu e o meu segundo Feishu”; a conta é escolhida com o argumento `account` da ferramenta (número ou nome). - **Apps integrados** (`catalog.ts`): Feishu, Tencent Docs, DingTalk, Amap, Yuque, Notion, GitHub, Linear, Atlassian, Canva. Eles usam os mesmos métodos de autenticação dos conectores personalizados: login (OAuth com registro dinâmico, `oauth.ts`), cabeçalhos, parâmetros de consulta, URL dedicada. O Yuque é um programa local distribuído com o app (`vendor/yuque-mcp`, gerado por `scripts/vendor-yuque-mcp.mjs`); o usuário só cola um token. - **Conectores personalizados**: Streamable HTTP remoto (com fallback para o SSE legado) ou um comando local, até 20. O reconhecimento do conteúdo colado e a conversão formulário ⇄ JSON ficam em `ui/lib/connector-config.js`. Os comandos locais rodam com o PATH do shell de login (`shell-path.ts`); executores de pacotes (npx, uvx) ganham mais tempo na primeira inicialização, quando baixam o pacote. - **O processo principal faz a conexão** (`mcp-client.ts`, o `@modelcontextprotocol/client` oficial): as credenciais ficam no processo principal, em `app-secrets.json` (permissão 600, fora do banco de dados, `secrets.ts`); o plugin `amb-connections` envia cada chamada de ferramenta ao app pela ponte. As ferramentas das conexões MCP não são registradas uma a uma. Em vez disso, o modelo recebe três ferramentas fixas: `app_tools` para encontrar uma ferramenta, `app_tool_schema` para ver os parâmetros dela, `app_call` para executá-la, mais `app_connect` para pedir ao usuário que conecte um app (um cartão aparece no chat e a tarefa continua assim que ele é conectado). - **Aprovação** (`gate` em `service.ts`; a pergunta ao usuário fica em `agent/manager.ts`): ferramentas de leitura rodam na hora; gravações seguem o nível de permissão do bot (não são oferecidas a bots somente leitura, pedem confirmação primeiro no nível padrão, com a opção “sempre permitir esta ferramenta”, e rodam direto com acesso total); exclusões e ações de envio sempre pedem confirmação; regras só de observação não podem gravar. Os tipos de ferramenta vêm de `classify.ts`: os apps integrados usam a tabela integrada e as anotações oficiais, enquanto as anotações de um conector personalizado são só um valor inicial que o usuário pode alterar. - **Contas existentes do Notion e do Feishu** (`src/tools/accounts/`): contas conectadas antes da atualização continuam funcionando sem nova autorização; os tokens delas passam do banco de dados para `app-secrets.json` na primeira inicialização. O Notion (`notion.ts`) usa autorização com um clique ou um token: tokens usam a API REST; a autorização com um clique usa OAuth + PKCE + registro dinâmico com retorno para o `/oauth/notion/callback` local e depois chama o MCP remoto oficial (`mcp.notion.com`); o modelo continua vendo `notion_search`, `notion_read_page`, `notion_query_database`, `notion_create_page` e `notion_update_page`, sem exclusão. As contas existentes do Feishu (`feishu.ts`) leem mensagens como o usuário (`feishu_list_chats`, `feishu_read_chat`, `feishu_search_messages`); as contas do Feishu conectadas a partir de agora usam o MCP remoto oficial (leitura/gravação de documentos na nuvem mais mensagens somente leitura), e uma conta existente passa para ele quando é autorizada de novo. - Modelos padrão de imagem / vídeo: escolhidos junto com o modelo de texto padrão durante a configuração inicial e alteráveis depois em Configurações → Modelos. “Automático” usa o primeiro modelo, na ordem recomendada, que não precisa de material de referência. - O seletor de modelos mostra o tamanho do contexto, as capacidades de visão / ferramentas / raciocínio e os preços de referência iniciais por milhão de tokens de entrada / saída (do catálogo público de preços; o que vale é a sua fatura). - Custo-benefício (requer login): as chaves que o app emite (a chave deste computador e a chave de orçamento de cada bot) levam a estratégia de roteamento Smart Key do Modelflare, `routing_strategy`: Econômico `low_cost` (primeiro o grupo de menor multiplicador), Equilibrado `balanced` (o padrão; começa pelo grupo de preço intermediário) ou Qualidade `stable` (primeiro o maior multiplicador). Altere em Configurações → Modelos → Custo-benefício ou no primeiro controle deslizante do seletor de modelos; as chaves existentes são atualizadas junto (`PATCH /api/connected-app/api-keys/`), e a escolha fica armazenada no banco de dados local (`account.routing`). Usuários com login feito que nunca escolheram recebem Equilibrado nas chaves existentes uma única vez, na primeira verificação de chave (registrado no log como `applied the default key routing strategy`). O suporte só conta quando o Modelflare devolve `routing_strategy` na resposta; se ele rejeitar o campo (400 / 422), a chave é emitida de novo sem ele, então a emissão, as verificações de chave e os orçamentos nunca falham por causa disso. Se ele ignorar o campo (sem devolvê-lo), isso também conta como não suportado. Em ambos os casos, a configuração fica oculta e é tentada de novo um dia depois. - Inteligência (o segundo controle deslizante do seletor de modelos): entre os modelos que esta conta pode usar e que têm dados da Artificial Analysis, ele pega a fronteira de Pareto entre o índice de inteligência (quanto maior, melhor) e o custo por tarefa (quanto menor, melhor), com até 5 posições, sempre incluindo o mais barato e o mais forte. A inteligência é considerada para o esforço que o app realmente usa: modelos de raciocínio na Responses API usam médio por padrão, então é usada a linha medium da AA; todos os outros usam a linha padrão da AA (`src/account/model-quality.ts`). Custo = custo por tarefa da AA (preços de tabela oficiais) × o fator de preço do modelo na configuração atual de Custo-benefício (o multiplicador dos seus grupos públicos utilizáveis × o seu multiplicador de venda: o menor para Econômico, o maior para Qualidade e o do meio para Equilibrado; grupos só para empresas ficam de fora; sem login ou sem suporte, assume-se Equilibrado). Mover o controle deslizante troca para o modelo daquela posição; o nome do modelo abre a lista completa para escolha manual, e um modelo que não é uma das posições aparece como Personalizado. Com menos de duas posições, o seletor é apenas a lista. Os cálculos estão em `ui/lib/frontier.js`. - Os dados da AA vêm no repositório como `assets/model-quality.json` (`updatedAt`, `source`, apenas os campos que usamos, já mapeados para os IDs de modelo do Modelflare). Atualize com `node scripts/update-model-quality.mjs`, que busca o ranking público https://artificialanalysis.ai/leaderboards/models e o catálogo público de preços do Modelflare; com `AA_API_KEY` definida, ele usa a API oficial em vez disso (os campos dela ainda não foram verificados com uma chave). `--html ` e `--pricing ` usam cópias salvas, `--dry-run` só imprime. Se a página mudar de formato (uma coluna faltando, linhas com formato errado, números que não podem ser interpretados, poucas linhas ou poucos modelos correspondidos), ele sai com código 1, indicando o que estava errado, e não mexe no snapshot existente. Modelos cujos nomes não são mapeados pelas regras vão em `OVERRIDES` no script. Os dados são da Artificial Analysis, com crédito no rodapé do seletor. - Atribuição: as requisições pelo Modelflare levam `X-Modelflare-Skill: anymodel-bot/`, e os links levam `utm_source=anymodelbot&utm_campaign=amb_` (`amb_signup`, `amb_topup`, `amb_console` e assim por diante dentro do app, `amb_readme` para o README e a documentação do repositório, `amb_homepage` para o site; `test/utm.unit.test.ts` fixa o formato). - Domínios de API: escolha `https://modelflare.dev/v1`, `https://origin.modelflare.dev/v1` ou `https://cf.modelflare.dev/v1` em Configurações → Modelos → Domínio da API. Os testes manuais rodam a partir do dispositivo em que o AnyModelBot está, fazem requisições a `/v1/models` nos três domínios sem credenciais e informam o tempo de resposta mediano e quantas das três sondagens tiveram sucesso. HTTP 401 conta como resposta da API, não como prova de que a chave é válida nem da velocidade de geração do modelo. Não há troca automática. Os valores de configuração legados `auto` / `global` ainda selecionam o domínio principal, `cn` seleciona origin e `cf` seleciona cf; esses valores de compatibilidade não indicam região nem qualidade. - Configurações → Modelos: conta, saldo e recarga, lembrete de saldo, uso, notificações, convites; custo-benefício (estratégia de roteamento) e esforço de raciocínio; modelo padrão, atualizar a lista de modelos. - O botão de modelo à direita do campo de mensagem troca o modelo da sessão atual (ou o define como padrão do bot); `/model`, `/reset`, `/new`, `/status`, `/stop` e `/help` funcionam. - Atividade: cada etapa de um turno do agente (requisições ao modelo com tempo e tokens, chamadas de ferramentas com argumentos e saída, novas tentativas, subagentes) fica recolhida na linha de “tempo” acima da resposta; clique para expandir. Para sessões do WeChat / Feishu, veja o painel de informações → Conversas recentes, onde o turno em andamento aparece em tempo real e pode ser interrompido. - Mensagens travadas: `/stop` (em qualquer canal) ou “Parar” no painel de informações encerra o turno atual imediatamente. Um turno sem progresso por 10 minutos (nenhuma resposta do modelo, nenhuma atividade de ferramenta, apenas novas tentativas; esperar a sua aprovação ou resposta não conta) é interrompido automaticamente, e um único turno tem limite de 30 minutos (`AMB_AGENT_IDLE_TIMEOUT_MS`, `AMB_AGENT_TURN_TIMEOUT_MS`). A interrupção para apenas o turno daquela conversa (o plugin amb-control dentro do processo do agente o cancela); as outras conversas do bot continuam rodando. O processo do agente só é reiniciado se o turno não tiver parado em 10 segundos (`AMB_STOP_GRACE_MS`). `/status` mostra até que etapa ele chegou e quanto tempo se passou desde o último progresso. No chat web, trocar para outro bot ou recarregar a janela não interrompe uma mensagem em andamento; quando você volta, ela continua de onde estava (a sua mensagem, a resposta até ali e a atividade). O botão de parar também funciona enquanto a mensagem está na fila, enquanto o agente está iniciando e enquanto espera aprovação, e também no chat simples. - Direcionamento: uma mensagem (texto, imagens, arquivos) enviada enquanto o agente está trabalhando entra no turno atual; o modelo a vê na próxima etapa e uma única resposta cobre as duas (primeiro você recebe um breve “recebido”, enviado pela caixa de saída e descartado se não puder sair em 10 minutos). As mensagens de direcionamento passam pela mesma verificação de admissão que qualquer outra: uma mensagem de alguém que não está na lista de permitidos nunca entra em um turno. Antes de o agente pegar a mensagem anterior, ou enquanto o turno está terminando, ela vai para a fila como antes, assim como os comandos (`/model`, `/reset`…). No chat web, você pode continuar digitando enquanto uma resposta está em andamento. - Comandos longos vão para segundo plano: um comando que ainda está rodando depois de 30 segundos (60 para o PowerShell no Windows) vira um job em segundo plano em vez de bloquear o turno, e o agente responde que ele ainda está rodando. Quando ele termina, o agente é acordado com o resultado e a resposta chega como uma nova mensagem na mesma conversa (em tempo real no chat web; no WeChat e no Feishu, ela passa pela caixa de saída como qualquer resposta: o id é derivado da sessão e do turno do agente, então ela é enviada uma única vez, a janela de 24 horas e a cota do WeChat são verificadas no momento do envio, e no Feishu ela responde à sua mensagem mais recente, cujo id fica guardado no banco de dados). A atividade mostra as etapas do comando em segundo plano, e `/status`, a interrupção e o watchdog tratam esse turno como qualquer outro. - Cada conversa usa o próprio modelo: dentro de um mesmo processo do agente, cada conversa roda no próprio modelo e na própria API (um provedor por API, janela de contexto e saída máxima por modelo). Trocar de modelo não reinicia o processo do agente; um modelo que o patch de inicialização não listou é adicionado em tempo de execução. - Substituição do processo do agente: o SDK do DSH não consegue fechar uma sessão, então a sessão de cada tarefa em segundo plano e de cada execução de regra fica no processo do agente até ele reiniciar (medido: cerca de 0,25–0,45 MB por sessão concluída e ociosa). Quando um processo já abriu mais de 100 delas, ele é substituído em segundo plano assim que fica totalmente ocioso: nenhum turno de chat, nenhuma tarefa ou execução de regra rodando ou finalizando, nenhuma aprovação pendente, nenhuma compactação, nenhum job em segundo plano no processo. As sessões seguem como antes, nada é interrompido e o usuário não percebe nada. O status do agente em Solução de problemas mostra `taskSessions` e `recycles`; `AMB_DSH_RECYCLE_SESSIONS` altera o limite. - Aviso após um reinício: uma mensagem que ainda estava sendo processada quando o app fechou ou travou (cada conversa mantém no banco de dados uma marcação de “em andamento”, removida quando a mensagem termina) é informada uma vez na própria conversa na próxima inicialização — “上次的消息「…」因为应用重启没有处理完,请重新发送” (a última mensagem não foi concluída porque o app reiniciou; envie-a de novo). Nada é reenviado automaticamente. O chat web recebe o aviso no histórico; o WeChat usa o context_token armazenado e a mesma cota de respostas, e, quando a janela de 24 horas ou a cota já acabou, o aviso é dado antes da resposta à sua próxima mensagem nessa conversa; o Feishu o envia para o chat original. - API do agente: o agente escolhe a API conforme o que o catálogo público do Modelflare diz que o modelo suporta — modelos com a Responses API (GPT-5.4 e posteriores, Grok 4.5 e posteriores, DeepSeek V4, GLM-5, Kimi K3 e outros) usam `/v1/responses` (modelos de raciocínio usam esforço médio por padrão; o raciocínio criptografado é reenviado com o histórico); modelos sem Responses, mas com Anthropic Messages (Claude), usam `/v1/messages`; todos os outros modelos, o caso em que o catálogo está indisponível (por exemplo, offline) e endpoints externos usam Chat Completions. A API é escolhida por modelo na primeira vez que o processo do agente o usa (o status do agente em Solução de problemas mostra o `api` do último turno e os modelos no processo); `AMB_AGENT_API=chat` força Chat Completions em tudo (para solução de problemas). O chat simples (sem agente) continua usando Chat Completions. Rede de segurança: quando o upstream rejeita a requisição Responses / Messages de um modelo (HTTP 400/404/405/415/422 ou “unsupported parameter / endpoint”; limites de requisições, saldo, permissões, indisponibilidade do upstream e estouro de contexto não contam), esse modelo passa para Chat Completions por 24 horas (registrado em `dsh/api-fallback.json` na pasta de dados, com um aviso no log, e o status do agente mostra `openai-completions (fallback: reason)`). Se o turno ainda não chamou nenhuma ferramenta, aquela conversa o refaz no Chat automaticamente (sem reiniciar o processo do agente) e o usuário ainda recebe uma resposta; se já chamou ferramentas, o erro é informado e o próximo turno usa o Chat. - Gateway integrado: o magpie roda no modo `--gateway`, com HOME / diretório de configuração próprios dentro da pasta de dados do app. Ele **não lê** as credenciais de assinatura do Claude / Codex que já existam no seu computador, **não reescreve** nenhuma configuração de agente e tem a atualização automática e a descoberta de contas desativadas. Ele só é chamado de dentro do processo do app; o console não faz mais proxy das páginas dele nem de `/v1`. - A chave de API é armazenada apenas na configuração isolada do gateway — nunca em linha de comando, nunca em logs — e o front-end só recebe os últimos caracteres dela. - Desenvolvimento / testes: `AMB_MAGPIE_BASE_URL` aponta o app para qualquer endpoint compatível com OpenAI (o gateway não é iniciado, a interface se considera conectada e os recursos de conta ficam indisponíveis); `AMB_MODELFLARE_BASE_URL` aponta a URL base do Modelflare para um mock local, e `AMB_MODELFLARE_ISSUER` aponta o login e as APIs de conta para um mock local. ## Chaves de API oficiais - As chaves podem ser informadas em “Usar sua própria chave de API”, na tela de boas-vindas, e em Configurações → Modelos. Os provedores suportados e os endereços deles são fixos em `src/providers/official.ts`, e a API (`GET /amb/api/official`, `PUT`/`DELETE /amb/api/official/`) só aceita ids dessa tabela, nunca um endereço. - Cada uma é um provedor no gateway integrado com o id `official-`, e os ids de modelo são `official-/`, o que os mantém separados do modelo de mesmo nome do Modelflare. As chaves nunca passam por argumentos de linha de comando: elas chegam à configuração isolada no corpo de uma requisição à API local do gateway. - A verificação usa a ação de listar modelos do gateway (os proxies e o estilo de autenticação de cada provedor ficam no gateway), e uma chave só é salva se passar. Os modelos são comparados com a lista do provedor a cada hora, e os que não servem para chat (fala, imagem, embedding…) ficam fora do seletor. - A API do agente depende do provedor: Responses para a OpenAI, Messages para a Anthropic e Chat Completions para os demais. Tamanhos de contexto, preços e suporte a raciocínio vêm do catálogo de modelos do gateway (`/v1/models`). Com apenas chaves oficiais, o catálogo e a conta do Modelflare não são lidos. - Modelos em APIs oficiais não levam `X-Modelflare-Skill`, não usam a compactação no servidor (o endpoint dela vem do catálogo do Modelflare) e não são limitados pelos orçamentos de bot (um orçamento é o limite de gasto de uma chave Modelflare). A geração de imagens e vídeos só existe enquanto o Modelflare está conectado. - Testes: `AMB_OFFICIAL_BASE_URL` aponta todos os provedores para um mock local (`test/official-providers.integration.test.ts`). ## Interface - Conversas em grupo (`src/core/groups.ts`, `ui/screens/group.js`): com dois ou mais bots, o menu + permite criar um grupo. Cada membro tem a própria conversa no grupo, `:web:group:`, então modelo, permissões, memória, aprovações e custos seguem o mesmo caminho das conversas individuais. O que o usuário diz fica armazenado na conversa `group:`, e a linha do tempo mescla essas mensagens por id. Quem for @mencionado responde (`@everyone` significa todos os membros); sem @, o bot que respondeu por último continua. Um bot que @menciona outro membro na resposta passa a vez, até 6 vezes por mensagem do usuário. Só um bot responde por vez em um grupo. - Barra lateral: lista de bots (avatar + nome + mensagem mais recente), + Novo, clique com o botão direito para renomear / excluir; a lista é dividida em seções que você pode criar, renomear, excluir e arrastar para reordenar (Meus e Novos por padrão, salvas no servidor e compartilhadas por todas as janelas), bots, conversas em grupo e bots do catálogo podem ser arrastados para outra seção ou movidos com “Mover para a seção”, e os bots do catálogo recém-listados caem em Novos; no canto inferior esquerdo, o avatar mostra o seu saldo e a engrenagem abre as Configurações (`⌘/Ctrl+,`). - Tela de chat: o botão flutuante com o nome do bot, no topo, abre o painel de informações (`⌘/Ctrl+⇧+I`: contas de canal que este bot atende por padrão, aprovações pendentes, tarefas e regras, memória, sessões recentes; ⚙ abre as configurações do bot). As respostas chegam em streaming (Markdown, cópia com um clique para blocos de código) e podem ser interrompidas a qualquer momento (`Esc`). Aprovações e perguntas aparecem como cartões no chat. Se você rolar para cima, a tela nunca é puxada de volta para o final. - Paleta de comandos `⌘/Ctrl+K`; a aparência (sistema / claro / escuro) fica em Configurações → Geral. - Idioma: chinês simplificado / inglês / japonês / coreano / russo / espanhol / português (Brasil), em Configurações → Geral → Idioma (também no canto superior direito da tela de configuração inicial e na paleta de comandos). Por padrão, segue o sistema (inglês quando o idioma do sistema não é suportado). A preferência fica em `ui.lang` no config.json; o menu da bandeja, as mensagens de erro e de status do servidor e as mensagens de sistema que os bots enviam ao WeChat / Feishu (`/help`, pedidos de aprovação, …) seguem essa preferência. Na linha de comando / Docker, “Sistema” significa `AMB_LANG`, depois `LC_ALL` / `LC_MESSAGES` / `LANG`, com chinês simplificado como alternativa final. Logs, prompts enviados ao modelo e nomes de bots escolhidos pelos usuários não são traduzidos. - Os textos da interface ficam em `ui/locales//.js` e são usados como `t("namespace.key", { params })` (`ui/lib/i18n.js`); os textos do servidor ficam em `src/i18n/messages/.ts`, com chaves verificadas por tipo. Toda requisição da interface leva `x-amb-lang`, e o servidor responde a essa requisição no mesmo idioma. - Adicione todo texto novo nos dois idiomas. `node scripts/i18n-check.mjs [files…]` aponta chinês deixado no código da interface, chaves que não existem e dicionários zh / en fora de sincronia (`test/i18n.unit.test.ts`, no `npm test`, verifica o mesmo). Chinês que precisa ficar no código (nomes de fontes, regexes que reconhecem entrada em chinês) recebe um `// i18n-ignore` na mesma linha. - Lista de primeiros passos: o estado vazio do chat mostra uma lista de seis passos — dizer olá, trocar de modelo, conectar o WeChat, pedir que ele faça algo, deixar que ele se lembre de você, gerar uma imagem. O último passo apenas reproduz uma demonstração integrada dentro do guia, tenha a conta modelos de imagem / vídeo ou não: como o fluxo padrão real, ela gera na hora e mostra a imagem no chat, mas nada é enviado ao bot, nenhum modelo é chamado e nada é cobrado; a imagem é o `ui/assets/guide-demo.svg` incluído no app. A ideia é ensinar o fluxo sem gastar o dinheiro do usuário; pedir de verdade um desenho, ou receber uma imagem mais tarde, também conclui o passo. “Enviar” testa diretamente; “Mostrar” ou a entrada da lista no canto inferior esquerdo da barra lateral mostra um cartão de guia apontando para o controle certo (destacado com um anel). Os passos são marcados sozinhos: uma resposta no chat, uma troca de modelo, um arquivo / imagem recebido, um canal conectado, uma gravação na memória. O progresso fica em `ui.guide` no config.json (API `/amb/api/guide`), e passos que usuários existentes já fizeram contam como concluídos. A lista pode ser recolhida no canto inferior esquerdo ou ocultada de vez, e Configurações → Geral → Lista de primeiros passos a reinicia. Dizer olá exige uma resposta real (avisos de erro não contam), e um canal precisa estar de fato conectado (só a configuração não conta); o passo “Conecte o app de chat que você usa” oferece WeChat, Feishu, Telegram, Slack e Discord, e o idioma da interface só decide qual deles é sugerido. Quando o usuário diz explicitamente “Lembre-se…”, a revisão de memória pós-turno roda na hora, em vez de depois de 90 segundos. O progresso é armazenado por usuário (por conta quando há login no Modelflare, no KV `guide.owner.*`; caso contrário, ainda em `ui.guide` no config.json); concluir tudo é um marco depois do qual a lista se oculta de vez, e os cartões de guia que aparecem sozinhos esperam enquanto o campo de mensagem tem um rascunho ou uma tarefa está rodando. - Visual: os tons neutros e os componentes foram adaptados da interface do magpie (MIT); a cor de destaque é o amarelo-esverdeado fluorescente do Modelflare, `#d7ff00`. O design system está descrito em `ui/design/README.md`. ### Localização e verificações de release Os mantenedores escrevem e adaptam cada tradução diretamente; não gere textos por APIs de tradução. Preserve nomes de produtos, IDs de modelo, comandos, URLs e parâmetros `{placeholder}`. - Mensagens do app: `ui/locales//.js`; novas localidades do servidor: `src/i18n/locales//.ts`. - Mensagens do site: `site/src/i18n/locales//`. Por exemplo, `home/hero.ts` corresponde a `home.hero.*`. As páginas leem do catálogo. - `ui/locales/manifest.json` lista os idiomas de destino e o status `ready` de cada um. O site mantém um `site/src/i18n/manifest.json` idêntico; a validação confere se os dois coincidem. Idiomas incompletos ficam como `ready: false` e fora dos menus públicos e das anotações de idioma para mecanismos de busca. - `npm run i18n:status` informa o progresso por idioma e por superfície. `npm run i18n:check` valida todos os idiomas de destino e os documentos de apoio. Chaves ausentes, vazias ou sobrando, parâmetros danificados e formas de plural ausentes bloqueiam a release. O fallback para inglês durante o desenvolvimento não conta como tradução. - O russo usa os plurais `one / few / many / other`; espanhol e português usam `one / many / other` (`many` só cobre milhões redondos e é escrito como `other`); japonês e coreano usam `other`. Localize a apresentação sem mudar a moeda, o fuso horário nem os valores. - O `npm run build` do site verifica todas as localidades de destino, os documentos sincronizados e as imagens de compartilhamento. `build:draft` serve para prévias locais e não é uma verificação de release. ## Persona e prompts Todos os prompts que o modelo do agente lê ficam em `src/agent/prompts/`. `test/prompts.unit.test.ts` verifica as frases obrigatórias e proibidas de cada parte e um limite de caracteres; `test/agent.integration.test.ts` verifica, com um processo filho real do DSH, o prompt de sistema que o modelo recebe e um limite para o prompt de sistema inteiro mais as definições de ferramentas. - **Persona**: a persona própria do DSH é um “coding agent”, precedida por “You are an AI agent powered by DeepSeek Harness.”. O patch (`host.ts` writePatch) substitui `personaPrefix` por uma persona de assistente pessoal (conversa e também escreve código, executa comandos, lida com arquivos e opera apps de desktop quando o uso do computador está ativado; o texto de páginas web, arquivos e mensagens encaminhadas é dado, não instrução) e desativa `includeHarnessIdentity` (que controla apenas essa frase). - **Seção do bot**: as regras de chat, como usar a memória de longo prazo (quando a memória está ativada), o nome do bot e a persona que você escreveu formam a última seção do prompt de sistema (`amb:bot`). Antes de cada turno, o host a grava em `dsh/.prompts/.md` na pasta de dados (as ferramentas de arquivo do agente não conseguem lê-la nem alterá-la; uma seção sem mudanças não é regravada), e o plugin `dsh-runtime/plugins/amb-prompt.mjs` a lê a cada requisição ao modelo — uma persona ou um nome editado vale a partir da próxima mensagem, sem `/new` e sem reiniciar o processo do agente. O DSH 0.2.1-rc.4 chama o `text` de uma seção de forma síncrona (uma Promise retornada não é aguardada), então o plugin lê um arquivo em vez de chamar a ponte. Quando a persona e as regras de chat divergem, vale a persona. Os subagentes não recebem essa seção: eles não falam com o usuário. - **Regras de chat** (em chinês, adaptadas do princípio de atenção do dsh-superagent): responder primeiro; para uma tarefa que leva algum tempo, dizer em uma linha o que vai fazer e depois não narrar os passos (eles estão no painel de atividade); ao terminar, dizer o que foi feito e onde está o resultado; o que ficou inacabado é informado como inacabado, e uma falha, como falha; decidir sozinho e dizer o que escolheu, nunca perguntando sobre idioma, formato, escopo ou estilo; só perguntar quando um palpite errado causaria dano, custaria dinheiro ou desperdiçaria a maior parte do trabalho, quando precisa de algo que só você tem (um login, um arquivo, uma senha) ou quando o passo não pode ser desfeito (pagar, excluir os seus dados, mandar mensagem para outras pessoas), uma pergunta concreta por vez, com `ask_user_question` para escolhas; sem enrolação; respostas curtas no WeChat e no Feishu. As tarefas em segundo plano seguem a mesma regra e dizem no resultado o que escolheram. - **A primeira mensagem de um segmento**: o canal (o WeChat mostra apenas texto simples; o Feishu e a web suportam Markdown), o snapshot da memória de longo prazo naquele momento, a nota de transição do segmento anterior e, por fim, a sua mensagem. Isso varia por conversa (memória do grupo, canal) e muda a cada fato memorizado; no prompt de sistema, invalidaria o cache de prefixo que todas as conversas de um bot compartilham, então fica aqui, sem mudanças dentro de um segmento, e as mudanças posteriores na memória são lidas com `recall`. - Cache de prefixo: uma seção sem mudanças mantém o prompt de sistema idêntico byte a byte; uma persona editada muda apenas a última seção. Conversas iniciadas antes da atualização ainda levam a persona antiga na primeira mensagem até o próximo segmento (`/new` ou uma troca feita pelo gestor de contexto). ## Permissões de arquivo No modo agente, cada bot tem uma predefinição de permissão de arquivos. Troque-a pelo botão de permissão no canto inferior esquerdo do campo de mensagem ou em Configurações do bot → Permissões; `/status` mostra a predefinição atual: - **Gravação no espaço de trabalho** (padrão): os comandos rodam em um sandbox e só podem alterar o espaço de trabalho deste bot; qualquer coisa fora dele pede a sua confirmação no chat primeiro. - **Somente leitura**: nenhuma alteração em arquivos; os comandos rodam em um sandbox somente leitura. - **Acesso total**: para usuários que entendem o risco e acham as aprovações frequentes demais. Os comandos rodam **sem sandbox**, diretamente como o seu usuário, e ações fora dos limites não pedem mais confirmação — ele pode ler e alterar qualquer arquivo do seu computador, inclusive a própria pasta de dados do AnyModelBot (banco de dados, tokens dos canais, chaves). Só as **exclusões recursivas** ainda pedem confirmação primeiro (mesmo fluxo de aprovação: responda com um número no WeChat, cartões no Feishu / web): `rm -r` / `-rf` / `--recursive` (inclusive dentro de `sudo`, `xargs`, `sh -c`), `find -delete`, `git clean`, `rsync --delete`, PowerShell `Remove-Item` / `rm` / `del` / `rd` / `rmdir` com `-Recurse` (e abreviações), cmd `rd /s`, `del /s`, `robocopy /MIR`, além de formas comuns como `shutil.rmtree` do Python e `fs.rm(…, { recursive })` do Node; arquivos de script citados no comando também são verificados. Ao escolher esse modo, uma confirmação aparece primeiro, e o botão de permissão e a página de configurações o marcam em vermelho. - Isto é uma proteção contra exclusão acidental, não uma barreira de segurança: ela inspeciona o texto do comando, então comandos montados em tempo de execução (`eval "$(…)"`, `curl … | sh`) e scripts de build como `make clean` ou `npm run clean` passam despercebidos; na dúvida, ela pergunta. Só ative esse modo quando confiar no modelo e na tarefa atuais. ## Quem pode usar um bot O agente roda no seu próprio computador (possivelmente com acesso total), então, por padrão, **só o proprietário e as pessoas que você permitir** podem falar com um bot. Isso é contado por conta de canal, e qualquer pessoa permitida em uma conta pode falar com qualquer bot (`src/core/access.ts`): - **Proprietário**: no WeChat, a conta que escaneou o QR code. O Feishu não tem um login próprio, seja escaneando para criar o app, seja informando um App ID e um App Secret, então **a primeira pessoa que manda uma mensagem individual para o bot depois que ele é conectado** se torna o proprietário (confiança no primeiro uso): assim que ele estiver conectado, mande uma linha para o bot do seu próprio Feishu, e essa mensagem é respondida normalmente. Um login do WeChat que não informou o id de quem escaneou funciona do mesmo jeito. Configurações do bot → Quem pode usar mostra o proprietário no Feishu; “Alterar” (após uma confirmação) limpa o proprietário, para que a próxima pessoa a mandar mensagem se torne o proprietário. Trocar para outro app do Feishu ou desconectar o Feishu também o limpa. Pessoas que tiveram o acesso negado nunca se tornam o proprietário. O chat web local nunca tem restrição. - **Qualquer outra pessoa** recebe uma única linha, “você ainda não tem permissão para usar este bot; o proprietário foi avisado” (no máximo uma vez por hora por pessoa); a mensagem não entra na fila, nenhum modelo é chamado e nada é gasto. O pedido aparece em Gerenciar, na conta correspondente em Configurações › Canais de chat (permitir / negar), e é enviado ao proprietário com um código de pareamento de 4 dígitos (no máximo 3 por conta por hora, e só se o proprietário falou nesse canal nas últimas 24 horas); o proprietário responde `同意 1234` / `拒绝 1234` (ou `yes 1234` / `no 1234`) no chat. Os códigos de pareamento nunca coincidem com os códigos de aprovação de ferramentas. Pessoas que tiveram o acesso negado não recebem resposta nenhuma. - A lista de permitidos fica em Gerenciar, na conta correspondente em Configurações › Canais de chat; é possível remover entradas (as pessoas podem pedir de novo mandando uma mensagem). `/status` também a mostra. - Na atualização, todos que já tinham uma conversa direta com um bot são colocados na lista de permitidos dele (marcados como “Já usava antes da atualização”), para que ninguém fique de fora; escanear de novo com outra conta do WeChat mantém o proprietário anterior na lista. - Conversas em grupo: nenhum canal as entrega hoje (o Feishu só lida com mensagens diretas, e o WeChat iLink não tem grupos), então as mensagens de grupo ficam como estavam; as regras vão para `access.ts` quando um canal passar a ter grupos. ## Respostas não se perdem As respostas finais, os arquivos entregues, os pedidos de aprovação e os avisos são gravados em uma caixa de saída no banco de dados local antes de serem enviados (`src/core/outbox.ts`; as atualizações de cartão em streaming e o indicador de digitação não são): - As mensagens para uma mesma conversa saem uma de cada vez, em ordem. Erros de rede ou da plataforma geram novas tentativas (a partir de 5 s, espaçando até 5 min); um canal offline é aguardado. - Depois que o app fecha ou trava, ele continua na próxima inicialização, e o que já foi enviado não é enviado de novo (uma resposta longa recomeça a partir da primeira parte não enviada). Os ids são derivados da mensagem recebida, então uma mensagem que a plataforma entrega duas vezes ainda recebe uma única resposta final; só a parte que estava em trânsito no momento de uma falha pode sair duas vezes, e ela leva um `uuid` fixo do Feishu (o Feishu descarta duplicatas por uma hora) e um `client_id` fixo do WeChat. - O WeChat só permite que um bot responda até 24 horas depois da última mensagem da pessoa, e no máximo 10 vezes por mensagem. Uma mensagem que não pode sair por esse motivo fica retida, não é descartada: na próxima vez que a pessoa escrever, as mensagens retidas saem primeiro, antes da resposta, na ordem original e uma única vez, com as de texto reunidas em uma única mensagem “你不在时:…” (“Enquanto você estava fora: …”) quando cabem em uma mensagem do WeChat (os arquivos vão como estão). As mensagens retidas são guardadas por até 7 dias; as de vida curta, como o aviso de “recebido” e os pedidos de aprovação, mantêm o próprio prazo. - As mensagens que não podem mais ser enviadas (rejeições repetidas da plataforma, espera longa demais) são marcadas com o motivo no log e no diagnóstico. ## Tarefas em segundo plano e regras O trabalho que leva algum tempo roda em segundo plano e nunca segura o chat (design: `docs/design/background-tasks-and-proactive.md`; código: `src/tasks/`): - **Tarefas em segundo plano**: o agente do chat inicia uma com `task_start` (sessão do DSH própria, `task-`, e uma pasta `tasks/-/` no espaço de trabalho) e diz em uma linha o que vai fazer; o resultado volta para a conversa que pediu (cite-o no WeChat ou responda a ele no Feishu para dar continuidade). Uma tarefa que precisa de você faz uma pergunta por vez em cada conversa. Depois de um reinício do app, ela continua e verifica antes de repetir ações externas. `/tasks` lista as tarefas e as regras, `/stop ` para uma delas. - **Regras**: criadas com `rule_create` ou em configurações do bot › Mensagens proativas › Regras, elas agem na hora: as suas próprias palavras são o mandato, então cada execução verifica e só manda uma linha quando algo importa, e editar o texto mantém a regra agindo. O modo de observação é opcional (diga ao bot “observe primeiro” ou escolha Observar no editor de regras): somente leitura, não envia nada, registra “enviaria” e, depois de 3 execuções observadas, Precisa de você pergunta se deve passá-la para o modo de ação. Cada execução ganha uma sessão nova, e o log de execuções pode ser exportado como Markdown. - **Visível, sem fazer barulho**: o grupo Tarefas do painel de informações lista as tarefas e as regras do bot (as tarefas encerradas ficam recolhidas em uma linha “encerradas”); abra uma para ver de onde ela veio, o progresso mais recente, o orçamento, a pasta e as etapas de cada execução. Tudo o que espera por você está em Precisa de você, na barra lateral. Só resultados, decisões e falhas geram notificação; o horário de silêncio (22:00–08:00 por padrão), os limites diários (3 no WeChat, 5 no Feishu) e os resumos ficam em configurações do bot › Mensagens proativas. - **Orçamentos**: $1 por tarefa, $0.10 por execução de regra e $3 por regra por mês, por padrão (estimados a partir dos preços de referência do Modelflare). Ao atingir o limite, o trabalho para e pergunta; altere o orçamento de uma tarefa nos detalhes dela e o de uma regra no editor de regras. Quando a chave de orçamento do bot fica abaixo de 20%, o trabalho em segundo plano para, e o restante fica para o chat. - **Pausa**: configurações do bot › Mensagens proativas › Pausar o trabalho em segundo plano (ou peça ao bot) suspende tarefas, regras e mensagens enviadas por iniciativa do bot; o chat continua. - **Retenção**: tarefas e logs de execução são mantidos por 30 dias; uma tarefa sem uso por 30 dias é arquivada. Uma pasta de tarefa sem uso por 30 dias é excluída junto com os arquivos de sessão do DSH da tarefa (em `dsh//sessions/`; o SDK não tem chamada para excluir uma sessão, então o próprio app remove os arquivos, e uma sessão ainda aberta no processo do agente é removida depois que o processo é substituído). Você não é consultado nem recebe notificação: aos 27 dias sem uso, a data de exclusão é definida e mostrada nos detalhes da tarefa, onde Manter preserva a pasta (ela nunca é excluída antes de 3 dias após a data ser definida). Os detalhes da tarefa mostram o tamanho da pasta e permitem excluir a tarefa junto com a pasta. ## Uso do computador Cada bot pode ativar ou desativar o uso do computador, escolher um backend e um modo de aprovação em Configurações do bot (⚙ no painel de informações) → Uso do computador; `/status` e a página de configurações mostram o backend atual, se ele está disponível e, se não estiver, por quê. - **Backends** - **Cua Driver (padrão, nas três plataformas de desktop)**: o [cua-driver](https://github.com/trycua/cua/tree/main/libs/cua-driver) 0.37.0 (MIT) oficial pré-compilado vem com o instalador; `scripts/fetch-cua-driver.mjs` o baixa por plataforma / arquitetura no momento do build e verifica o SHA256. Ele roda como o servidor MCP integrado `cua-driver mcp`, sempre com `CUA_DRIVER_PERMISSION_MODE=bounded` mais um manifesto de capacidades, e **nunca** com `--dangerously-bypass-approvals`. O ambiente do processo filho é limpo (nenhum segredo é repassado), a telemetria fica desligada e `run_script` fica desativado. Só as 24 ferramentas necessárias ficam expostas (ler janelas / capturas de tela, abrir apps, clicar, digitar, pressionar teclas, combinações de teclas, rolar, arrastar, definir valores, menus etc.); todo o resto (scripts, encerrar processos, área de transferência, injeção no navegador, gravação, atualizações, …) fica invisível para o modelo e é recusado. O manifesto lista os apps gráficos instalados na máquina (no Linux, lê os arquivos .desktop; no macOS, /Applications; no Windows, o menu Iniciar) e permite entrada em tela cheia; ele vale por no máximo 24 horas, e o driver reinicia antes de expirar. - **Ponte do Codex Computer Use (opcional, só macOS)**: kimi-computer-use. Requer o OpenAI Codex Computer Use instalado localmente; volta para o Cua Driver quando indisponível. - **Aprovações**: por padrão, todas as ações de uso do computador (ler, capturar a tela, clicar, digitar, pressionar teclas, rolar, arrastar, definir valor) **rodam automaticamente, sem aprovação**. Opcionalmente, “perguntar antes de ações que mudam a tela, como cliques e digitação” (ler a tela continua automático). Na atualização, os bots existentes (inclusive o bot padrão) são migrados para o modo automático. Bots com a predefinição somente leitura só recebem ferramentas de leitura. - **Observações por plataforma** - macOS: conceda ao app as permissões de Acessibilidade e Gravação de Tela em Ajustes do Sistema → Privacidade e Segurança; o driver roda no modo `--direct` (ainda não verificado em máquinas reais). Sem essas permissões, as chamadas de ferramentas de uso do computador do bot são bloqueadas, o turno termina dizendo que não pôde continuar por falta de permissão, e o chat mostra um cartão exatamente com as permissões que faltam (pedido nativo do sistema, abrir o painel correspondente dos Ajustes do Sistema, verificar de novo; a Gravação de Tela geralmente exige reabrir o app, o que o cartão oferece, trazendo de volta os rascunhos não enviados). Nada é solicitado na primeira execução. - Linux: no X11, a interface é lida pelo AT-SPI (requer at-spi2-core e apps com acessibilidade ativada); no Wayland, o suporte é limitado — só a janela em primeiro plano de apps XWayland pode ser operada, e a interface avisa isso. Indisponível sem uma sessão gráfica (DISPLAY / WAYLAND_DISPLAY). - Windows: precisa rodar na sessão interativa da área de trabalho de um usuário conectado (sessões não interativas de serviços / tarefas agendadas não são suportadas). - As capturas de tela só são enviadas aos modelos (com visão). Elas não são encaminhadas aos chats do WeChat / Feishu e só são enviadas como arquivos quando o usuário pede. - Não incluídos: cua-perception, OmniParser (AGPL) e quaisquer componentes do Cua Spaces (FSL). ## Configuração do app do Feishu O jeito mais fácil: Configurações › Canais de chat → Feishu → Conectar, escaneie com o Feishu para criar o app do bot e depois mande uma mensagem para ele no Feishu. Para usar um app empresarial personalizado que você já tem, escolha “Usar meu próprio app” e configure assim: 1. Abra a [Feishu Open Platform](https://open.feishu.cn/app) → crie um app empresarial personalizado e adicione a capacidade Bot. 2. Em Permissions, ative: - `im:message.p2p_msg:readonly` (ler as mensagens individuais que os usuários enviam ao bot) - `im:message:send_as_bot` (enviar mensagens como o app) - `cardkit:card:write` (criar e atualizar cartões, usado nas respostas em streaming) - `im:message.history:readonly` (ler o histórico de conversas: a conexão persistente não reenvia mensagens enviadas enquanto o app estava offline, então, ao iniciar e depois de uma reconexão, o app recupera pelo histórico as conversas ativas nos últimos 7 dias, pegando mensagens das últimas 24 horas, no máximo 20 por conversa. Sem essa permissão, todo o resto funciona, mas nada é recuperado, e o app avisa isso uma vez) 3. Primeiro, informe o App ID / App Secret neste app em Configurações › Canais de chat → Feishu → Conectar → Usar meu próprio app e salve (a conexão persistente precisa estar online antes que o próximo passo possa ser salvo). 4. Events & callbacks → modo de assinatura: “**Receive events through persistent connection**”, e adicione o evento `im.message.receive_v1`. 5. Publique uma versão, adicione você mesmo ao escopo de disponibilidade e depois mande uma linha para o bot do seu próprio Feishu: a primeira pessoa a mandar mensagem depois que ele é conectado se torna o proprietário dele no Feishu (veja “Quem pode usar um bot”). Observação: por enquanto, só conversas individuais (p2p) são tratadas; mensagens de grupo são ignoradas. ## WeChat (iLink) - Configurações › Canais de chat → WeChat → Conectar, escaneie o QR code com o WeChat e confirme; ou rode `npm run weixin:login` (o QR code aparece no terminal e é salvo como PNG na pasta de dados). - O estado de login, o cursor de sincronização e o context_token ficam na pasta de dados e são retomados após um reinício. - Limites da plataforma: depois de cada mensagem do usuário, é possível enviar no máximo 10 respostas em 24 horas; as respostas são enviadas de uma vez quando a geração termina e divididas acima de 4000 caracteres; “digitando…” aparece enquanto a resposta é gerada. - Imagens, arquivos, vídeos: tudo o que o usuário envia (ou cita) é baixado da CDN do WeChat e descriptografado (até 50 MB cada). No modo agente, é salvo em `inbox/` no espaço de trabalho do bot, e a mensagem indica o caminho, para que o agente possa ler, processar ou usar o arquivo como imagem de referência para geração. Modelos com visão (Claude, GPT-5/6, Gemini, Grok 4, Kimi K3, Muse Spark e outros) também recebem a própria imagem; os outros modelos só recebem o caminho. No Linux, o agente ainda não recebe imagens diretamente (o sharp trava no Electron) e só recebe o caminho. Os arquivos que o bot entrega (present) voltam para o WeChat como imagens ou arquivos. - Quando a sessão expira (código de erro -14), o canal é pausado e a interface avisa; escaneie de novo para retomar. ## Desenvolvimento e testes ```bash npm run typecheck npm test # testes unitários + end-to-end offline (mock de Feishu / iLink / Modelflare, incluindo streaming e parada) + testes de integração do gateway (requer o magpie compilado) npm run docs:check # autoverificação do README: recursos presentes e dentro do tamanho, paridade de estrutura zh/en, links relativos e âncoras, termos proibidos, texto alt ``` A lista de recursos do README e a especificação de gravação estão em [docs/assets/README.md](./assets/README.md). Para bots do catálogo (bots prontos com interface própria), veja [docs/bot-framework_en.md](./bot-framework_en.md). ## Licença MIT; veja `LICENSE`. Os componentes de terceiros (magpie MIT — incluindo os estilos de interface adaptados, Tencent openclaw-weixin MIT e outros) estão listados em [THIRD_PARTY_NOTICES_pt.md](../THIRD_PARTY_NOTICES_pt.md) e em Configurações → Sobre. ## Habilidades do usuário e pesquisa no histórico de conversas Veja o [guia de habilidades](./skills_pt.md) para a interface e o formato ZIP. `src/skills/archive.ts` valida os caminhos do ZIP, os limites de extração e o `SKILL.md`; `service.ts` armazena por bot os pacotes, os estados de ativação e as impressões digitais de versão. `src/web/skills.ts` fornece os endpoints de prévia e gerenciamento. As habilidades ativadas são implantadas quando o DSH está ocioso, sem interromper turnos ativos, subagentes nem comandos em segundo plano. `src/botkit/installer.ts` verifica conflitos de nome de habilidades importadas ao atualizar bots do catálogo. `src/store/chat-search.ts` mantém o índice de mensagens no SQLite e oferece filtros, paginação e o contexto da mensagem original. `ui/screens/message-search.js` implementa a pesquisa global; `ui/components/chat-search.js` implementa a busca no chat atual. A pesquisa lê o banco de dados local, sem chamadas ao modelo. Testes relacionados: `test/skills*.test.ts` e `test/chat-search*.test.ts`. Defina `AMB_TEST_CHROME` para escolher o Chromium usado na validação no navegador. Com o runtime do DSH disponível, os testes também verificam a descoberta, o carregamento, a leitura de referências e a desativação de uma habilidade durante um trabalho em andamento.