@diegosouzacdv/jev-browser-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1728 -0
- package/config/ui-testing.json +23 -0
- package/docs/jev-browser-mcp.md +193 -0
- package/mcp_servers/jev-browser-npm/bin/jev-browser-mcp.cjs +34 -0
- package/mcp_servers/jev-browser-npm/src/config.mjs +162 -0
- package/mcp_servers/jev-browser-npm/src/flow.mjs +365 -0
- package/mcp_servers/jev-browser-npm/src/jev-client.mjs +129 -0
- package/mcp_servers/jev-browser-npm/src/server.mjs +206 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,1728 @@
|
|
|
1
|
+
# Orquestrador Multi-Agente Corporativo
|
|
2
|
+
|
|
3
|
+
Orquestrador de desenvolvimento de software com LangGraph, LangChain, MCP,
|
|
4
|
+
persistência SQLite, revisão multiagente, gates determinísticos, HITL e
|
|
5
|
+
artefatos versionados por manifesto.
|
|
6
|
+
|
|
7
|
+
O provedor LLM padrão do código é a Groq quando nenhuma variável é definida.
|
|
8
|
+
O modelo padrão do perfil operacional deste projeto é
|
|
9
|
+
`deepseek/deepseek-v4-flash-0731`, usado via OpenRouter.
|
|
10
|
+
A chave nunca deve ser gravada no código, no `.env.example`, em checkpoints,
|
|
11
|
+
logs ou relatórios.
|
|
12
|
+
|
|
13
|
+
As configurações operacionais dos agentes ficam centralizadas em
|
|
14
|
+
`config/agent-policy.json` (modelo, limite, timeout, retry, contrato, contexto,
|
|
15
|
+
skills, MCP e ownership). Valide ou projete as variáveis de um workflow com:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
python scripts/validate_agent_policy.py validate
|
|
19
|
+
python scripts/render_agent_policy.py show
|
|
20
|
+
python scripts/render_agent_policy.py --github-env
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`ORQUESTRADOR_AGENT_RUNTIME_JSON` e demais variáveis legadas existem apenas
|
|
24
|
+
para compatibilidade explícita; os workflows canônicos derivam a configuração
|
|
25
|
+
do arquivo central.
|
|
26
|
+
|
|
27
|
+
## 1. Pré-requisitos
|
|
28
|
+
|
|
29
|
+
- Python 3.12+;
|
|
30
|
+
- Git;
|
|
31
|
+
- Docker Desktop/Engine para o sandbox estrito e para o Gitea local;
|
|
32
|
+
- Node.js/npm somente quando houver validação TypeScript;
|
|
33
|
+
- uma chave Groq criada no [console de API da Groq](https://console.groq.com/keys)
|
|
34
|
+
ou uma chave OpenRouter criada em [OpenRouter Keys](https://openrouter.ai/settings/keys);
|
|
35
|
+
- OpenSSH no Windows para o acesso ao Gitea;
|
|
36
|
+
- portas locais `3000` (HTTP) e `2222` (SSH) livres.
|
|
37
|
+
|
|
38
|
+
Se uma chave foi publicada em chat, commit ou log, revogue-a e crie outra antes
|
|
39
|
+
de continuar.
|
|
40
|
+
|
|
41
|
+
## 2. Instalação local
|
|
42
|
+
|
|
43
|
+
Windows PowerShell:
|
|
44
|
+
|
|
45
|
+
```powershell
|
|
46
|
+
py -3.12 -m venv .venv
|
|
47
|
+
.venv\Scripts\Activate.ps1
|
|
48
|
+
python -m pip install --upgrade pip
|
|
49
|
+
pip install -r requirements.txt
|
|
50
|
+
Copy-Item .env.example .env
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Linux/macOS:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python3.12 -m venv .venv
|
|
57
|
+
source .venv/bin/activate
|
|
58
|
+
python -m pip install --upgrade pip
|
|
59
|
+
pip install -r requirements.txt
|
|
60
|
+
cp .env.example .env
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Edite `.env` localmente. O arquivo `.env.example` é o catálogo versionado das
|
|
64
|
+
variáveis suportadas; o `.env` é ignorado pelo Git e contém os valores
|
|
65
|
+
específicos da máquina. Para o perfil local deste projeto:
|
|
66
|
+
|
|
67
|
+
```dotenv
|
|
68
|
+
ORQUESTRADOR_LLM_PROVIDER=openrouter
|
|
69
|
+
OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
|
|
70
|
+
ORQUESTRADOR_AGENT_MODEL=deepseek/deepseek-v4-flash-0731
|
|
71
|
+
MCP_FILESYSTEM_ROOTS=.
|
|
72
|
+
ORQUESTRADOR_SANDBOX_MODE=docker
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Quando a chave estiver cadastrada no ambiente de usuário do Windows, use a
|
|
76
|
+
expansão `${OPENROUTER_API_KEY}` acima; não copie o segredo para o `.env`. Para
|
|
77
|
+
CI e produção, use secrets/variáveis do ambiente; não publique o arquivo `.env`.
|
|
78
|
+
|
|
79
|
+
### Conferir a integridade da `.venv`
|
|
80
|
+
|
|
81
|
+
A `.venv` é ignorada pelo Git: se algo reescrever um arquivo dela, nada o traz de
|
|
82
|
+
volta. Em 2026-08-28 um script de tradução reescreveu 23 pacotes (`resolver` →
|
|
83
|
+
`resolve`), e isso só apareceu semanas depois, como erros sem relação aparente.
|
|
84
|
+
Cada pacote instalado guarda o sha256 de cada arquivo no próprio `RECORD`, e o
|
|
85
|
+
script abaixo confere tudo contra ele:
|
|
86
|
+
|
|
87
|
+
```powershell
|
|
88
|
+
.venv\Scripts\python.exe scripts\venv_integrity.py
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
.venv/bin/python scripts/venv_integrity.py
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Sai `0` com `"clean": true`; sai `1` nomeando pacote e arquivo em `mismatched`
|
|
96
|
+
(conteúdo diferente), `missing` (arquivo apagado) ou `unverifiable` (pacote sem
|
|
97
|
+
`RECORD`, que não dá para conferir e por isso não conta como limpo). Rode com o
|
|
98
|
+
interpretador da `.venv` que quer conferir. Leva de 1 a 11 s, conforme o cache de
|
|
99
|
+
disco. Para reparar um pacote apontado:
|
|
100
|
+
`pip install --force-reinstall --no-deps <pacote>==<versão>`.
|
|
101
|
+
|
|
102
|
+
Na agenda do Hermes, uma vez por semana, pela skill
|
|
103
|
+
`.hermes/skills/orquestrador-venv-integrity/`, que só reporta e nunca repara; o
|
|
104
|
+
comando `hermes cron create` está no `SKILL.md` dela.
|
|
105
|
+
|
|
106
|
+
## 2.1. Gitea local para Actions e espelhamento do GitHub
|
|
107
|
+
|
|
108
|
+
O GitHub é a origem canônica deste projeto. O Gitea é uma cópia local usada para
|
|
109
|
+
executar os workflows self-hosted; portanto, o fluxo normal é publicar primeiro
|
|
110
|
+
em `origin` (GitHub) e depois atualizar o remote `gitea`. Não trate o clone local
|
|
111
|
+
do Gitea como fonte de verdade.
|
|
112
|
+
|
|
113
|
+
| Componente | Caminho/URL nesta máquina |
|
|
114
|
+
|---|---|
|
|
115
|
+
| Checkout de desenvolvimento | `C:\orquestrador` |
|
|
116
|
+
| Servidor Gitea, compose e dados persistentes | `C:\gitea` |
|
|
117
|
+
| Runner Linux/Docker | `C:\gitea-runner` |
|
|
118
|
+
| Runner Windows/host | `C:\gitea-runner-win` |
|
|
119
|
+
| URL configurada no compose | `http://192.168.1.3:3000` |
|
|
120
|
+
| SSH do Gitea | `ssh://gitea-local/...` na porta `2222` |
|
|
121
|
+
|
|
122
|
+
O `docker-compose.yml` atual usa `192.168.1.3`. Se o endereço da máquina mudar, atualize
|
|
123
|
+
`ROOT_URL`, `DOMAIN` e `SSH_DOMAIN` no compose antes de registrar novos runners.
|
|
124
|
+
Não misture `localhost`, o IP da LAN e outro hostname no mesmo registro: o endereço usado
|
|
125
|
+
pelo runner precisa ser alcançável por ele.
|
|
126
|
+
|
|
127
|
+
Referências oficiais: [instalação com Docker](https://docs.gitea.com/next/installation/install-with-docker/),
|
|
128
|
+
[Gitea Actions](https://docs.gitea.com/usage/actions/overview/) e
|
|
129
|
+
[Gitea Runner](https://docs.gitea.com/usage/actions/act-runner/).
|
|
130
|
+
|
|
131
|
+
### Subir o servidor Gitea
|
|
132
|
+
|
|
133
|
+
Inicie o Docker Desktop com o engine Linux e confirme que ele está saudável:
|
|
134
|
+
|
|
135
|
+
~~~powershell
|
|
136
|
+
docker info
|
|
137
|
+
docker compose version
|
|
138
|
+
~~~
|
|
139
|
+
|
|
140
|
+
Suba a instância já configurada em `C:\gitea`; não recrie o volume de dados:
|
|
141
|
+
|
|
142
|
+
~~~powershell
|
|
143
|
+
Set-Location C:\gitea
|
|
144
|
+
docker compose pull
|
|
145
|
+
docker compose up -d
|
|
146
|
+
docker compose ps
|
|
147
|
+
docker compose logs --tail 100 server
|
|
148
|
+
Invoke-WebRequest http://192.168.1.3:3000/api/v1/version -TimeoutSec 10
|
|
149
|
+
~~~
|
|
150
|
+
|
|
151
|
+
Na primeira instalação, acesse <http://192.168.1.3:3000>, crie o usuário
|
|
152
|
+
administrador, ative Actions e confirme que o repositório
|
|
153
|
+
`admin/projeto-global` existe. Os dados ficam em `C:\gitea\gitea`; não use
|
|
154
|
+
`docker compose down -v`, pois isso remove o armazenamento persistente.
|
|
155
|
+
|
|
156
|
+
Para parar temporariamente sem apagar dados:
|
|
157
|
+
|
|
158
|
+
~~~powershell
|
|
159
|
+
Set-Location C:\gitea
|
|
160
|
+
docker compose stop
|
|
161
|
+
~~~
|
|
162
|
+
|
|
163
|
+
### GitHub como origem e Gitea como espelho
|
|
164
|
+
|
|
165
|
+
No checkout principal, preserve `origin` apontando para o GitHub e mantenha o
|
|
166
|
+
Gitea em um segundo remote:
|
|
167
|
+
|
|
168
|
+
~~~powershell
|
|
169
|
+
Set-Location C:\orquestrador
|
|
170
|
+
git remote -v
|
|
171
|
+
git remote set-url origin https://github.com/diegosouzacdv/orquestrador.git
|
|
172
|
+
git remote set-url gitea ssh://gitea-local/admin/projeto-global.git
|
|
173
|
+
git fetch origin --prune
|
|
174
|
+
~~~
|
|
175
|
+
|
|
176
|
+
Se `gitea` ainda não existir, use `git remote add gitea ...` em vez de
|
|
177
|
+
`set-url`. A chave SSH do Gitea pode ser criada assim:
|
|
178
|
+
|
|
179
|
+
~~~powershell
|
|
180
|
+
$sshDir = Join-Path $env:USERPROFILE ".ssh"
|
|
181
|
+
New-Item -ItemType Directory -Force $sshDir | Out-Null
|
|
182
|
+
ssh-keygen -t ed25519 -C "gitea-local" -f (Join-Path $sshDir "gitea_local")
|
|
183
|
+
Get-Content (Join-Path $sshDir "gitea_local.pub")
|
|
184
|
+
~~~
|
|
185
|
+
|
|
186
|
+
Adicione a chave pública em **Gitea → Settings → SSH / GPG Keys** e crie
|
|
187
|
+
`C:\Users\<usuario>\.ssh\config`:
|
|
188
|
+
|
|
189
|
+
~~~sshconfig
|
|
190
|
+
Host gitea-local
|
|
191
|
+
HostName 192.168.1.3
|
|
192
|
+
Port 2222
|
|
193
|
+
User git
|
|
194
|
+
IdentityFile ~/.ssh/gitea_local
|
|
195
|
+
IdentitiesOnly yes
|
|
196
|
+
~~~
|
|
197
|
+
|
|
198
|
+
Valide a autenticação e publique o mesmo commit nos dois destinos:
|
|
199
|
+
|
|
200
|
+
~~~powershell
|
|
201
|
+
ssh -T gitea-local
|
|
202
|
+
git pull --ff-only origin master
|
|
203
|
+
git push origin master
|
|
204
|
+
git push gitea master:master
|
|
205
|
+
git push gitea --tags
|
|
206
|
+
git ls-remote origin refs/heads/master
|
|
207
|
+
git ls-remote gitea refs/heads/master
|
|
208
|
+
~~~
|
|
209
|
+
|
|
210
|
+
O `ssh -T` pode responder que não existe shell interativo; isso é esperado se a
|
|
211
|
+
autenticação funcionar. O último bloco deve mostrar o mesmo SHA no GitHub e
|
|
212
|
+
no Gitea. O clone atual em `C:\gitea-runner\repositorio` é apenas um bootstrap
|
|
213
|
+
antigo e não deve ser usado como origem do espelho sem antes conferir seus
|
|
214
|
+
remotes e seu estado:
|
|
215
|
+
|
|
216
|
+
~~~powershell
|
|
217
|
+
git -C C:\gitea-runner\repositorio remote -v
|
|
218
|
+
git -C C:\gitea-runner\repositorio status --short --branch
|
|
219
|
+
~~~
|
|
220
|
+
|
|
221
|
+
Para espelhar também branches e tags removidas, use um clone bare dedicado.
|
|
222
|
+
O comando `git push --mirror` substitui todas as refs do repositório de destino;
|
|
223
|
+
confirme o destino antes de executá-lo e não aponte para um repositório com
|
|
224
|
+
trabalho independente:
|
|
225
|
+
|
|
226
|
+
~~~powershell
|
|
227
|
+
$mirror = "C:\gitea-runner\github-mirror.git"
|
|
228
|
+
if (Test-Path $mirror) { throw "O mirror já existe; valide-o manualmente antes de reutilizar o caminho." }
|
|
229
|
+
git clone --mirror https://github.com/diegosouzacdv/orquestrador.git $mirror
|
|
230
|
+
Set-Location $mirror
|
|
231
|
+
git remote add gitea ssh://gitea-local/admin/projeto-global.git
|
|
232
|
+
git fetch origin --prune
|
|
233
|
+
git push --mirror gitea
|
|
234
|
+
~~~
|
|
235
|
+
|
|
236
|
+
### Runner Linux/Docker — `C:\gitea-runner`
|
|
237
|
+
|
|
238
|
+
Este é o runner usado pelos workflows com `runs-on: ubuntu-latest`, como
|
|
239
|
+
`.gitea/workflows/agentic-graph-e2e.yml` e
|
|
240
|
+
`.gitea/workflows/mcp-integration-smoke.yml`. Ele usa Docker Desktop e o
|
|
241
|
+
socket `/var/run/docker.sock` dentro dos jobs. O socket dá ao job controle do
|
|
242
|
+
Docker do host; aceite jobs somente do repositório confiável.
|
|
243
|
+
|
|
244
|
+
O binário e a configuração já ficam em `C:\gitea-runner`. Se for necessário
|
|
245
|
+
registrar novamente, gere o arquivo uma única vez, solicite um token novo na
|
|
246
|
+
interface do Gitea e não salve o token em arquivo, histórico ou Git:
|
|
247
|
+
|
|
248
|
+
~~~powershell
|
|
249
|
+
Set-Location C:\gitea-runner
|
|
250
|
+
if (!(Test-Path .\config.yaml)) {
|
|
251
|
+
.\act_runner.exe config generate | Out-File -Encoding utf8 .\config.yaml
|
|
252
|
+
}
|
|
253
|
+
$runnerToken = Read-Host "Token do runner Gitea"
|
|
254
|
+
.\act_runner.exe register --no-interactive --instance "http://192.168.1.3:3000" --token $runnerToken --name "orquestrador-local" --labels "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest,ubuntu-24.04:docker://docker.gitea.com/runner-images:ubuntu-24.04,ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04"
|
|
255
|
+
Remove-Variable runnerToken
|
|
256
|
+
~~~
|
|
257
|
+
|
|
258
|
+
Na configuração, preserve pelo menos estas opções para os workflows Docker:
|
|
259
|
+
|
|
260
|
+
~~~yaml
|
|
261
|
+
container:
|
|
262
|
+
options: --volume /var/run/docker.sock:/var/run/docker.sock --add-host=host.docker.internal:host-gateway
|
|
263
|
+
valid_volumes:
|
|
264
|
+
- '**'
|
|
265
|
+
require_docker: true
|
|
266
|
+
docker_timeout: 60s
|
|
267
|
+
bind_workdir: true
|
|
268
|
+
~~~
|
|
269
|
+
|
|
270
|
+
Teste em primeiro plano antes de depender do serviço do Windows:
|
|
271
|
+
|
|
272
|
+
~~~powershell
|
|
273
|
+
docker info
|
|
274
|
+
Set-Location C:\gitea-runner
|
|
275
|
+
.\act_runner.exe daemon --config C:\gitea-runner\config.yaml
|
|
276
|
+
~~~
|
|
277
|
+
|
|
278
|
+
Nesta máquina, o serviço `GiteaActRunner` já está configurado para executar
|
|
279
|
+
`C:\gitea-runner\act_runner.exe daemon --config C:\gitea-runner\config.yaml`.
|
|
280
|
+
Depois que o Docker Desktop estiver pronto, reinicie-o e confira o log:
|
|
281
|
+
|
|
282
|
+
~~~powershell
|
|
283
|
+
Get-Service GiteaActRunner
|
|
284
|
+
Restart-Service GiteaActRunner
|
|
285
|
+
Get-Content C:\gitea-runner\service.err.log -Tail 80
|
|
286
|
+
~~~
|
|
287
|
+
|
|
288
|
+
O serviço deve aparecer como `Running` e o runner deve ficar online em
|
|
289
|
+
**Gitea → Actions → Runners**. Se `docker info` falhar ou o log mencionar
|
|
290
|
+
`Docker Engine socket not found`, corrija o Docker Desktop primeiro; reiniciar
|
|
291
|
+
o runner sozinho não resolve essa causa.
|
|
292
|
+
|
|
293
|
+
Estado verificado em 2026-09-13: serviço `Running`, runner `orquestrador-local`
|
|
294
|
+
(id 3) em `v3.3.1`, com as três labels `ubuntu-*` e `last_online` corrente. O
|
|
295
|
+
log de sucesso tem exatamente três linhas — `Starting runner daemon`,
|
|
296
|
+
`Docker is ready` e `declare successfully`. A ausência de `Docker is ready`
|
|
297
|
+
neste runner é defeito; no runner Windows, não.
|
|
298
|
+
|
|
299
|
+
Uma enxurrada de `failed to fetch task ... target machine actively refused it`
|
|
300
|
+
**não** é defeito do runner: é o container do Gitea parado. O runner fica em
|
|
301
|
+
retry e volta sozinho quando o servidor sobe — foi o que ocorreu entre 07:41 e
|
|
302
|
+
07:55 de 2026-09-13, com recuperação às 07:59 sem nenhuma intervenção no
|
|
303
|
+
runner. Antes de mexer no runner, confira `docker compose ps` em `C:\gitea`.
|
|
304
|
+
|
|
305
|
+
### Runner Windows/host — `C:\gitea-runner-win`
|
|
306
|
+
|
|
307
|
+
Este é um segundo runner, independente do runner Docker, registrado como
|
|
308
|
+
`orquestrador-windows` (id 4) com a label `windows-latest:host`. Ele é usado
|
|
309
|
+
pelos jobs Windows da matriz `os: [ubuntu-latest, windows-latest]` de
|
|
310
|
+
`.gitea/workflows/antigravity-sdk-homologation.yml` — hoje o único workflow
|
|
311
|
+
Gitea que não é só `ubuntu-latest`. Sem este runner, a perna `windows-latest`
|
|
312
|
+
dessa matriz fica enfileirada para sempre em vez de falhar, que é o sintoma
|
|
313
|
+
pelo qual essa ausência costuma ser percebida.
|
|
314
|
+
|
|
315
|
+
**O serviço `GiteaActRunnerWindows` já existe nesta máquina** e está correto:
|
|
316
|
+
NSSM, `AUTO_START`, `Application=C:\gitea-runner-win\act_runner.exe`,
|
|
317
|
+
`AppDirectory=C:\gitea-runner-win`,
|
|
318
|
+
`AppParameters=daemon --config C:\gitea-runner-win\config.yaml`. Confira antes
|
|
319
|
+
de tentar criá-lo de novo — `nssm install` sobre um serviço existente não é o
|
|
320
|
+
caminho:
|
|
321
|
+
|
|
322
|
+
~~~powershell
|
|
323
|
+
sc.exe query GiteaActRunnerWindows
|
|
324
|
+
reg query "HKLM\SYSTEM\CurrentControlSet\Services\GiteaActRunnerWindows\Parameters"
|
|
325
|
+
~~~
|
|
326
|
+
|
|
327
|
+
#### As duas causas que mantiveram este runner parado
|
|
328
|
+
|
|
329
|
+
Diagnosticadas em 2026-09-13, com o serviço existente e bem configurado:
|
|
330
|
+
|
|
331
|
+
1. **O binário não estava lá.** `C:\gitea-runner-win\` tinha `.runner` e
|
|
332
|
+
`config.yaml`, mas não `act_runner.exe`. O serviço sobe, não acha o
|
|
333
|
+
executável e morre com `ESTADO: STOPPED`, `WIN32_EXIT_CODE: 1066 (0x42a)` e
|
|
334
|
+
`SERVICE_EXIT_CODE: 3` — `ERROR_PATH_NOT_FOUND`. O 1066 é genérico
|
|
335
|
+
("erro específico do serviço") e não aponta o arquivo; quem lê só o 1066
|
|
336
|
+
procura defeito de registro e não encontra nada.
|
|
337
|
+
2. **`require_docker: true` herdado do runner Docker.** O `config.yaml` deste
|
|
338
|
+
diretório é cópia do config do runner Linux, inclusive do bloco
|
|
339
|
+
`container:`. A opção está documentada no próprio arquivo como *"Always
|
|
340
|
+
require a reachable docker daemon, **even if not required by runner**"* —
|
|
341
|
+
ou seja, ela amarra a subida deste runner ao Docker Desktop, que um runner
|
|
342
|
+
`host` não usa. Com o Docker parado, o runner Windows recusa subir sem ter
|
|
343
|
+
motivo próprio para isso.
|
|
344
|
+
|
|
345
|
+
Correção das duas, na ordem:
|
|
346
|
+
|
|
347
|
+
~~~powershell
|
|
348
|
+
Set-Location C:\gitea-runner-win
|
|
349
|
+
if (!(Test-Path .\act_runner.exe)) {
|
|
350
|
+
Copy-Item C:\gitea-runner\act_runner.exe .\act_runner.exe
|
|
351
|
+
}
|
|
352
|
+
.\act_runner.exe --version # precisa casar com o runner Docker: v3.3.1
|
|
353
|
+
~~~
|
|
354
|
+
|
|
355
|
+
No `config.yaml` deste runner, e **somente** neste, deixe:
|
|
356
|
+
|
|
357
|
+
~~~yaml
|
|
358
|
+
container:
|
|
359
|
+
require_docker: false
|
|
360
|
+
~~~
|
|
361
|
+
|
|
362
|
+
O resto do bloco `container:` (socket Docker, `valid_volumes`, `bind_workdir`)
|
|
363
|
+
é inerte aqui, porque um runner `host` não cria container — mas mantê-lo copiado
|
|
364
|
+
é o que faz `require_docker` voltar despercebido na próxima cópia do arquivo.
|
|
365
|
+
|
|
366
|
+
Valide em foreground antes de depender do serviço:
|
|
367
|
+
|
|
368
|
+
~~~powershell
|
|
369
|
+
Set-Location C:\gitea-runner-win
|
|
370
|
+
.\act_runner.exe daemon --config C:\gitea-runner-win\config.yaml
|
|
371
|
+
~~~
|
|
372
|
+
|
|
373
|
+
O sucesso são **duas** linhas, sem `Docker is ready`:
|
|
374
|
+
|
|
375
|
+
~~~text
|
|
376
|
+
level=info msg="Starting runner daemon"
|
|
377
|
+
level=info msg="runner: orquestrador-windows, with version: v3.3.1, with labels: [windows-latest], declare successfully"
|
|
378
|
+
~~~
|
|
379
|
+
|
|
380
|
+
`Docker is ready` aqui significa que `require_docker` voltou a `true`. A label
|
|
381
|
+
sai como `windows-latest` porque o `:host` é o modo de execução do lado do
|
|
382
|
+
runner, não parte do nome da label — `runs-on: windows-latest` no workflow é o
|
|
383
|
+
que casa.
|
|
384
|
+
|
|
385
|
+
O `config.yaml` desse runner aponta
|
|
386
|
+
`ORQUESTRADOR_WINDOWS_PYTHON` para
|
|
387
|
+
`C:\orquestrador\.venv\Scripts\python.exe`. Confira que o ambiente virtual
|
|
388
|
+
existe e tem as dependências do projeto antes de iniciar o serviço.
|
|
389
|
+
|
|
390
|
+
Encerre o foreground com `Ctrl+C` antes de subir o serviço: os dois usam o
|
|
391
|
+
mesmo `.runner.lock` e a mesma identidade registrada.
|
|
392
|
+
|
|
393
|
+
#### Subir o serviço — exige PowerShell elevado
|
|
394
|
+
|
|
395
|
+
`sc.exe start` sem elevação falha com `OpenService FALHA 5: Acesso negado`, e
|
|
396
|
+
esse 5 é do controle do serviço, não do runner. Em PowerShell **como
|
|
397
|
+
administrador**:
|
|
398
|
+
|
|
399
|
+
~~~powershell
|
|
400
|
+
Start-Service GiteaActRunnerWindows
|
|
401
|
+
Get-Service GiteaActRunner, GiteaActRunnerWindows
|
|
402
|
+
~~~
|
|
403
|
+
|
|
404
|
+
Executado pelo operador em 2026-09-13: o serviço ficou `RUNNING` e os **dois**
|
|
405
|
+
runners passaram a reportar `last_online` no mesmo minuto — id 3
|
|
406
|
+
(`orquestrador-local`) e id 4 (`orquestrador-windows`), com 12 segundos de
|
|
407
|
+
diferença. É essa a evidência que fecha a subida dos dois runners; serviço
|
|
408
|
+
`RUNNING` sozinho não prova conexão com o Gitea.
|
|
409
|
+
|
|
410
|
+
Só crie o serviço com NSSM se `sc.exe query` disser que ele não existe. Ele
|
|
411
|
+
precisa de diretório de trabalho próprio e não pode reutilizar o registro do
|
|
412
|
+
runner Docker:
|
|
413
|
+
|
|
414
|
+
~~~powershell
|
|
415
|
+
$nssm = (Get-Command nssm.exe).Source
|
|
416
|
+
& $nssm install GiteaActRunnerWindows C:\gitea-runner-win\act_runner.exe
|
|
417
|
+
& $nssm set GiteaActRunnerWindows AppDirectory C:\gitea-runner-win
|
|
418
|
+
& $nssm set GiteaActRunnerWindows AppParameters 'daemon --config C:\gitea-runner-win\config.yaml'
|
|
419
|
+
& $nssm set GiteaActRunnerWindows Start SERVICE_AUTO_START
|
|
420
|
+
Start-Service GiteaActRunnerWindows
|
|
421
|
+
~~~
|
|
422
|
+
|
|
423
|
+
O modo `host` expõe o ambiente Windows, ferramentas e credenciais da máquina
|
|
424
|
+
ao job. Use-o apenas para workflows confiáveis.
|
|
425
|
+
|
|
426
|
+
### Secrets e workflows
|
|
427
|
+
|
|
428
|
+
Secrets do GitHub não são copiados automaticamente para o Gitea. Cadastre no
|
|
429
|
+
repositório Gitea, em **Settings → Actions → Secrets**, os valores exigidos
|
|
430
|
+
pelos workflows, como `OPENROUTER_API_KEY`, `CONTEXT7_API_KEY`,
|
|
431
|
+
`TWENTY_FIRST_API_KEY` e `GEMINI_API_KEY` quando aplicável. Tokens de clone e
|
|
432
|
+
tokens de registro de runner devem ser tratados separadamente e nunca entrar
|
|
433
|
+
no repositório.
|
|
434
|
+
|
|
435
|
+
Os arquivos em `.gitea/workflows/` são executados pelo Gitea. Os arquivos em
|
|
436
|
+
`.github/workflows/` continuam sendo executados pelo GitHub Actions; o fato de
|
|
437
|
+
o conteúdo ser espelhado não faz o Gitea executar workflows do GitHub. Dentro
|
|
438
|
+
de um job Docker, use `http://host.docker.internal:3000` para acessar o Gitea;
|
|
439
|
+
`localhost` aponta para o próprio container.
|
|
440
|
+
|
|
441
|
+
### Diagnóstico e manutenção
|
|
442
|
+
|
|
443
|
+
~~~powershell
|
|
444
|
+
Set-Location C:\gitea
|
|
445
|
+
docker compose ps
|
|
446
|
+
docker compose logs --tail 100 server
|
|
447
|
+
Get-Service GiteaActRunner
|
|
448
|
+
Get-Content C:\gitea-runner\service.err.log -Tail 80
|
|
449
|
+
Get-Service GiteaActRunnerWindows -ErrorAction SilentlyContinue
|
|
450
|
+
Get-Content C:\gitea-runner-win\service.err.log -Tail 80 -ErrorAction SilentlyContinue
|
|
451
|
+
~~~
|
|
452
|
+
|
|
453
|
+
Serviço `Running` prova que o processo existe, não que o runner está falando
|
|
454
|
+
com o Gitea. Quem responde isso é o `last_online` do servidor, e ele é
|
|
455
|
+
consultável sem abrir a interface e sem autenticar:
|
|
456
|
+
|
|
457
|
+
~~~powershell
|
|
458
|
+
docker exec gitea sqlite3 /data/gitea/gitea.db "select id, name, version, agent_labels, datetime(last_online,'unixepoch','localtime') from action_runner where deleted is null;"
|
|
459
|
+
~~~
|
|
460
|
+
|
|
461
|
+
**O `where deleted is null` não é opcional.** O Gitea apaga runner por *soft
|
|
462
|
+
delete*: a linha permanece em `action_runner` com a coluna `deleted`
|
|
463
|
+
preenchida, e a interface e a API deixam de listá-la. Uma consulta sem esse
|
|
464
|
+
filtro mostra registros já removidos como se estivessem ativos — foi o que
|
|
465
|
+
aconteceu em 2026-09-13, quando os ids 1 e 2 foram lidos como "órfãos
|
|
466
|
+
aparecendo offline na interface" e na verdade já tinham sido excluídos em
|
|
467
|
+
2026-08-30, minutos depois de criados e antes do id 3 existir.
|
|
468
|
+
|
|
469
|
+
Um `last_online` recente é o runner online agora. Os registros vivos nesta
|
|
470
|
+
máquina são o id 3 (`orquestrador-local`, labels `ubuntu-*`) e o id 4
|
|
471
|
+
(`orquestrador-windows`, label `windows-latest`). Para conferir o que a
|
|
472
|
+
interface de fato mostra, sem depender da leitura do banco:
|
|
473
|
+
|
|
474
|
+
~~~powershell
|
|
475
|
+
docker exec gitea sqlite3 /data/gitea/gitea.db "select id, datetime(deleted,'unixepoch','localtime') from action_runner;"
|
|
476
|
+
~~~
|
|
477
|
+
|
|
478
|
+
Nunca apague linha de `action_runner` por SQL: remover runner é operação de
|
|
479
|
+
interface, e o soft delete existe para preservar a trilha dos jobs que aquele
|
|
480
|
+
runner atendeu.
|
|
481
|
+
|
|
482
|
+
Se um runner for removido pela interface do Gitea, pare o daemon e remova
|
|
483
|
+
somente `.runner` e `.runner.lock` no diretório daquele runner; preserve
|
|
484
|
+
`config.yaml`, registre um novo token e valide novamente. Nunca remova
|
|
485
|
+
`C:\gitea\gitea` para corrigir um problema de runner.
|
|
486
|
+
|
|
487
|
+
No diagnóstico desta máquina, em 2026-09-11, o endpoint do Gitea recusou
|
|
488
|
+
conexão porque o container estava parado e `C:\gitea-runner\service.err.log`
|
|
489
|
+
registrou `Docker Engine socket not found`. A sequência de recuperação é:
|
|
490
|
+
|
|
491
|
+
~~~powershell
|
|
492
|
+
Set-Location C:\gitea
|
|
493
|
+
docker compose up -d
|
|
494
|
+
docker info
|
|
495
|
+
Restart-Service GiteaActRunner
|
|
496
|
+
Get-Content C:\gitea-runner\service.err.log -Tail 80
|
|
497
|
+
~~~
|
|
498
|
+
|
|
499
|
+
Em 2026-09-13 o Gitea (`1.27.3`) e o runner Docker estavam saudáveis, e só o
|
|
500
|
+
runner Windows estava parado: binário ausente e `require_docker: true` herdado.
|
|
501
|
+
A lição de diagnóstico é que os dois runners falham por causas **disjuntas** —
|
|
502
|
+
o Docker depende do Docker Desktop e do container do Gitea; o Windows/host não
|
|
503
|
+
depende de nenhum dos dois. Tratar "os runners não sobem" como um problema só
|
|
504
|
+
faz procurar causa comum onde não há.
|
|
505
|
+
|
|
506
|
+
## 2.2. Variáveis de ambiente
|
|
507
|
+
|
|
508
|
+
O arquivo [`.env.example`](.env.example) é o catálogo versionado das variáveis
|
|
509
|
+
suportadas. O `.env` local é ignorado pelo Git e não deve ser publicado.
|
|
510
|
+
Para as credenciais cadastradas no ambiente de usuário do Windows, use referências
|
|
511
|
+
sem copiar os valores:
|
|
512
|
+
|
|
513
|
+
```dotenv
|
|
514
|
+
OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
|
|
515
|
+
CONTEXT7_API_KEY=${CONTEXT7_API_KEY}
|
|
516
|
+
TWENTY_FIRST_API_KEY=${TWENTY_FIRST_API_KEY}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
Abra um novo PowerShell depois de alterar variáveis no Windows. Confira somente
|
|
520
|
+
a presença das credenciais:
|
|
521
|
+
|
|
522
|
+
```powershell
|
|
523
|
+
python -c "import os; from dotenv import load_dotenv; load_dotenv('.env', override=False); names=['OPENROUTER_API_KEY','CONTEXT7_API_KEY','TWENTY_FIRST_API_KEY']; print({n:bool(os.environ.get(n)) for n in names})"
|
|
524
|
+
git check-ignore -v .env
|
|
525
|
+
python scripts/validate_agent_policy.py validate
|
|
526
|
+
python scripts/plan_status.py validate
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
As variáveis estão organizadas no `.env.example` por provider LLM, OpenRouter,
|
|
530
|
+
MCP, Gitea/Git, sandbox, retry, kernel, orçamento, observabilidade, skills,
|
|
531
|
+
HITL, identidade e mídia. O modelo padrão operacional é
|
|
532
|
+
`deepseek/deepseek-v4-flash-0731`; o Backend usa `reasoning_effort=max` na
|
|
533
|
+
política central `config/agent-policy.json`. Não habilite overrides legados.
|
|
534
|
+
|
|
535
|
+
## 2.3. Supermemory local (memória entre sessões)
|
|
536
|
+
|
|
537
|
+
Desde 2026-09-23, por decisão do operador, a memória entre sessões mora no
|
|
538
|
+
`supermemory-server` rodando no **WSL Ubuntu** da máquina do operador. O plano da
|
|
539
|
+
nuvem chegou ao limite e passou a recusar gravações, e o conector da nuvem foi
|
|
540
|
+
**desativado** no mesmo dia (claude.ai → Settings → Connectors → Supermemory). As
|
|
541
|
+
regras de uso estão no `AGENTS.md`, subseção "Supermemory: o servidor local do
|
|
542
|
+
operador".
|
|
543
|
+
|
|
544
|
+
Tudo abaixo roda **dentro do Ubuntu** (`wsl -d Ubuntu`), a partir de `~`, nunca
|
|
545
|
+
de `/mnt/c/...`: a pasta de dados segue o diretório atual quando não é fixada, e
|
|
546
|
+
`/mnt/c` é o disco do Windows, mais lento.
|
|
547
|
+
|
|
548
|
+
### Instalar (uma vez)
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
sudo apt update && sudo apt install -y curl
|
|
552
|
+
cd ~ && curl -fsSL https://supermemory.ai/install | bash
|
|
553
|
+
echo 'export PATH="$HOME/.supermemory/bin:$PATH"' >> ~/.bashrc
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### Configuração fixa (uma vez)
|
|
557
|
+
|
|
558
|
+
Embedding multilíngue e local, com a pasta de dados fixada. Nada disso é
|
|
559
|
+
segredo. O `embedding-plan.json` que o assistente grava **não** é o que o
|
|
560
|
+
servidor carrega; sem estas variáveis ele sobe com o modelo inglês de 768
|
|
561
|
+
dimensões, que não combina com as tabelas de 1024 (medido em 2026-09-23).
|
|
562
|
+
|
|
563
|
+
```bash
|
|
564
|
+
cat >> ~/.bashrc <<'EOF'
|
|
565
|
+
export SUPERMEMORY_EMBEDDING_PROVIDER=local
|
|
566
|
+
export SUPERMEMORY_EMBEDDING_MODEL=Xenova/bge-m3
|
|
567
|
+
export SUPERMEMORY_EMBEDDING_DIMENSIONS=1024
|
|
568
|
+
export SUPERMEMORY_DATA_DIR="$HOME/.supermemory"
|
|
569
|
+
EOF
|
|
570
|
+
source ~/.bashrc
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
Chave do LLM que extrai as memórias (GLM, pela API compatível com OpenAI da
|
|
574
|
+
Z.ai). A chave vai para um arquivo **fora** de `~/.supermemory`, porque o
|
|
575
|
+
servidor lê o `~/.supermemory/env`, cifra o conteúdo em `env.enc` e apaga o
|
|
576
|
+
original; e a extração roda num processo que só enxerga variáveis exportadas.
|
|
577
|
+
|
|
578
|
+
```bash
|
|
579
|
+
mkdir -p ~/.config/glm && chmod 700 ~/.config/glm
|
|
580
|
+
read -rsp "Chave GLM: " K; echo; printf '%s' "$K" > ~/.config/glm/key; unset K
|
|
581
|
+
chmod 600 ~/.config/glm/key
|
|
582
|
+
|
|
583
|
+
cat >> ~/.bashrc <<'EOF'
|
|
584
|
+
sm() { ( export OPENAI_API_KEY="$(cat "$HOME/.config/glm/key")" \
|
|
585
|
+
OPENAI_BASE_URL=https://api.z.ai/api/coding/paas/v4 \
|
|
586
|
+
OPENAI_MODEL=glm-4.6 OPENAI_FAST_MODEL=glm-4.5-air
|
|
587
|
+
exec supermemory-server "$@" ); }
|
|
588
|
+
EOF
|
|
589
|
+
source ~/.bashrc
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
`OPENAI_BASE_URL` depende do plano da Z.ai: com a assinatura de código use a URL
|
|
593
|
+
`coding` acima; com saldo pré-pago, `https://api.z.ai/api/paas/v4`. A URL errada
|
|
594
|
+
responde `Insufficient balance or no resource package`.
|
|
595
|
+
|
|
596
|
+
### Subir e conferir
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
cd ~ && sm # sobe em http://localhost:6767 e fica em primeiro plano
|
|
600
|
+
supermemory-server doctor # noutro terminal: provedor, embedding, pasta de dados
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
O quadro `supermemory ready` precisa mostrar `database /home/<você>/.supermemory` e
|
|
604
|
+
`embeddings local · Xenova/bge-m3 · 1024d`. **Não compartilhe prints desse quadro:
|
|
605
|
+
a linha `api key` traz a chave inteira.**
|
|
606
|
+
|
|
607
|
+
### Testar
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
# grava um documento; no terminal do servidor a linha final deve terminar em "N memories"
|
|
611
|
+
curl -s http://localhost:6767/v3/documents -H "Content-Type: application/json" \
|
|
612
|
+
-d '{"content":"Teste do Supermemory local.","containerTag":"orquestrador"}'; echo
|
|
613
|
+
|
|
614
|
+
# busca: containerTags é LISTA. Sem ela, ou com containerTag no singular, a
|
|
615
|
+
# resposta é 200 com resultado vazio -- parece "nada gravado" e não é.
|
|
616
|
+
curl -s http://localhost:6767/v4/search -H "Content-Type: application/json" \
|
|
617
|
+
-d '{"q":"supermemory local","containerTags":["orquestrador"],"searchMode":"memories","threshold":0}'; echo
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
Mande sempre `threshold`. Sem ele o servidor aplica um limiar próprio e descarta
|
|
621
|
+
memória relevante: medido em 2026-09-23, "Hermes cron workdir" devolveu 1 memória
|
|
622
|
+
sem `threshold` e as do mesmo assunto com `threshold: 0`. Com 0, a busca devolve
|
|
623
|
+
as mais próximas até o `limit`, cada uma com a `similarity` ao lado. O cliente do
|
|
624
|
+
orquestrador manda o valor de `config/durable-stores.json`,
|
|
625
|
+
`memory_store.search_threshold`.
|
|
626
|
+
|
|
627
|
+
Teste de ponta a ponta de **todas** as chamadas que o orquestrador faz (gravar,
|
|
628
|
+
buscar, editar, grafo, ler, esquecer, apagar), num espaço descartável
|
|
629
|
+
`orquestrador-smoke`, que ele limpa no fim. Cada linha diz `OK` ou `FAIL` com o
|
|
630
|
+
motivo que o servidor deu:
|
|
631
|
+
|
|
632
|
+
```bash
|
|
633
|
+
PYTHONPATH=$PWD .venv/bin/python scripts/supermemory_local_smoke.py
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
No **Windows**, que é onde a `.venv` do projeto mora, rode pelo PowerShell a
|
|
637
|
+
partir do checkout — o Windows alcança o servidor do WSL por `127.0.0.1:6767`.
|
|
638
|
+
Os scripts abaixo (migração e sync) seguem a mesma forma:
|
|
639
|
+
|
|
640
|
+
```powershell
|
|
641
|
+
cd C:\orquestrador # ou a worktree da branch que tem os scripts
|
|
642
|
+
$env:PYTHONPATH = (Get-Location).Path
|
|
643
|
+
$env:PYTHONUTF8 = "1" # o console cp1252 quebra no ✅ das memórias
|
|
644
|
+
$py = "C:\orquestrador\.venv\Scripts\python.exe"
|
|
645
|
+
& $py scripts\supermemory_local_smoke.py
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
A busca do smoke tenta de novo por até 30 s antes de reprovar: o servidor indexa
|
|
649
|
+
a memória nova na própria fila, e uma espera fixa confundia "ainda não indexou"
|
|
650
|
+
com "a busca não funciona".
|
|
651
|
+
|
|
652
|
+
### Listar as memórias
|
|
653
|
+
|
|
654
|
+
```bash
|
|
655
|
+
# documentos do espaço, paginados
|
|
656
|
+
curl -s http://localhost:6767/v3/documents/list -H "Content-Type: application/json" \
|
|
657
|
+
-d '{"containerTags":["orquestrador"],"page":1,"limit":50}'; echo
|
|
658
|
+
|
|
659
|
+
# um documento com as memórias extraídas dele
|
|
660
|
+
curl -s http://localhost:6767/v3/documents/<documentId>; echo
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
Pelo orquestrador, o grafo inteiro (todos os documentos com as memórias de cada
|
|
664
|
+
um) sai de `GET /memory/graph` da API HTTP (seção 7), autenticado como as outras
|
|
665
|
+
rotas.
|
|
666
|
+
|
|
667
|
+
### Migrar as memórias da nuvem (uma vez)
|
|
668
|
+
|
|
669
|
+
Com o servidor de pé, a partir do checkout do orquestrador:
|
|
670
|
+
|
|
671
|
+
```bash
|
|
672
|
+
PYTHONPATH=$PWD .venv/bin/python scripts/supermemory_migrate_to_local.py --dry-run
|
|
673
|
+
PYTHONPATH=$PWD .venv/bin/python scripts/supermemory_migrate_to_local.py
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
São as 33 memórias de `docs/supermemory/cloud-export-2026-09-23.json`, já
|
|
677
|
+
normalizadas para o padrão de gravação, escritas com o texto exato, sem reprocessar
|
|
678
|
+
no LLM. O script confere o export inteiro contra o padrão **antes** da primeira
|
|
679
|
+
gravação — `--dry-run` lista em `invalid` o que seria recusado — e guarda em
|
|
680
|
+
`data/supermemory-migration.json` o que já foi aceito: rodar de novo não duplica.
|
|
681
|
+
|
|
682
|
+
### Padrão de gravação (aplicado pelo código)
|
|
683
|
+
|
|
684
|
+
Toda gravação — MCP, tela, migração, inbox — passa por `src/memory/standard.py`,
|
|
685
|
+
que recusa e nomeia cada violação. O formato que passa:
|
|
686
|
+
|
|
687
|
+
```text
|
|
688
|
+
TEC-002 was delivered on 2026-09-22 in commit 93bbf57.
|
|
689
|
+
STK-004 was marked ✅, meaning developed and validated within the scope of the cited evidence, not approved by the operator, on 2026-09-22 in commit d2ab40d.
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
Uma frase, em inglês, com data ISO e âncora (SHA, id de item ou `session <id>`),
|
|
693
|
+
ícone com o sentido escrito, sem estado volátil ("still pending", "in progress")
|
|
694
|
+
e sem segredo. Os valores estão em `config/durable-stores.json`,
|
|
695
|
+
`memory_store.standard`.
|
|
696
|
+
|
|
697
|
+
### Sincronizar com as sessões da nuvem
|
|
698
|
+
|
|
699
|
+
Sessões na nuvem não alcançam este servidor. Elas leem
|
|
700
|
+
`docs/supermemory/snapshot.json` e gravam em `docs/supermemory/inbox/`, que chegam
|
|
701
|
+
aqui pelo git. Depois de trazer as branches mescladas para o checkout:
|
|
702
|
+
|
|
703
|
+
```bash
|
|
704
|
+
PYTHONPATH=$PWD .venv/bin/python scripts/supermemory_sync.py
|
|
705
|
+
git add docs/supermemory/snapshot.json
|
|
706
|
+
git commit -m "memory: snapshot do servidor local" -- docs/supermemory/snapshot.json
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
O script aplica cada operação do inbox uma vez (o registro fica em
|
|
710
|
+
`data/supermemory-sync.json`) e reescreve o snapshot a partir do servidor. Rode-o
|
|
711
|
+
depois da migração, para o snapshot passar a vir do servidor, e sempre que um
|
|
712
|
+
inbox novo chegar. O Hermes pode rodá-lo na mesma agenda do laço do plano.
|
|
713
|
+
|
|
714
|
+
As memórias seguidas de uma mesma sessão vão numa chamada só, e o servidor faz um
|
|
715
|
+
documento por chamada: no grafo, cada sessão aparece como um documento com os
|
|
716
|
+
fatos em volta. Até 2026-09-23 cada memória ia sozinha, e o grafo mostrava um par
|
|
717
|
+
documento-memória para cada uma.
|
|
718
|
+
|
|
719
|
+
### Reagrupar as memórias gravadas uma por documento (uma vez)
|
|
720
|
+
|
|
721
|
+
As 42 memórias gravadas antes dessa mudança continuam uma por documento. O
|
|
722
|
+
reagrupamento junta cada lote de gravação da nuvem e cada sessão do inbox num
|
|
723
|
+
documento só. Ele só apaga um documento antigo **depois** de conferir que cada
|
|
724
|
+
memória dele está no documento novo, e no fim confere que o conjunto de memórias
|
|
725
|
+
é o mesmo de antes, sem perda e sem duplicata. Memória que o repositório não
|
|
726
|
+
conhece (gravada pela tela, por exemplo) fica onde está.
|
|
727
|
+
|
|
728
|
+
```powershell
|
|
729
|
+
git fetch origin claude/nifty-knuth-27gns3
|
|
730
|
+
git checkout origin/claude/nifty-knuth-27gns3
|
|
731
|
+
& $py scripts\supermemory_sync.py # aplica o que estiver no inbox
|
|
732
|
+
& $py scripts\supermemory_regroup.py # só o plano, não toca em nada
|
|
733
|
+
& $py scripts\supermemory_regroup.py --apply # "verified": true no fim
|
|
734
|
+
& $py scripts\supermemory_sync.py # snapshot a partir do servidor
|
|
735
|
+
git add docs/supermemory/snapshot.json
|
|
736
|
+
git commit -m "memory: snapshot reagrupado" -- docs/supermemory/snapshot.json
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
`--apply` sai `0` só com `"verified": true`. Se sair `1`, nada foi apagado antes
|
|
740
|
+
do problema: o relatório nomeia o grupo que o servidor não confirmou. Rodar de
|
|
741
|
+
novo é seguro.
|
|
742
|
+
|
|
743
|
+
### Sessões e Hermes: o MCP `supermemory-local`
|
|
744
|
+
|
|
745
|
+
O `.mcp.json` declara `supermemory-local`, um MCP `stdio` que chama o servidor
|
|
746
|
+
local. Ferramentas: `search_memory`, `add_memory` (`action` `save`/`forget`),
|
|
747
|
+
`list_memories`, `get_document`, `update_memory`, `delete_document`. O
|
|
748
|
+
interpretador vem de `ORQUESTRADOR_PYTHON`:
|
|
749
|
+
|
|
750
|
+
```powershell
|
|
751
|
+
# Windows (checkout em C:\orquestrador)
|
|
752
|
+
[System.Environment]::SetEnvironmentVariable("ORQUESTRADOR_PYTHON", "C:\orquestrador\.venv\Scripts\python.exe", "User")
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
```bash
|
|
756
|
+
# Linux/WSL
|
|
757
|
+
echo "export ORQUESTRADOR_PYTHON=$PWD/.venv/bin/python" >> ~/.bashrc
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
Depois, em uma sessão nova, `/mcp` deve listar `supermemory-local` conectado.
|
|
761
|
+
Se o interpretador escolhido não tiver o pacote `mcp`, o servidor se entrega ao
|
|
762
|
+
`python` da `.venv` do projeto. Sessões na nuvem (claude.ai/code) **não** alcançam
|
|
763
|
+
o `localhost` do operador: o mesmo MCP responde a elas pelo snapshot e grava no
|
|
764
|
+
inbox, com o erro do servidor ao lado para ninguém confundir espelho com servidor.
|
|
765
|
+
Para o MCP subir num container da nuvem, o ambiente precisa da `.venv` com
|
|
766
|
+
`requirements.txt` instalado — o script de setup do ambiente é o lugar.
|
|
767
|
+
|
|
768
|
+
Hermes, em `~/.hermes/config.yaml`. O nome `supermemory` mantém as ferramentas
|
|
769
|
+
`mcp_supermemory_search_memory` e `mcp_supermemory_add_memory` que a skill
|
|
770
|
+
`orquestrador-plan-loop` já chama:
|
|
771
|
+
|
|
772
|
+
```yaml
|
|
773
|
+
mcp_servers:
|
|
774
|
+
supermemory:
|
|
775
|
+
command: /caminho/do/orquestrador/.venv/bin/python
|
|
776
|
+
args: ["/caminho/do/orquestrador/mcp_servers/supermemory_local_server.py"]
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Espaço de desenvolvimento e despacho contínuo pelo Hermes
|
|
780
|
+
|
|
781
|
+
Desde 2026-09-24 a memória tem dois espaços. O geral (`orquestrador`) guarda
|
|
782
|
+
decisões e achados; o de desenvolvimento (`orquestrador-dev`) guarda só os
|
|
783
|
+
registros `[dispatch]`, `[result]`, `[blocked]` e `[compact]` do despacho. O mesmo
|
|
784
|
+
servidor MCP atende os dois: `supermemory-dev` é ele com
|
|
785
|
+
`SUPERMEMORY_LOCAL_SPACE=dev`, já declarado no `.mcp.json`. O espaço dev não tem
|
|
786
|
+
espelho versionado; sem o servidor, ele responde com o erro e nada mais.
|
|
787
|
+
|
|
788
|
+
O Hermes despacha cada task do plano para a sessão Claude Code persistente do
|
|
789
|
+
papel dela (`backend`, `frontend`, `docs`, `dba`), um turno por task com
|
|
790
|
+
`claude -p --resume`, até o plano acabar. Política em `config/dev-dispatch.json`;
|
|
791
|
+
instalação, pré-requisitos (login do Claude Code, uma worktree e uma sessão por
|
|
792
|
+
papel, o campo `role` nos itens) e operação em
|
|
793
|
+
[`.hermes/skills/orquestrador-dev-dispatcher/SKILL.md`](.hermes/skills/orquestrador-dev-dispatcher/SKILL.md).
|
|
794
|
+
Para ver o que ele faria sem lançar nada:
|
|
795
|
+
|
|
796
|
+
```powershell
|
|
797
|
+
$env:HERMES_HOME = "$env:LOCALAPPDATA\hermes"
|
|
798
|
+
C:\orquestrador\.venv\Scripts\python.exe .hermes\skills\orquestrador-dev-dispatcher\scripts\dispatch_tick.py --repo C:\orquestrador --dry-run
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
### Segurança da porta 6767
|
|
802
|
+
|
|
803
|
+
O servidor escuta em `0.0.0.0:6767` (`ss -ltn | grep 6767`), e não há opção
|
|
804
|
+
documentada para prendê-lo ao `localhost`. No WSL2 em modo NAT, o padrão, só o
|
|
805
|
+
próprio Windows o alcança. Se o `.wslconfig` usa `networkingMode=mirrored`, ou se
|
|
806
|
+
houver `portproxy`, a rede local também alcança. Bloqueie a entrada vinda da rede
|
|
807
|
+
no firewall do Windows (PowerShell como administrador). O tráfego de loopback
|
|
808
|
+
não passa por essa regra:
|
|
809
|
+
|
|
810
|
+
```powershell
|
|
811
|
+
New-NetFirewallRule -DisplayName "Supermemory local - bloqueia rede" -Direction Inbound `
|
|
812
|
+
-Protocol TCP -LocalPort 6767 -RemoteAddress LocalSubnet,Internet -Action Block
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
## 3. Modelos Groq
|
|
816
|
+
|
|
817
|
+
Os perfis podem ser alterados sem modificar código:
|
|
818
|
+
|
|
819
|
+
```dotenv
|
|
820
|
+
ORQUESTRADOR_MODEL_STRUCTURED=openai/gpt-oss-20b
|
|
821
|
+
ORQUESTRADOR_MODEL_CODING=openai/gpt-oss-120b
|
|
822
|
+
ORQUESTRADOR_MODEL_REASONING=openai/gpt-oss-120b
|
|
823
|
+
ORQUESTRADOR_MODEL_RESEARCH=groq/compound
|
|
824
|
+
ORQUESTRADOR_MODEL_RESEARCH_FAST=groq/compound-mini
|
|
825
|
+
ORQUESTRADOR_MODEL_INPUT_GUARD=meta-llama/llama-prompt-guard-2-86m
|
|
826
|
+
ORQUESTRADOR_MODEL_OUTPUT_GUARD=openai/gpt-oss-safeguard-20b
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
Mapa recomendado, conforme a documentação oficial da Groq:
|
|
830
|
+
|
|
831
|
+
| Perfil/modelo | Janela | Máx. saída | Uso |
|
|
832
|
+
|---|---:|---:|---|
|
|
833
|
+
| `openai/gpt-oss-20b` | 131.072 | 65.536 | router, classificações e JSON estruturado rápido |
|
|
834
|
+
| `openai/gpt-oss-120b` | 131.072 | 65.536 | código, arquitetura, review, compliance e DBA |
|
|
835
|
+
| `groq/compound` | 131.072 | 8.192 | pesquisa/agentes com ferramentas Groq |
|
|
836
|
+
| `groq/compound-mini` | 131.072 | 8.192 | pesquisa rápida e sumarização |
|
|
837
|
+
| `meta-llama/llama-prompt-guard-2-86m` | 512 | 512 | classificação de prompt de entrada |
|
|
838
|
+
| `openai/gpt-oss-safeguard-20b` | 131.072 | 65.536 | inspeção de saída/safety |
|
|
839
|
+
| `qwen/qwen3.6-27b` | 131.072 | 16.384 | alternativa experimental; modelo preview e mais caro |
|
|
840
|
+
|
|
841
|
+
O perfil estruturado usa `json_schema` nos GPT-OSS; o perfil de
|
|
842
|
+
código/raciocínio usa o GPT-OSS 120B. Compound fica reservado para pesquisa
|
|
843
|
+
com ferramentas. Prompt Guard e Safeguard permanecem nos portões de segurança
|
|
844
|
+
e foram validados pelo smoke real do workflow Groq.
|
|
845
|
+
|
|
846
|
+
Os números da tabela são capacidades máximas dos modelos, não a quota da
|
|
847
|
+
conta. No tier on-demand com 8.000 TPM, configure limites conservadores para
|
|
848
|
+
que entrada mais saída solicitada caibam na quota:
|
|
849
|
+
|
|
850
|
+
```dotenv
|
|
851
|
+
ORQUESTRADOR_LLM_MAX_INPUT_CHARS=12000
|
|
852
|
+
ORQUESTRADOR_GROQ_MAX_COMPLETION_TOKENS=2048
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
Ao migrar para um tier superior, esses dois valores podem ser aumentados sem
|
|
856
|
+
alterar o código.
|
|
857
|
+
|
|
858
|
+
Para OpenRouter, os limites não ficam presos aos valores conservadores da
|
|
859
|
+
Groq. O catálogo operacional por modelo está centralizado em
|
|
860
|
+
[`src/llm/llm_model_config.py`](src/llm/llm_model_config.py). A troca global do modelo
|
|
861
|
+
é feita em um único ponto:
|
|
862
|
+
|
|
863
|
+
| Modelo agentic | Contexto | Máx. saída | Input / 1M | Output / 1M |
|
|
864
|
+
|---|---:|---:|---:|---:|
|
|
865
|
+
| `deepseek/deepseek-v4-flash-0731` | 1.310.720 | 943.718 | US$ 0,04 | US$ 0,08 |
|
|
866
|
+
| `minimax/minimax-m3` (fallback produção) | 1.048.576 | 512.000 | US$ 0,30 | US$ 1,20 |
|
|
867
|
+
| `moonshotai/kimi-k2.7-code` (fallback produção) | 262.144 | 235.929 | US$ 0,67 | US$ 3,40 |
|
|
868
|
+
|
|
869
|
+
Os valores são snapshot do catálogo e não substituem o preço efetivo da
|
|
870
|
+
resposta. O adapter registra usage/custo retornados pelo OpenRouter.
|
|
871
|
+
|
|
872
|
+
```dotenv
|
|
873
|
+
ORQUESTRADOR_LLM_PROVIDER=openrouter
|
|
874
|
+
ORQUESTRADOR_AGENT_MODEL=deepseek/deepseek-v4-flash-0731
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
O `DeepSeek V4 Flash 0731` está registrado com contexto de 1.310.720 tokens e
|
|
878
|
+
até 943.718 tokens de saída. O limite de entrada operacional é de 1.000.000
|
|
879
|
+
de caracteres para evitar enviar acidentalmente o repositório inteiro em uma
|
|
880
|
+
única chamada. Esses valores podem ser sobrescritos temporariamente com
|
|
881
|
+
`ORQUESTRADOR_MODEL_CONFIG_JSON`, sem editar agentes. Um teto operacional por
|
|
882
|
+
ambiente (`ORQUESTRADOR_OPENROUTER_MAX_COMPLETION_TOKENS` ou
|
|
883
|
+
`ORQUESTRADOR_LLM_MAX_INPUT_CHARS`) continua disponível e tem precedência.
|
|
884
|
+
|
|
885
|
+
Referências oficiais: [Quickstart Groq](https://console.groq.com/docs/quickstart),
|
|
886
|
+
[modelos](https://console.groq.com/docs/models), [API compatível com OpenAI](https://console.groq.com/docs/openai),
|
|
887
|
+
[Structured Outputs](https://console.groq.com/docs/structured-outputs),
|
|
888
|
+
[LangChain + Groq](https://console.groq.com/docs/langchain),
|
|
889
|
+
[Prompt Caching](https://console.groq.com/docs/prompt-caching) e
|
|
890
|
+
[limites](https://console.groq.com/docs/rate-limits).
|
|
891
|
+
Páginas dos modelos: [Compound](https://console.groq.com/docs/compound),
|
|
892
|
+
[Compound mini](https://console.groq.com/docs/compound/systems/compound-mini),
|
|
893
|
+
[GPT-OSS 120B](https://console.groq.com/docs/model/openai/gpt-oss-120b),
|
|
894
|
+
[GPT-OSS 20B](https://console.groq.com/docs/model/openai/gpt-oss-20b),
|
|
895
|
+
[Qwen 3.6 27B](https://console.groq.com/docs/model/qwen/qwen3.6-27b),
|
|
896
|
+
[Prompt Guard 86M](https://console.groq.com/docs/model/meta-llama/llama-prompt-guard-2-86m) e
|
|
897
|
+
[Safeguard 20B](https://console.groq.com/docs/model/openai/gpt-oss-safeguard-20b).
|
|
898
|
+
|
|
899
|
+
## 4. Roteamento multi-provedor
|
|
900
|
+
|
|
901
|
+
Os agentes usam uma fábrica única (`src/llm/llm_provider.py`) e podem ser
|
|
902
|
+
direcionados individualmente, por perfil ou globalmente. O comportamento
|
|
903
|
+
legado continua sendo Groq por padrão. As credenciais são sempre lidas de
|
|
904
|
+
variáveis de ambiente:
|
|
905
|
+
|
|
906
|
+
```dotenv
|
|
907
|
+
# Provedor único para todos os agentes
|
|
908
|
+
ORQUESTRADOR_LLM_PROVIDER=groq
|
|
909
|
+
GROQ_API_KEY=...
|
|
910
|
+
|
|
911
|
+
# Alternativas suportadas pelo adapter
|
|
912
|
+
ANTHROPIC_API_KEY=...
|
|
913
|
+
OPENAI_API_KEY=...
|
|
914
|
+
GEMINI_API_KEY=...
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
Para escolher provedor e modelo por agente, use JSON no secret/variável
|
|
918
|
+
`ORQUESTRADOR_LLM_ROUTING_JSON` (não coloque chaves dentro do JSON):
|
|
919
|
+
|
|
920
|
+
```json
|
|
921
|
+
{
|
|
922
|
+
"default": {"provider": "groq", "model": "openai/gpt-oss-20b"},
|
|
923
|
+
"profiles": {
|
|
924
|
+
"reasoning": {"provider": "anthropic", "model": "claude-sonnet-4-5"},
|
|
925
|
+
"coding": {"provider": "openai", "model": "gpt-4o-mini"}
|
|
926
|
+
},
|
|
927
|
+
"agents": {
|
|
928
|
+
"agent_architect": {"provider": "anthropic", "model": "claude-opus-4-1"},
|
|
929
|
+
"agent_backend": {"provider": "openai", "model": "gpt-4o"},
|
|
930
|
+
"agent_code_review": {"provider": "groq", "model": "openai/gpt-oss-120b"}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
Precedência: `agents` > `profiles` > `default` > variáveis legadas de
|
|
936
|
+
provedor/perfil. Assim, o futuro frontend poderá salvar uma política de
|
|
937
|
+
execução sem alterar o código dos agentes. Para trocar tudo de uma vez,
|
|
938
|
+
defina apenas `ORQUESTRADOR_LLM_PROVIDER`, a chave correspondente e os
|
|
939
|
+
modelos `ORQUESTRADOR_MODEL_*`.
|
|
940
|
+
|
|
941
|
+
## 5. Executar o orquestrador
|
|
942
|
+
|
|
943
|
+
Modo automático para CI/local:
|
|
944
|
+
|
|
945
|
+
```bash
|
|
946
|
+
python main.py --auto-aprovar --tipo new --prazo "2 semanas" \
|
|
947
|
+
"Criar um sistema de controle de tarefas"
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
Opções importantes:
|
|
951
|
+
|
|
952
|
+
```bash
|
|
953
|
+
python main.py --projeto-id meu-projeto --documentacao docs/requisitos.md pedido
|
|
954
|
+
python main.py --no-cache --auto-aprovar pedido
|
|
955
|
+
python main.py --resume --thread-id minha-thread
|
|
956
|
+
python main.py --devops --auto-aprovar pedido
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
O resultado só é considerado entrega aprovada quando `compliance_status=approved`.
|
|
960
|
+
No modo sem chave LLM, o fluxo pode terminar com artefato fallback rejeitado.
|
|
961
|
+
|
|
962
|
+
## 6. Sandbox e gates D2
|
|
963
|
+
|
|
964
|
+
Construa a imagem segura:
|
|
965
|
+
|
|
966
|
+
```bash
|
|
967
|
+
docker build -f docker/Dockerfile.sandbox -t orquestrador-sandbox:py312 .
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
Execute gates em container:
|
|
971
|
+
|
|
972
|
+
```bash
|
|
973
|
+
ORQUESTRADOR_SANDBOX_MODE=docker \
|
|
974
|
+
ORQUESTRADOR_SANDBOX_IMAGE=orquestrador-sandbox:py312 \
|
|
975
|
+
python -m pytest -q
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
O container usa rede desabilitada, filesystem read-only, usuário não-root,
|
|
979
|
+
limites de CPU/memória/PIDs, timeout e ambiente sem credenciais. O contrato
|
|
980
|
+
`config/frontend-validation.json` fixa a versão do TypeScript usada pelo
|
|
981
|
+
validador e pelo `docker/Dockerfile.sandbox`; o workflow instala essa mesma versão
|
|
982
|
+
antes do graph. ESLint e Playwright continuam pertencendo ao projeto gerado e
|
|
983
|
+
só são executados quando sua configuração e dependências versionadas existem.
|
|
984
|
+
O modo
|
|
985
|
+
`subprocess` é apenas uma opção local de desenvolvimento.
|
|
986
|
+
|
|
987
|
+
Os gates executam compilação Python, Ruff, mypy, pytest, TypeScript e EXPLAIN
|
|
988
|
+
SQL quando o artefato e o ambiente suportam cada verificação.
|
|
989
|
+
|
|
990
|
+
## 7. HITL HTTP
|
|
991
|
+
|
|
992
|
+
`HITL_API_TOKEN` é um token interno criado pelo operador para proteger a API
|
|
993
|
+
HTTP local. Ele não é fornecido pelo Google, não é `GEMINI_API_KEY`, não é uma
|
|
994
|
+
credencial OAuth do Antigravity e não é um token do Gitea. Mantenha-o fora do
|
|
995
|
+
repositório, prompts, argumentos de processo, logs e artefatos.
|
|
996
|
+
|
|
997
|
+
Em Windows PowerShell, gere um token aleatório sem colar o valor literal no
|
|
998
|
+
histórico. Esta forma funciona também no Windows PowerShell antigo; não troque
|
|
999
|
+
por uma expressão com parênteses ou barras invertidas:
|
|
1000
|
+
|
|
1001
|
+
~~~powershell
|
|
1002
|
+
$bytes = New-Object byte[] 32
|
|
1003
|
+
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
|
|
1004
|
+
|
|
1005
|
+
try {
|
|
1006
|
+
$rng.GetBytes($bytes)
|
|
1007
|
+
}
|
|
1008
|
+
finally {
|
|
1009
|
+
$rng.Dispose()
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
$env:HITL_API_TOKEN = [Convert]::ToBase64String($bytes)
|
|
1013
|
+
Remove-Variable bytes, rng
|
|
1014
|
+
~~~
|
|
1015
|
+
|
|
1016
|
+
A variável vale para a janela/processo atual. Inicie a API na mesma janela,
|
|
1017
|
+
preferencialmente a partir do checkout:
|
|
1018
|
+
|
|
1019
|
+
~~~powershell
|
|
1020
|
+
Set-Location C:\orquestrador
|
|
1021
|
+
& 'C:\orquestrador\.venv\Scripts\python.exe' -m uvicorn server.app:app --host 127.0.0.1 --port 8000
|
|
1022
|
+
~~~
|
|
1023
|
+
|
|
1024
|
+
Se a API rodar como serviço, carregue o mesmo token pelo cofre de segredos do
|
|
1025
|
+
serviço; não coloque o valor diretamente no comando, no arquivo versionado ou
|
|
1026
|
+
em `setx` compartilhado. Para chamar a API em outro terminal, forneça o mesmo
|
|
1027
|
+
valor ao ambiente desse cliente e use apenas o cabeçalho Bearer:
|
|
1028
|
+
|
|
1029
|
+
~~~powershell
|
|
1030
|
+
$headers = @{ Authorization = "Bearer $env:HITL_API_TOKEN" }
|
|
1031
|
+
~~~
|
|
1032
|
+
|
|
1033
|
+
Não execute `$env:HITL_API_TOKEN` sozinho, pois isso imprime o segredo. Ao
|
|
1034
|
+
terminar a sessão, limpe a variável se ela não for mais necessária:
|
|
1035
|
+
|
|
1036
|
+
~~~powershell
|
|
1037
|
+
Remove-Item Env:HITL_API_TOKEN -ErrorAction SilentlyContinue
|
|
1038
|
+
~~~
|
|
1039
|
+
|
|
1040
|
+
Verifique saúde:
|
|
1041
|
+
|
|
1042
|
+
```bash
|
|
1043
|
+
curl http://127.0.0.1:8000/health
|
|
1044
|
+
```
|
|
1045
|
+
|
|
1046
|
+
Consulte ou retome uma thread usando `Authorization: Bearer ...`:
|
|
1047
|
+
|
|
1048
|
+
```bash
|
|
1049
|
+
curl -H "Authorization: Bearer $HITL_API_TOKEN" \
|
|
1050
|
+
http://127.0.0.1:8000/threads/minha-thread/state
|
|
1051
|
+
|
|
1052
|
+
curl -X POST -H "Authorization: Bearer $HITL_API_TOKEN" \
|
|
1053
|
+
-H "Content-Type: application/json" \
|
|
1054
|
+
-d '{"resume_data":{"aprovado":true}}' \
|
|
1055
|
+
http://127.0.0.1:8000/threads/minha-thread/resume
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
### 7.1. Antigravity OAuth: state dir, host e container
|
|
1059
|
+
|
|
1060
|
+
`ORQUESTRADOR_ANTIGRAVITY_STATE_DIR` é o diretório privado onde a API mantém o
|
|
1061
|
+
estado protegido do login Antigravity. Ele não é uma API key, não é o
|
|
1062
|
+
`client_id`, não é o `client_secret` e não deve apontar para o checkout. O
|
|
1063
|
+
diretório pode conter o refresh token; por isso, copiar seus arquivos para
|
|
1064
|
+
prompt, log, issue, artefato ou workspace é uma violação da fronteira de
|
|
1065
|
+
credenciais.
|
|
1066
|
+
|
|
1067
|
+
Há duas visões do mesmo diretório no ambiente local:
|
|
1068
|
+
|
|
1069
|
+
| Onde | Caminho | Uso |
|
|
1070
|
+
|---|---|---|
|
|
1071
|
+
| API no host Windows | `C:\orquestrador-secrets\antigravity-state` | persistência do login e do refresh |
|
|
1072
|
+
| job Linux do runner Docker | `/var/lib/orquestrador/antigravity-state` | caminho usado pelo workflow |
|
|
1073
|
+
| secret do Gitea | `/var/lib/orquestrador/antigravity-state` | valor que o job recebe; não use o caminho Windows |
|
|
1074
|
+
|
|
1075
|
+
O secret do Gitea configura somente o processo do job. Ele não configura uma
|
|
1076
|
+
API que já esteja rodando no host. A API precisa receber o caminho Windows em
|
|
1077
|
+
seu próprio ambiente; o runner precisa montar o mesmo diretório no caminho
|
|
1078
|
+
Linux antes de executar o workflow.
|
|
1079
|
+
|
|
1080
|
+
O diretório deve ser criado e protegido pelo administrador do host, fora de
|
|
1081
|
+
`C:\orquestrador`, com ACL/DACL privada e sem junction, symlink ou reparse
|
|
1082
|
+
point. Para o job Linux, o runner autorizado deve montar esse mesmo diretório
|
|
1083
|
+
como `/var/lib/orquestrador/antigravity-state`; o valor salvo no Gitea é sempre
|
|
1084
|
+
o caminho Linux, nunca o caminho Windows. Não liste nem copie os arquivos de
|
|
1085
|
+
estado OAuth.
|
|
1086
|
+
|
|
1087
|
+
#### 1. Cadastrar o caminho no Gitea
|
|
1088
|
+
|
|
1089
|
+
Em `admin/projeto-global`, abra **Settings → Actions → Secrets** e cadastre
|
|
1090
|
+
somente o par abaixo:
|
|
1091
|
+
|
|
1092
|
+
~~~text
|
|
1093
|
+
Nome: ORQUESTRADOR_ANTIGRAVITY_STATE_DIR
|
|
1094
|
+
Valor: /var/lib/orquestrador/antigravity-state
|
|
1095
|
+
~~~
|
|
1096
|
+
|
|
1097
|
+
Não coloque o caminho Windows nesse secret: o job é Linux dentro do Docker.
|
|
1098
|
+
Não tente ler o valor de volta pela interface e não registre secrets em
|
|
1099
|
+
artefatos. Os secrets `ORQUESTRADOR_ANTIGRAVITY_OAUTH_CLIENT_ID` e
|
|
1100
|
+
`ORQUESTRADOR_ANTIGRAVITY_OAUTH_CLIENT_SECRET` continuam necessários nos
|
|
1101
|
+
passos Kernel/Claude: a conta persistida contém um `refreshToken`, e o proxy
|
|
1102
|
+
precisa desses dois valores para convertê-lo em um access token antes da probe.
|
|
1103
|
+
Eles não são necessários para SDK ou Pi e não devem ser injetados nesses jobs.
|
|
1104
|
+
|
|
1105
|
+
**Não crie um cliente OAuth próprio para esta rota.** Esta instrução existiu
|
|
1106
|
+
aqui até 2026-09-14 e estava errada; ela custou três runs e duas sessões de
|
|
1107
|
+
diagnóstico, e o sintoma não apontava para ela.
|
|
1108
|
+
|
|
1109
|
+
Um cliente criado no seu projeto do Google Cloud **autentica com sucesso** — o
|
|
1110
|
+
login abre, o consentimento passa, o `refresh_token` é emitido e renovado — e
|
|
1111
|
+
mesmo assim toda chamada morre em `UPSTREAM_HTTP_403`. O Google emite token
|
|
1112
|
+
para qualquer cliente válido; o backend do Antigravity só aceita token cunhado
|
|
1113
|
+
pelo cliente em que ele confia. A falha aparece apenas na chamada real, muito
|
|
1114
|
+
depois do login, e se parece com um problema de conta ou de modelo.
|
|
1115
|
+
|
|
1116
|
+
O par que a rota exige é o que o snapshot upstream traz embutido, visível nas
|
|
1117
|
+
linhas removidas de `antigravity_proxy/patches/003-account-boundary.patch`. O
|
|
1118
|
+
patch o tirou do **default de runtime** para que uma cópia do sidecar não fosse
|
|
1119
|
+
fonte de credencial; o valor continua no repositório, dentro do patch, e é dali
|
|
1120
|
+
que ele deve ser lido para preencher as duas variáveis:
|
|
1121
|
+
|
|
1122
|
+
~~~text
|
|
1123
|
+
ORQUESTRADOR_ANTIGRAVITY_OAUTH_CLIENT_ID
|
|
1124
|
+
ORQUESTRADOR_ANTIGRAVITY_OAUTH_CLIENT_SECRET
|
|
1125
|
+
~~~
|
|
1126
|
+
|
|
1127
|
+
Isso vale para o ambiente da API **e** para os secrets do Gitea: os dois lados
|
|
1128
|
+
precisam do mesmo cliente, porque o refresh token fica preso ao cliente que o
|
|
1129
|
+
emitiu. Trocar o cliente invalida a conta persistida — é obrigatório
|
|
1130
|
+
`logout` seguido de `auto --force-login` depois de mudar esses valores.
|
|
1131
|
+
|
|
1132
|
+
Como confirmar em segundos, sem gastar uma corrida da matriz:
|
|
1133
|
+
|
|
1134
|
+
~~~powershell
|
|
1135
|
+
Set-Location C:\orquestrador
|
|
1136
|
+
$env:PYTHONPATH = "."
|
|
1137
|
+
& '.\.venv\Scripts\python.exe' scripts\list_antigravity_models.py
|
|
1138
|
+
~~~
|
|
1139
|
+
|
|
1140
|
+
Com o cliente certo, isso lista os modelos que a conta anuncia — em 2026-09-14,
|
|
1141
|
+
onze, incluindo `gemini-3.8-flash-tiered`, que é o default do workflow. Com o
|
|
1142
|
+
cliente errado, responde `500 ... UPSTREAM_HTTP_403`, e nenhuma troca de conta
|
|
1143
|
+
ou de modelo muda esse resultado.
|
|
1144
|
+
|
|
1145
|
+
#### 2. Login automático
|
|
1146
|
+
|
|
1147
|
+
Depois de configurar no ambiente da API `ORQUESTRADOR_ANTIGRAVITY_STATE_DIR`,
|
|
1148
|
+
`ORQUESTRADOR_ANTIGRAVITY_OAUTH_CLIENT_ID` e
|
|
1149
|
+
`ORQUESTRADOR_ANTIGRAVITY_OAUTH_CLIENT_SECRET`, execute somente o helper abaixo
|
|
1150
|
+
no host autorizado:
|
|
1151
|
+
|
|
1152
|
+
~~~powershell
|
|
1153
|
+
Set-Location C:\orquestrador
|
|
1154
|
+
& 'C:\orquestrador\.venv\Scripts\python.exe' scripts\antigravity_auth.py auto
|
|
1155
|
+
~~~
|
|
1156
|
+
|
|
1157
|
+
O helper valida o ambiente, abre o navegador, recebe o callback loopback sem
|
|
1158
|
+
clipboard ou impressão de código e executa `refresh`. Se já houver uma conta,
|
|
1159
|
+
ele não abre novo login. O resultado esperado é `ready`; a API grava o estado
|
|
1160
|
+
protegido somente no state dir. Não execute outro fluxo OAuth em paralelo.
|
|
1161
|
+
|
|
1162
|
+
O valor produzido depois do login é estado protegido da conta, não deve ser
|
|
1163
|
+
usado como `ORQUESTRADOR_ANTIGRAVITY_OAUTH_CLIENT_SECRET` nem copiado para
|
|
1164
|
+
prompt, log, issue ou artefato.
|
|
1165
|
+
|
|
1166
|
+
Somente depois de `status` pronto, refresh aprovado, DACL/reparse verificados e
|
|
1167
|
+
mount confirmado deve ser disparada a matriz real. O procedimento detalhado de
|
|
1168
|
+
AGS-004/ACP está em
|
|
1169
|
+
[`docs/ags004-acp-live-runbook.md`](docs/ags004-acp-live-runbook.md).
|
|
1170
|
+
|
|
1171
|
+
## 8. Testes locais
|
|
1172
|
+
|
|
1173
|
+
```bash
|
|
1174
|
+
pytest -q
|
|
1175
|
+
ruff check .
|
|
1176
|
+
mypy agents src server main.py scripts pi_harness mcp_servers
|
|
1177
|
+
python scripts/smoke/mcp_tools_binding.py
|
|
1178
|
+
python scripts/smoke/full_orchestrator_e2e.py
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
Smoke real Groq, com limite baixo de tokens:
|
|
1182
|
+
|
|
1183
|
+
```bash
|
|
1184
|
+
GROQ_API_KEY="$GROQ_API_KEY" ORQUESTRADOR_SMOKE_MAX_TOKENS=512 \
|
|
1185
|
+
ORQUESTRADOR_SMOKE_RUN_GRAPH=false \
|
|
1186
|
+
python scripts/smoke_groq.py
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
O smoke não imprime a chave. O prompt caching é automático na Groq; o teste
|
|
1190
|
+
registra `cached_tokens`, mas um cache miss não é tratado como falha porque a
|
|
1191
|
+
Groq não garante hit em toda chamada. O workflow manual aprovado percorre
|
|
1192
|
+
Compound, Compound Mini, Prompt Guard, GPT-OSS 20B, GPT-OSS 120B, Qwen 27B e
|
|
1193
|
+
Safeguard 20B; todas as chamadas retornaram `OK`. O grafo fica desativado nesse
|
|
1194
|
+
workflow para separar smoke de modelos de gates HITL/Compliance.
|
|
1195
|
+
|
|
1196
|
+
## 9. Batch API
|
|
1197
|
+
|
|
1198
|
+
Ative somente para workloads assíncronos:
|
|
1199
|
+
|
|
1200
|
+
```dotenv
|
|
1201
|
+
ORQUESTRADOR_BATCH_ENABLED=true
|
|
1202
|
+
ORQUESTRADOR_USE_BATCH=true
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
O adapter usa JSONL e a API Groq Batch. A janela é assíncrona; acompanhe o
|
|
1206
|
+
`batch_id` e não coloque prompts completos no `GraphState`.
|
|
1207
|
+
|
|
1208
|
+
O Batch depende de habilitação do plano Groq. Se a API responder
|
|
1209
|
+
`403 not_available_for_plan`, a credencial está válida, mas a conta ainda não
|
|
1210
|
+
possui esse recurso; o adapter registra a situação e usa uma simulação offline
|
|
1211
|
+
correlacionável para testes locais. A execução real exige habilitar o Batch no
|
|
1212
|
+
plano Groq e usar um modelo elegível.
|
|
1213
|
+
|
|
1214
|
+
Documentação: [Groq Batch API](https://console.groq.com/docs/batch) e
|
|
1215
|
+
[referência de batches](https://console.groq.com/docs/api-reference).
|
|
1216
|
+
|
|
1217
|
+
## 10. Git remoto e GitHub Checks
|
|
1218
|
+
|
|
1219
|
+
Em GitHub Actions, configure secrets/variáveis:
|
|
1220
|
+
|
|
1221
|
+
```dotenv
|
|
1222
|
+
GITHUB_TOKEN=provided-by-github-actions
|
|
1223
|
+
GITHUB_REPOSITORY=owner/repository
|
|
1224
|
+
GITHUB_SHA=commit-sha
|
|
1225
|
+
ORQUESTRADOR_GIT_REMOTE=https://github.com/owner/repository.git
|
|
1226
|
+
```
|
|
1227
|
+
|
|
1228
|
+
O workflow cria branch por thread, publica commit, abre PR draft e publica
|
|
1229
|
+
GitHub Check. Sem credencial explícita, o sistema mantém apenas branch/commit
|
|
1230
|
+
local e não inventa URL de PR.
|
|
1231
|
+
|
|
1232
|
+
## 11. CI
|
|
1233
|
+
|
|
1234
|
+
O workflow `quality.yml` é o portão determinístico: instalação, Ruff, mypy,
|
|
1235
|
+
pytest, os smokes e o build do sandbox. Ele é **dividido em dois jobs** —
|
|
1236
|
+
`quality` roda `-m 'not slow and not quarantine'` e `slow-e2e` roda o
|
|
1237
|
+
complemento —, e os dois somados são `pytest -q`.
|
|
1238
|
+
|
|
1239
|
+
**Quando ele roda está nos próprios arquivos, e as duas superfícies diferem de
|
|
1240
|
+
propósito.** No GitHub, `pull_request` e disparo manual; no Gitea, disparo
|
|
1241
|
+
manual apenas, porque enfileirar um check obrigatório contra um runner local
|
|
1242
|
+
que nem sempre está ligado seria pior que não ter. Esta frase dizia "em todo
|
|
1243
|
+
push/PR" até 2026-09-15, o que estava errado nas duas — e é o tipo de cópia em
|
|
1244
|
+
prosa que `tests/guards/test_governance_document_truth_guard.py` passou a
|
|
1245
|
+
conferir contra o YAML.
|
|
1246
|
+
|
|
1247
|
+
Ele **não bloqueia merge** em nenhuma das duas: o `master` do Gitea não tem
|
|
1248
|
+
regra de proteção, e a do GitHub é recurso pago em repositório privado. O que
|
|
1249
|
+
segura a qualidade antes do push é a regra de trabalho do `AGENTS.md`, aplicada
|
|
1250
|
+
pelo hook de pre-commit; o workflow é a repetição disso num Linux, que é o
|
|
1251
|
+
ambiente que a máquina do operador não tem.
|
|
1252
|
+
|
|
1253
|
+
O workflow `groq-smoke.yml` é manual (`workflow_dispatch`) e exige o secret
|
|
1254
|
+
`GROQ_API_KEY` em um GitHub Environment protegido. Ele não deve ser executado
|
|
1255
|
+
em PRs de forks.
|
|
1256
|
+
|
|
1257
|
+
Para executar o E2E do grafo com LLM real, use o workflow manual
|
|
1258
|
+
`.github/workflows/groq-graph-e2e.yml`. Ele usa o mesmo Environment protegido,
|
|
1259
|
+
executa as trilhas `new`, `feature` e `fix` com Groq e mantém os limites do tier
|
|
1260
|
+
on-demand. O workflow define `ORQUESTRADOR_REQUIRE_REAL_LLM=true`, portanto
|
|
1261
|
+
fallback offline faz o job falhar. A execução pode terminar com
|
|
1262
|
+
`compliance_status=rejected` por
|
|
1263
|
+
pendências legítimas; o objetivo desse workflow é comprovar o caminho real do
|
|
1264
|
+
grafo, não forçar aprovação de uma entrega sem evidência.
|
|
1265
|
+
|
|
1266
|
+
No GitHub: **Actions → groq-graph-e2e → Run workflow → master**. Se o
|
|
1267
|
+
Environment exigir aprovação, aprove o job antes que os secrets sejam
|
|
1268
|
+
liberados.
|
|
1269
|
+
|
|
1270
|
+
Permissões mínimas recomendadas:
|
|
1271
|
+
|
|
1272
|
+
```yaml
|
|
1273
|
+
permissions:
|
|
1274
|
+
contents: read
|
|
1275
|
+
checks: write
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
Para PR remoto, conceda `contents: write` e `pull-requests: write` apenas no
|
|
1279
|
+
workflow manual protegido.
|
|
1280
|
+
|
|
1281
|
+
## 12. Observabilidade
|
|
1282
|
+
|
|
1283
|
+
Para exportar spans via OTLP:
|
|
1284
|
+
|
|
1285
|
+
```dotenv
|
|
1286
|
+
OTEL_SERVICE_NAME=orquestrador
|
|
1287
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
Sem endpoint, o sistema registra apenas eventos técnicos locais. Prompts,
|
|
1291
|
+
código, tokens de autenticação e conteúdo de documentação não entram nos
|
|
1292
|
+
traces.
|
|
1293
|
+
|
|
1294
|
+
## 13. Artefatos e D5
|
|
1295
|
+
|
|
1296
|
+
O código gerado é salvo em `workspace/<thread-id>/`; o estado carrega hashes e
|
|
1297
|
+
referências do manifesto. Checkpoints legados ainda podem ser lidos durante a
|
|
1298
|
+
migração, mas novas execuções não precisam persistir blobs completos quando
|
|
1299
|
+
`ORQUESTRADOR_LEGACY_CODE_STATE=false`.
|
|
1300
|
+
|
|
1301
|
+
## 14. Documentação do projeto
|
|
1302
|
+
|
|
1303
|
+
- [Trilha de sessões e status](docs/plano-agentic-skills-status.md) (append-only, com índice em `docs/plano-status.json`)
|
|
1304
|
+
- [Estrutura de pastas e nomenclatura](docs/planos/auditoria/estrutura-pastas-e-nomenclatura.md)
|
|
1305
|
+
- [Fluxo de agentes, documentação e execução](docs/fluxo-agentes-documentacao-e-execucao.md)
|
|
1306
|
+
- [Guia de atualização dos validadores](docs/planos/auditoria/guia-atualizacao-versoes-validadores.md)
|
|
1307
|
+
|
|
1308
|
+
## 15. Time travel, retenção e governança
|
|
1309
|
+
|
|
1310
|
+
Forks são não destrutivos: o checkpoint pai permanece na thread original e o
|
|
1311
|
+
fork recebe novo `thread_id`, workspace independente e branch local.
|
|
1312
|
+
|
|
1313
|
+
```bash
|
|
1314
|
+
python scripts/time_travel.py list --thread-id <id>
|
|
1315
|
+
python scripts/time_travel.py fork --thread-id <id> --checkpoint-id <cp> \
|
|
1316
|
+
--field prazo_entrega --value "2026-09-15"
|
|
1317
|
+
python scripts/time_travel.py retention --days 30 # dry-run
|
|
1318
|
+
python scripts/time_travel.py retention --days 30 --purge --legal-hold <id>
|
|
1319
|
+
```
|
|
1320
|
+
|
|
1321
|
+
Objetos D5 são endereçados por SHA-256 e o modo legado permanece opt-in:
|
|
1322
|
+
`ORQUESTRADOR_LEGACY_CODE_STATE=true`.
|
|
1323
|
+
|
|
1324
|
+
## 16. Web escopada, qualidade e Batch
|
|
1325
|
+
|
|
1326
|
+
Configure `ORQUESTRADOR_WEB_ALLOWLIST` antes de habilitar o MCP web; sem
|
|
1327
|
+
allowlist, HTTPS, resolução pública e aprovação humana, o conteúdo é
|
|
1328
|
+
bloqueado. Para quality gates completos, use `docker/Dockerfile.quality`, que instala
|
|
1329
|
+
Playwright/Lighthouse e grava `data/quality-evidence.json`.
|
|
1330
|
+
|
|
1331
|
+
Submissão/consulta Batch:
|
|
1332
|
+
|
|
1333
|
+
```bash
|
|
1334
|
+
python scripts/batch_cli.py submit --prompt "health" --model deepseek/deepseek-v4-flash-0731
|
|
1335
|
+
python scripts/batch_cli.py status <batch-id>
|
|
1336
|
+
```
|
|
1337
|
+
|
|
1338
|
+
O workflow `.github/workflows/batch-submit.yml` usa somente modelos Groq Batch
|
|
1339
|
+
elegíveis e um GitHub Environment protegido.
|
|
1340
|
+
|
|
1341
|
+
## 17. OpenRouter opcional (multi-provedor e multimodal)
|
|
1342
|
+
|
|
1343
|
+
Groq continua sendo o padrão. Para habilitar OpenRouter, configure a chave
|
|
1344
|
+
somente no ambiente de execução (nunca em `GraphState`, registry ou logs):
|
|
1345
|
+
|
|
1346
|
+
```dotenv
|
|
1347
|
+
ORQUESTRADOR_LLM_PROVIDER=openrouter
|
|
1348
|
+
OPENROUTER_API_KEY=...
|
|
1349
|
+
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
|
|
1350
|
+
# opcional: OPENROUTER_HTTP_REFERER=https://seu-dominio
|
|
1351
|
+
# opcional: OPENROUTER_TITLE=Orquestrador
|
|
1352
|
+
# opcional: ORQUESTRADOR_OPENROUTER_TIMEOUT_SECONDS=90
|
|
1353
|
+
# compatibilidade legada, somente com ORQUESTRADOR_POLICY_LEGACY_OVERRIDES=true
|
|
1354
|
+
# ORQUESTRADOR_OPENROUTER_REASONING_EFFORT=low
|
|
1355
|
+
# ORQUESTRADOR_OPENROUTER_STRUCTURED_METHOD=json_schema
|
|
1356
|
+
# ORQUESTRADOR_OPENROUTER_REQUIRE_PARAMETERS=true
|
|
1357
|
+
# ORQUESTRADOR_OPENROUTER_STRUCTURED_RETRIES=1
|
|
1358
|
+
# opcional: ORQUESTRADOR_SMOKE_MAX_COMPLETION_TOKENS=16
|
|
1359
|
+
# opcional: ORQUESTRADOR_LLM_LOG_PREVIEW_CHARS=120 (0 desabilita previews)
|
|
1360
|
+
```
|
|
1361
|
+
|
|
1362
|
+
Para modelos marcados no catálogo local como compatíveis, o padrão é
|
|
1363
|
+
`json_schema` com `strict=true`. No E2E canônico,
|
|
1364
|
+
`ORQUESTRADOR_OPENROUTER_REQUIRE_PARAMETERS=true` também restringe o
|
|
1365
|
+
roteamento a endpoints que aceitem os parâmetros enviados. Use `json_mode`
|
|
1366
|
+
somente como override para um modelo/endpoint sem structured outputs nativos.
|
|
1367
|
+
|
|
1368
|
+
As chamadas diretas de OpenRouter (catálogo, mídia, TTS/STT, vídeo e smoke)
|
|
1369
|
+
usam o pacote oficial `openrouter==1.1.81`, com respostas tipadas e clientes
|
|
1370
|
+
síncrono/assíncrono. O LangGraph permanece sobre LangChain para preservar
|
|
1371
|
+
`Runnable`, saída estruturada, callbacks, ferramentas e o roteamento entre
|
|
1372
|
+
Groq, OpenRouter e demais provedores. O Batch é a única exceção: nessa versão
|
|
1373
|
+
do SDK o endpoint `/beta/batches` ainda não está exposto, portanto fica em um
|
|
1374
|
+
transporte HTTP privado e isolado.
|
|
1375
|
+
|
|
1376
|
+
O SDK não realiza novas tentativas por conta própria. Backoff, circuit breaker,
|
|
1377
|
+
limite de quatro tentativas e fallback continuam centralizados no orquestrador.
|
|
1378
|
+
O adapter aceita `max_tokens` apenas por compatibilidade e o converte para
|
|
1379
|
+
`max_completion_tokens`; código novo deve usar o segundo nome.
|
|
1380
|
+
|
|
1381
|
+
Cada chamada OpenRouter também emite logs reduzidos `[OPENROUTER] REQUEST`,
|
|
1382
|
+
`RESPONSE` e `ERROR`, com agente, modelo, tentativa, duração, hashes,
|
|
1383
|
+
tamanhos, tokens/cache, generation ID e erro sanitizado. Prompts e respostas
|
|
1384
|
+
completos não são gravados; o preview é limitado por
|
|
1385
|
+
`ORQUESTRADOR_LLM_LOG_PREVIEW_CHARS` (defina `0` em ambientes sensíveis).
|
|
1386
|
+
|
|
1387
|
+
Os limites físicos de cada modelo permanecem em `src/llm/llm_model_config.py`. Já
|
|
1388
|
+
os orçamentos operacionais por agente ficam em um único JSON:
|
|
1389
|
+
|
|
1390
|
+
```dotenv
|
|
1391
|
+
# Somente para migração de ambientes antigos; a fonte normal é
|
|
1392
|
+
# config/agent-policy.json e o mapa é ignorado sem opt-in explícito.
|
|
1393
|
+
ORQUESTRADOR_POLICY_LEGACY_OVERRIDES=true
|
|
1394
|
+
ORQUESTRADOR_AGENT_RUNTIME_JSON={"agent_clean_code":{"max_completion_tokens":8192,"reasoning_effort":"minimal","timeout_seconds":180,"structured_retries":1},"agent_code_review":{"max_completion_tokens":8192,"reasoning_effort":"minimal","timeout_seconds":180,"structured_retries":0}}
|
|
1395
|
+
ORQUESTRADOR_REVIEW_MAX_INPUT_CHARS=30000
|
|
1396
|
+
ORQUESTRADOR_RED_TEAM_MAX_INPUT_CHARS=30000
|
|
1397
|
+
```
|
|
1398
|
+
|
|
1399
|
+
Falhas do Planejador ou de Requisitos são bloqueantes. Primeiro,
|
|
1400
|
+
`structured_retries` repete apenas a chamada do agente. Se todas as tentativas
|
|
1401
|
+
falharem, o portão `critical_failure_gate` permite repetir somente esse nó a
|
|
1402
|
+
partir do checkpoint ou encerrar. `ORQUESTRADOR_CRITICAL_NODE_RETRIES` limita
|
|
1403
|
+
essas repetições manuais (padrão: `1`). Em CI, `auto_aprovar` escolhe encerrar;
|
|
1404
|
+
documentação ausente nunca é transformada em fallback aprovado.
|
|
1405
|
+
|
|
1406
|
+
Planejador e Consultor persistem seus resultados antes dos nós de aprovação.
|
|
1407
|
+
Assim, retomar um HITL não repete a chamada LLM. Clean Code, Code Review, Red
|
|
1408
|
+
Team e Compliance usam contratos compactos; fallback de um quality gate é
|
|
1409
|
+
registrado em `validacoes_fallback`, reprova o Compliance e faz o E2E real
|
|
1410
|
+
falhar explicitamente.
|
|
1411
|
+
|
|
1412
|
+
O dispatcher é orientado por fases: o roadmap normalizado atribui os agentes
|
|
1413
|
+
de implementação e o grafo executa `agent_dba` (persistência/contrato),
|
|
1414
|
+
`agent_backend` (API) e `agent_frontend` (interface) em etapas separadas,
|
|
1415
|
+
com fan-in e aprovação HITL entre elas. Code Review e Red Team só começam após
|
|
1416
|
+
a última fase; threads antigas sem a atribuição `agentes` mantêm o fan-out
|
|
1417
|
+
legado para compatibilidade.
|
|
1418
|
+
|
|
1419
|
+
O workflow `openrouter-graph-e2e.yml` define
|
|
1420
|
+
`ORQUESTRADOR_ITERATIVE_REPAIR=false`: ele valida uma rodada completa e
|
|
1421
|
+
encaminha achados ao Compliance sem repetir três ciclos de geração, que
|
|
1422
|
+
custariam três vezes mais para dizer a mesma coisa. O padrão está em
|
|
1423
|
+
`defaults.iterative_repair` e a variável o sobrepõe para uma corrida.
|
|
1424
|
+
|
|
1425
|
+
Antes isso era `ORQUESTRADOR_E2E_NO_REPAIR`, lida com literal dentro de
|
|
1426
|
+
`src/state/graph.py`. É deliberadamente independente de
|
|
1427
|
+
`ORQUESTRADOR_RUN_MODE`: amarrar as duas faria quem quer uma corrida mais
|
|
1428
|
+
barata pinar o provedor sem pedir, e tornaria impossível medir o próprio laço
|
|
1429
|
+
de reparação.
|
|
1430
|
+
|
|
1431
|
+
Rotas podem ser escolhidas por agente/perfil/thread em
|
|
1432
|
+
`ORQUESTRADOR_LLM_ROUTING_JSON`; `fallback_models` usa a cascata nativa do
|
|
1433
|
+
OpenRouter e `external_routes` define a cascata entre provedores. O limite é
|
|
1434
|
+
de quatro tentativas e erros de autenticação, payload, orçamento e segurança
|
|
1435
|
+
não fazem fallback.
|
|
1436
|
+
|
|
1437
|
+
A precedência é `thread/agente > thread/perfil > projeto/agente >
|
|
1438
|
+
projeto/perfil > defaults do projeto > ambiente`. O registry persiste apenas
|
|
1439
|
+
JSON de política; chaves permanecem exclusivamente em variáveis de ambiente.
|
|
1440
|
+
|
|
1441
|
+
### Claude Code como harness opcional
|
|
1442
|
+
|
|
1443
|
+
É possível adicionar o Claude Code/Anthropic Agent SDK como backend de
|
|
1444
|
+
execução dos agentes de código, mantendo a OpenRouter como gateway. A
|
|
1445
|
+
arquitetura recomendada é preservar o LangGraph como plano de controle
|
|
1446
|
+
(roteamento, HITL, orçamento, checkpoints, Compliance e D5) e delegar apenas
|
|
1447
|
+
uma tarefa de implementação isolada ao harness Claude Code, com workspace e
|
|
1448
|
+
tools permitidas explicitamente.
|
|
1449
|
+
|
|
1450
|
+
O processo filho receberá as credenciais somente em memória:
|
|
1451
|
+
|
|
1452
|
+
```dotenv
|
|
1453
|
+
ANTHROPIC_BASE_URL=https://openrouter.ai/api
|
|
1454
|
+
ANTHROPIC_AUTH_TOKEN=<valor de OPENROUTER_API_KEY injetado no processo>
|
|
1455
|
+
ANTHROPIC_API_KEY=
|
|
1456
|
+
```
|
|
1457
|
+
|
|
1458
|
+
O executor está disponível por política explícita. O workflow canônico usa
|
|
1459
|
+
`deepseek/deepseek-v4-flash-0731` através do protocolo Anthropic Skin. A
|
|
1460
|
+
documentação da OpenRouter garante formalmente o Claude Code apenas com
|
|
1461
|
+
modelos Anthropic first-party; DeepSeek, MiniMax e Kimi são portanto uma rota
|
|
1462
|
+
experimental do projeto e precisam de E2E próprio. A política de produção é:
|
|
1463
|
+
|
|
1464
|
+
```json
|
|
1465
|
+
{"agents":{"agent_backend":{"executor":"claude_code","model":"deepseek/deepseek-v4-flash-0731","fallback_models":["minimax/minimax-m3","moonshotai/kimi-k2.7-code"]},"agent_frontend":{"executor":"claude_code","model":"deepseek/deepseek-v4-flash-0731","fallback_models":["minimax/minimax-m3","moonshotai/kimi-k2.7-code"]}}}
|
|
1466
|
+
```
|
|
1467
|
+
|
|
1468
|
+
MiniMax M3 custa US$ 0,30/M de input e US$ 1,20/M de output; Kimi K2.7
|
|
1469
|
+
Code custa US$ 0,67/M de input e US$ 3,40/M de output (valores de catálogo,
|
|
1470
|
+
o custo efetivo vem do usage). Esses fallbacks só são habilitados quando
|
|
1471
|
+
`ORQUESTRADOR_EXECUTOR_FALLBACK_MODE=production`; workflows usam
|
|
1472
|
+
`disabled`, DeepSeek exclusivamente e no máximo três tentativas. Erros de
|
|
1473
|
+
payload, autenticação, orçamento, segurança e ownership nunca fazem fallback.
|
|
1474
|
+
|
|
1475
|
+
Documentação oficial: [Quickstart](https://openrouter.ai/docs/quickstart),
|
|
1476
|
+
[Client SDKs](https://openrouter.ai/docs/client-sdks/overview),
|
|
1477
|
+
[SDK Python](https://openrouter.ai/docs/client-sdks/python/overview),
|
|
1478
|
+
[model fallbacks](https://openrouter.ai/docs/guides/routing/model-fallbacks),
|
|
1479
|
+
[multimodal](https://openrouter.ai/docs/guides/overview/multimodal/overview),
|
|
1480
|
+
[Batch](https://openrouter.ai/docs/batch-quickstart),
|
|
1481
|
+
[prompt caching](https://openrouter.ai/docs/guides/best-practices/prompt-caching#caching-in-the-batch-api)
|
|
1482
|
+
e [MCP administrativo](https://openrouter.ai/docs/guides/overview/mcp-server).
|
|
1483
|
+
Para o harness, consulte [Claude Code com OpenRouter](https://openrouter.ai/docs/cookbook/coding-agents/claude-code-integration)
|
|
1484
|
+
e [Anthropic Agent SDK com OpenRouter](https://openrouter.ai/docs/guides/community/anthropic-agent-sdk).
|
|
1485
|
+
|
|
1486
|
+
Workflows manuais separados:
|
|
1487
|
+
|
|
1488
|
+
```text
|
|
1489
|
+
.github/workflows/openrouter-smoke.yml
|
|
1490
|
+
.github/workflows/openrouter-graph-e2e.yml
|
|
1491
|
+
.github/workflows/openrouter-batch-submit.yml
|
|
1492
|
+
.github/workflows/openrouter-batch-status.yml
|
|
1493
|
+
```
|
|
1494
|
+
|
|
1495
|
+
Configuração no GitHub:
|
|
1496
|
+
|
|
1497
|
+
1. Crie uma chave em [OpenRouter Keys](https://openrouter.ai/settings/keys).
|
|
1498
|
+
2. Em **Settings → Environments**, crie exatamente `openrouter-smoke`,
|
|
1499
|
+
`openrouter-graph-e2e` e `openrouter-batch`.
|
|
1500
|
+
3. Em cada Environment, adicione o secret **`OPENROUTER_API_KEY`**. Não use
|
|
1501
|
+
Repository variable nem coloque a chave no YAML. Se o Environment exigir
|
|
1502
|
+
aprovação, aprove a implantação antes da execução.
|
|
1503
|
+
4. Opcionalmente adicione as variables `OPENROUTER_HTTP_REFERER` e
|
|
1504
|
+
`OPENROUTER_TITLE` para identificação da aplicação.
|
|
1505
|
+
5. Em **Actions → workflow → Run workflow**, execute primeiro o smoke. Depois
|
|
1506
|
+
use o graph E2E. Batch exige quota/plano habilitado; submeta o lote e copie
|
|
1507
|
+
o `batch_id` impresso para o workflow de status.
|
|
1508
|
+
|
|
1509
|
+
Pela CLI do GitHub:
|
|
1510
|
+
|
|
1511
|
+
```bash
|
|
1512
|
+
gh secret set OPENROUTER_API_KEY --env openrouter-smoke --repo OWNER/REPO
|
|
1513
|
+
gh secret set OPENROUTER_API_KEY --env openrouter-graph-e2e --repo OWNER/REPO
|
|
1514
|
+
gh secret set OPENROUTER_API_KEY --env openrouter-batch --repo OWNER/REPO
|
|
1515
|
+
gh workflow run openrouter-smoke.yml --ref master -f model=deepseek/deepseek-v4-flash-0731
|
|
1516
|
+
gh run watch --exit-status
|
|
1517
|
+
```
|
|
1518
|
+
|
|
1519
|
+
O grafo E2E executa uma trilha por vez por padrão, reduzindo rate limit e
|
|
1520
|
+
duração. Escolha a trilha assim:
|
|
1521
|
+
|
|
1522
|
+
A trilha `new` do workflow usa deliberadamente o pedido fixo e pequeno
|
|
1523
|
+
`CRUD simples de produtos (código identificador e nome)`, para validar o
|
|
1524
|
+
fluxo do grafo e o Compliance sem transformar o teste em uma geração de
|
|
1525
|
+
produto grande. Ele pode ser sobrescrito apenas por `ORQUESTRADOR_E2E_CLIENT_INPUT`
|
|
1526
|
+
em execuções controladas.
|
|
1527
|
+
|
|
1528
|
+
Quando a trilha `new` não recebe `projeto_id` nem `mcp_roots`, o Arquiteto usa
|
|
1529
|
+
um contexto **greenfield** determinístico e não indexa o código-fonte do
|
|
1530
|
+
próprio orquestrador. Para evoluir um código existente desde o início, registre
|
|
1531
|
+
o projeto/base ou forneça um root MCP explícito; nesse caso a análise detalhada
|
|
1532
|
+
do repositório continua habilitada.
|
|
1533
|
+
|
|
1534
|
+
```bash
|
|
1535
|
+
gh workflow run openrouter-graph-e2e.yml --ref master \
|
|
1536
|
+
-f model=deepseek/deepseek-v4-flash-0731 -f tracks=new
|
|
1537
|
+
# Para executar todas: -f tracks=new,feature,fix
|
|
1538
|
+
```
|
|
1539
|
+
|
|
1540
|
+
O primeiro passo de cada workflow executa `test -n "$OPENROUTER_API_KEY"`; se
|
|
1541
|
+
falhar, o secret está no Environment errado, o Environment não foi aprovado ou
|
|
1542
|
+
o nome está diferente. A API usa `https://openrouter.ai/api/v1`, conforme o
|
|
1543
|
+
[Quickstart oficial](https://openrouter.ai/docs/quickstart), e os modelos
|
|
1544
|
+
devem ser IDs publicados no [catálogo](https://openrouter.ai/models).
|
|
1545
|
+
Para contas com crédito limitado, reduza opcionalmente
|
|
1546
|
+
`ORQUESTRADOR_OPENROUTER_MAX_COMPLETION_TOKENS`; quando ausente, cada modelo
|
|
1547
|
+
usa o limite registrado em `src/llm/llm_model_config.py`. Os workflows OpenRouter
|
|
1548
|
+
usam uma cascata paga de DeepSeek V4 Flash e Solar Pro4, evitando as cotas
|
|
1549
|
+
instáveis dos endpoints gratuitos.
|
|
1550
|
+
|
|
1551
|
+
O smoke registra modelo efetivo, generation ID, tokens, cache, custo e BYOK.
|
|
1552
|
+
O smoke usa `ORQUESTRADOR_SMOKE_MAX_COMPLETION_TOKENS` (16 por padrão, apenas
|
|
1553
|
+
para manter a chamada curta); `ORQUESTRADOR_SMOKE_MAX_TOKENS` continua aceito
|
|
1554
|
+
temporariamente. Ele não limita as execuções do grafo. No Batch, `max_tokens`
|
|
1555
|
+
é opcional por item: se omitido, o adapter não injeta um teto artificial do
|
|
1556
|
+
OpenRouter; informe-o somente quando quiser limitar aquele item.
|
|
1557
|
+
Batch OpenRouter aceita apenas texto, um modelo por lote e janela de 24 horas;
|
|
1558
|
+
simulação offline só é permitida com
|
|
1559
|
+
`ORQUESTRADOR_BATCH_OFFLINE_SIMULATION=true`.
|
|
1560
|
+
|
|
1561
|
+
Uploads multimodais são armazenados localmente por SHA-256 em D5. Imagem,
|
|
1562
|
+
PDF, áudio e vídeo são convertidos para as partes da API apenas durante a
|
|
1563
|
+
requisição; base64 e binários não entram em checkpoints. As APIs prontas para
|
|
1564
|
+
frontend são `/llm/*`, `/integrations/openrouter/mcp/*`, `/media/*`,
|
|
1565
|
+
`/batches` e `POST /threads`. Para OAuth administrativo por usuário configure
|
|
1566
|
+
`OIDC_ISSUER_URL`, `OIDC_AUDIENCE`, `OIDC_JWKS_URL` e
|
|
1567
|
+
`ORQUESTRADOR_CREDENTIAL_ENCRYPTION_KEY`.
|
|
1568
|
+
|
|
1569
|
+
## Planejamento especializado e executores agentic
|
|
1570
|
+
|
|
1571
|
+
O grafo separa planejamento e execução: requisitos, arquitetura e Clean Code
|
|
1572
|
+
alimentam os planos DBA, Backend e Frontend; uma validação determinística
|
|
1573
|
+
confere cobertura/propriedade; uma única aprovação HITL libera a ordem DBA →
|
|
1574
|
+
Backend → Frontend. Backend não pode possuir migrations e Frontend não pode
|
|
1575
|
+
inventar endpoint, SQL ou regra de negócio. Migrations que falham no banco
|
|
1576
|
+
efêmero não entram no manifesto D5.
|
|
1577
|
+
|
|
1578
|
+
O executor padrão é o **`kernel`**, declarado em `config/agent-policy.json`
|
|
1579
|
+
(`defaults.executor`). Os executores selecionáveis são `kernel`, `claude_code`,
|
|
1580
|
+
`pi` e `antigravity_sdk` (`defaults.selectable.executor`); `legacy` (o adapter
|
|
1581
|
+
LangChain) e `deepseek_harness` permanecem apenas como compatibilidade, em
|
|
1582
|
+
`COMPATIBILITY_EXECUTOR_NAMES` de `src/execution/executors.py`, e não são o
|
|
1583
|
+
padrão de nada. Claude Code pode ser ativado por política para Backend/Frontend
|
|
1584
|
+
e usa o SDK `claude-agent-sdk` com OpenRouter/Anthropic Skin. DeepSeek Harness é
|
|
1585
|
+
somente shadow/piloto em Linux, workspace isolado e sem aplicar artefatos ao
|
|
1586
|
+
workspace canônico. Se um executor explicitamente solicitado não estiver
|
|
1587
|
+
disponível, a execução é bloqueada; não há fallback silencioso.
|
|
1588
|
+
|
|
1589
|
+
Cada executor externo trabalha numa cópia descartável do workspace. Apenas
|
|
1590
|
+
arquivos textuais permitidos pela task card, pertencentes ao agente e dentro do
|
|
1591
|
+
orçamento são promovidos ao armazenamento D5. Remoções, binários, paths fora do
|
|
1592
|
+
plano e violações de propriedade bloqueiam a fase. Claude Code não recebe Bash,
|
|
1593
|
+
WebFetch, WebSearch ou NotebookEdit; os limites de contexto/conclusão do
|
|
1594
|
+
DeepSeek vêm de `src/llm/llm_model_config.py`, e não do arquivo Cordis.
|
|
1595
|
+
|
|
1596
|
+
O executor `pi` roda o loop do harness Pi (`@earendil-works/pi-agent-core` +
|
|
1597
|
+
`pi-ai`) num sidecar Node privado em `pi_harness/worker/`. Ele vem **desligado** em
|
|
1598
|
+
`config/agent-policy.json` (`pi_harness.enabled=false`) e, quando ligado,
|
|
1599
|
+
atende só os owners listados em `pi_harness.agents`. O sidecar não recebe
|
|
1600
|
+
credencial, Git, shell nem o workspace canônico: cada inferência volta para a
|
|
1601
|
+
fachada central em `src/llm/llm_provider.py` e cada ferramenta é revalidada contra
|
|
1602
|
+
o WorkPacket em `pi_harness/tools.py`. Instale o worker com
|
|
1603
|
+
`npm ci --ignore-scripts --prefix pi_harness/worker` e verifique o runtime com
|
|
1604
|
+
`python -m pi_harness.bridge`. Detalhes de operação em
|
|
1605
|
+
[`docs/planos/auditoria/operacao-pi-harness.md`](docs/planos/auditoria/operacao-pi-harness.md).
|
|
1606
|
+
|
|
1607
|
+
Instale os SDKs opcionais em Linux/Docker com
|
|
1608
|
+
`pip install -r requirements-agentic.txt`; a instalação padrão não os exige.
|
|
1609
|
+
As versões testadas são `claude-agent-sdk==0.2.128` e
|
|
1610
|
+
`deepseek-harness-sdk==0.1.1rc2`. O DeepSeek Harness não possui wheel do runtime
|
|
1611
|
+
para Windows e, por isso, seu piloto roda apenas em Linux.
|
|
1612
|
+
|
|
1613
|
+
Exemplo de política (sem credenciais; fallbacks somente em produção):
|
|
1614
|
+
|
|
1615
|
+
```json
|
|
1616
|
+
{"default":{"executor":"kernel"},"fallback_mode":"disabled","agents":{"agent_backend":{"executor":"claude_code","model":"deepseek/deepseek-v4-flash-0731","fallback_models":["minimax/minimax-m3","moonshotai/kimi-k2.7-code"],"shadow_executor":"pi"},"agent_frontend":{"executor":"claude_code","model":"deepseek/deepseek-v4-flash-0731","fallback_models":["minimax/minimax-m3","moonshotai/kimi-k2.7-code"],"shadow_executor":"pi"},"agent_dba":{"executor":"kernel"}}}
|
|
1617
|
+
```
|
|
1618
|
+
|
|
1619
|
+
MCPs são registrados por especialidade: Context7 (`resolve-library-id` e
|
|
1620
|
+
`query-docs`) para planejamento/desenvolvimento, 21st somente para Frontend e
|
|
1621
|
+
PostgreSQL somente leitura para o DBA. Configure `CONTEXT7_API_KEY`,
|
|
1622
|
+
`TWENTY_FIRST_API_KEY` e `MCP_POSTGRES_DSN` apenas no secret manager; o estado
|
|
1623
|
+
guarda somente hashes de evidência e capacidades liberadas.
|
|
1624
|
+
|
|
1625
|
+
O cliente MCP aplica a allowlist antes de cada chamada, protege conteúdo
|
|
1626
|
+
externo contra prompt injection e limita o texto retornado. No PostgreSQL, o
|
|
1627
|
+
modelo não recebe a ferramenta `connect` nem o DSN: o orquestrador conecta com
|
|
1628
|
+
um usuário read-only, entrega somente schema/query/EXPLAIN e desconecta. A
|
|
1629
|
+
validação de migrations usa separadamente `MCP_POSTGRES_VALIDATION_DSN`, que é
|
|
1630
|
+
aceito apenas para hosts allowlisted e bancos efêmeros. Para inspecionar a
|
|
1631
|
+
conectividade real use `GET /mcp/capabilities?probe=true`.
|
|
1632
|
+
|
|
1633
|
+
Claude Code recebe proxies MCP locais, não os servidores remotos brutos. Esses
|
|
1634
|
+
proxies publicam exclusivamente a allowlist read-only e reaplicam sanitização e
|
|
1635
|
+
Prompt Guard em cada resultado. Portanto, ferramentas novas ou mutantes que o
|
|
1636
|
+
21st venha a publicar não ficam automaticamente acessíveis ao executor.
|
|
1637
|
+
|
|
1638
|
+
Para testes de tela conduzidos por Jev e Playwright, inclusive a instalação do
|
|
1639
|
+
MCP Node via GitHub em outros harnesses, consulte
|
|
1640
|
+
[`docs/jev-browser-mcp.md`](docs/jev-browser-mcp.md). A escolha entre o browser
|
|
1641
|
+
headless do harness e Chrome/Edge visível fica em `config/ui-testing.json`; a
|
|
1642
|
+
chave do OpenRouter fica no ambiente do processo.
|
|
1643
|
+
|
|
1644
|
+
O sidecar PostgreSQL de teste pode ser validado localmente com:
|
|
1645
|
+
|
|
1646
|
+
```bash
|
|
1647
|
+
docker compose -f docker-compose.agentic.yml up -d --build
|
|
1648
|
+
python scripts/smoke_mcp.py postgres --rows
|
|
1649
|
+
python scripts/smoke_postgres_discovery.py --seed tests/fixtures/postgres_existing_schema.sql
|
|
1650
|
+
docker compose -f docker-compose.agentic.yml down -v
|
|
1651
|
+
```
|
|
1652
|
+
|
|
1653
|
+
`smoke_mcp.py postgres` lista as ferramentas publicadas; `--rows` chama
|
|
1654
|
+
`pg_query`/`pg_explain` de verdade e exige que o gate recuse a leitura de linhas
|
|
1655
|
+
sem consent `read_only_data`. Listar ferramenta não é exercitá-la: foi por isso
|
|
1656
|
+
que um nome de argumento errado sobreviveu a probes verdes.
|
|
1657
|
+
|
|
1658
|
+
`smoke_postgres_discovery.py` homologa AUD-034-DISCOVERY ponta a ponta —
|
|
1659
|
+
introspecta o schema físico por MCP, aplica `UP`/`DOWN` no banco efêmero,
|
|
1660
|
+
reintrospecta e compara os três snapshots. Sai com 0 somente quando a migration
|
|
1661
|
+
reversível é verificada **e** a irreversível é bloqueada. As duas DSNs são
|
|
1662
|
+
deliberadamente diferentes: `MCP_POSTGRES_DSN` é read-only e é a que a sessão
|
|
1663
|
+
MCP usa, `MCP_POSTGRES_VALIDATION_DSN` é a única que aplica migration e só é
|
|
1664
|
+
aceita para host allowlisted e banco efêmero.
|
|
1665
|
+
|
|
1666
|
+
Novas APIs autenticadas: `GET/PUT /execution/policies`,
|
|
1667
|
+
`GET /execution/executors` e `GET /mcp/capabilities`. `POST /threads` aceita
|
|
1668
|
+
`execution_policy` por thread. Workflows de rollout:
|
|
1669
|
+
|
|
1670
|
+
```text
|
|
1671
|
+
.github/workflows/agentic-offline.yml
|
|
1672
|
+
.github/workflows/mcp-integration-smoke.yml
|
|
1673
|
+
.github/workflows/agentic-graph-e2e.yml
|
|
1674
|
+
.github/workflows/deepseek-harness-eval.yml
|
|
1675
|
+
.github/workflows/executor-benchmark.yml
|
|
1676
|
+
.github/workflows/skills-offline.yml
|
|
1677
|
+
```
|
|
1678
|
+
|
|
1679
|
+
### Skills por agente
|
|
1680
|
+
|
|
1681
|
+
O catálogo canônico fica em `.agents/skills`. A descoberta retorna apenas
|
|
1682
|
+
nome, descrição, origem e hash; corpo e referências são carregados somente
|
|
1683
|
+
quando a política do agente autoriza a skill e a stack é compatível. O estado,
|
|
1684
|
+
relatório e logs recebem somente `skill_catalog_digest` e hashes de evidência.
|
|
1685
|
+
Campos `model`, `provider`, `tools`, credenciais e orçamento encontrados em
|
|
1686
|
+
frontmatter são inertes: a rota continua sendo resolvida exclusivamente pela
|
|
1687
|
+
política LLM/execution policy.
|
|
1688
|
+
|
|
1689
|
+
Exemplo para ativar `frontend-design` no Designer UI e Remotion no Frontend/Code Review:
|
|
1690
|
+
|
|
1691
|
+
```json
|
|
1692
|
+
{
|
|
1693
|
+
"default": {"executor": "kernel", "skills": [], "skill_mode": "disabled"},
|
|
1694
|
+
"agents": {
|
|
1695
|
+
"agent_ui_designer": {"skills": ["frontend-design"], "skill_mode": "required"},
|
|
1696
|
+
"agent_frontend": {"skills": ["frontend-design", "remotion-best-practices"], "skill_mode": "auto"},
|
|
1697
|
+
"agent_code_review": {"skills": ["remotion-best-practices"], "skill_mode": "auto"}
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
```
|
|
1701
|
+
|
|
1702
|
+
Valide o bundle e seu lockfile sem rede:
|
|
1703
|
+
|
|
1704
|
+
```bash
|
|
1705
|
+
python scripts/validate_skills.py
|
|
1706
|
+
python -m pytest -q tests/test_skill_registry.py
|
|
1707
|
+
```
|
|
1708
|
+
|
|
1709
|
+
As APIs autenticadas `GET /skills`, `GET /skills/{name}`,
|
|
1710
|
+
`GET /skills/capabilities`, `GET/PUT /skills/policies` e
|
|
1711
|
+
`POST /skills/validate` nunca retornam o corpo da skill. Instalação remota fica
|
|
1712
|
+
desabilitada até existir preview/diff, scanner, aprovação HITL e atualização
|
|
1713
|
+
atômica do lockfile. `find-skills` é reservado ao futuro curador administrativo
|
|
1714
|
+
e não é liberado aos desenvolvedores.
|
|
1715
|
+
|
|
1716
|
+
Crie o Environment protegido `agentic-e2e`, exija aprovação manual e cadastre
|
|
1717
|
+
`OPENROUTER_API_KEY`, `CONTEXT7_API_KEY` e uma `TWENTY_FIRST_API_KEY` nova. A
|
|
1718
|
+
ordem de rollout é: `agentic-offline`, `mcp-integration-smoke`,
|
|
1719
|
+
`agentic-graph-e2e`, `deepseek-harness-eval` e, por fim,
|
|
1720
|
+
`executor-benchmark`. Os três últimos são validações reais/manuais: não devem
|
|
1721
|
+
ser tratados como aprovados somente porque a suíte offline passou. Consulte os
|
|
1722
|
+
guias oficiais:
|
|
1723
|
+
[Claude Code com OpenRouter](https://openrouter.ai/docs/guides/coding-agents/claude-code-integration),
|
|
1724
|
+
[Claude Agent SDK](https://github.com/anthropics/claude-agent-sdk-python),
|
|
1725
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/guide/python-sdk.md),
|
|
1726
|
+
[Context7](https://github.com/upstash/context7),
|
|
1727
|
+
[21st MCP](https://github.com/21st-dev/magic-mcp) e
|
|
1728
|
+
[pg-mcp-server](https://github.com/stuzero/pg-mcp-server).
|