pantheon-opencode 1.6.0-beta.1 → 1.6.0-beta.11

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.
Files changed (133) hide show
  1. package/AGENTS.global.md +206 -0
  2. package/AGENTS.md +10 -891
  3. package/CHANGELOG.md +350 -0
  4. package/README.md +561 -52
  5. package/README.pt-BR.md +285 -20
  6. package/bin/pantheon-init.mjs +70 -4
  7. package/commands/pantheon-audit.md +7 -1
  8. package/commands/pantheon-consolidate.md +1 -1
  9. package/commands/pantheon-deepwork.md +14 -3
  10. package/commands/pantheon-focus.md +17 -0
  11. package/commands/pantheon-vision.md +61 -12
  12. package/package.json +24 -17
  13. package/plugin.json +2 -2
  14. package/pyproject.toml +8 -6
  15. package/scripts/build-agents-md.mjs +428 -56
  16. package/scripts/changelog-merge.mjs +161 -0
  17. package/scripts/{code_mode_server.py → code_mode.py} +3 -3
  18. package/scripts/cost.mjs +244 -41
  19. package/scripts/doctor.mjs +151 -1
  20. package/scripts/install/agents-md.mjs +4 -4
  21. package/scripts/install/backup.mjs +475 -0
  22. package/scripts/install/cli-ui.mjs +6 -1
  23. package/scripts/install/config-migration.mjs +317 -4
  24. package/scripts/install/exit-codes.mjs +26 -0
  25. package/scripts/install/health-check.mjs +21 -19
  26. package/scripts/install/interactive.mjs +16 -2
  27. package/scripts/install/migrate.mjs +41 -22
  28. package/scripts/install/opencode-version.mjs +246 -4
  29. package/scripts/install/opencode.mjs +1175 -215
  30. package/scripts/install/plan.mjs +287 -0
  31. package/scripts/install/plugin.mjs +62 -14
  32. package/scripts/install/shared.mjs +190 -48
  33. package/scripts/install/strings.mjs +20 -0
  34. package/scripts/install/venv.mjs +13 -3
  35. package/scripts/install/vision-prompt.mjs +171 -0
  36. package/scripts/install-mcp.mjs +28 -9
  37. package/scripts/{mcp_persistence_server.py → mcp_persistence.py} +52 -17
  38. package/{src/mcp/mcp_resources_server.py → scripts/mcp_resources.py} +2 -2
  39. package/scripts/{memory_mcp_server.py → memory_mcp.py} +2 -2
  40. package/scripts/probe-context-rehydrate.mjs +8 -2
  41. package/scripts/probe-pantheon-cost.mjs +71 -15
  42. package/scripts/prune.mjs +9 -3
  43. package/scripts/restore.mjs +222 -0
  44. package/scripts/run-node-tests.mjs +189 -0
  45. package/scripts/sync-artifacts.mjs +26 -13
  46. package/scripts/sync-tui.mjs +24 -3
  47. package/scripts/test-opencode-v2-sandbox.sh +180 -26
  48. package/scripts/validate-routing.mjs +3 -3
  49. package/scripts/validate-sandbox-install.mjs +138 -0
  50. package/scripts/versioning.mjs +13 -1
  51. package/scripts/vision-status.mjs +682 -0
  52. package/skills-lock.json +8 -8
  53. package/src/agents/aphrodite.md +20 -37
  54. package/src/agents/apollo.md +7 -3
  55. package/src/agents/athena.md +63 -35
  56. package/src/agents/demeter.md +8 -28
  57. package/src/agents/gaia.md +1 -1
  58. package/src/agents/hephaestus.md +10 -21
  59. package/src/agents/hermes.md +15 -38
  60. package/src/agents/iris.md +7 -1
  61. package/src/agents/mnemosyne.md +18 -54
  62. package/src/agents/nyx.md +1 -1
  63. package/src/agents/prometheus.md +16 -20
  64. package/src/agents/talos.md +10 -2
  65. package/src/agents/themis.md +5 -5
  66. package/src/agents/zeus.md +99 -172
  67. package/src/instructions/agent-return-format.instructions.md +34 -17
  68. package/src/instructions/backend-standards.instructions.md +2 -0
  69. package/src/instructions/frontend-standards.instructions.md +3 -1
  70. package/src/instructions/memory-protocol.instructions.md +12 -110
  71. package/src/instructions/test-economy.instructions.md +21 -0
  72. package/src/instructions/zeus-anti-stall.instructions.md +20 -67
  73. package/src/instructions/zeus-communication-rules.instructions.md +0 -1
  74. package/src/instructions/zeus-council-synthesis.instructions.md +31 -260
  75. package/src/instructions/zeus-memory-operations.instructions.md +30 -0
  76. package/src/instructions/zeus-timeout-retry.instructions.md +61 -89
  77. package/src/mcp/{code_mode_server.py → code_mode.py} +3 -3
  78. package/src/mcp/eval_store.py +2 -2
  79. package/src/mcp/{mcp_persistence_server.py → mcp_persistence.py} +52 -17
  80. package/{scripts/mcp_resources_server.py → src/mcp/mcp_resources.py} +2 -2
  81. package/src/mcp/{memory_mcp_server.py → memory_mcp.py} +2 -2
  82. package/src/mcp/{pantheon_vision_server.py → pantheon_vision.py} +286 -42
  83. package/src/mcp/requirements-mcp.txt +6 -4
  84. package/src/mcp/requirements-vision.txt +2 -2
  85. package/src/pantheon/cost-command.ts +403 -102
  86. package/src/pantheon/delegation-enforce.ts +10 -1
  87. package/src/pantheon/model-agents.ts +19 -0
  88. package/src/pantheon/model-command.ts +218 -56
  89. package/src/pantheon/presets.d.mts +0 -8
  90. package/src/pantheon/presets.mjs +5 -32
  91. package/src/pantheon/v2-bounded-read.ts +640 -0
  92. package/src/pantheon/v2-delegation-enforce.ts +214 -0
  93. package/src/pantheon/v2-execute-after.ts +186 -0
  94. package/src/pantheon/v2-model-overrides.ts +275 -0
  95. package/src/pantheon/v2-tools.ts +418 -0
  96. package/src/pantheon/v2-unsupported.d.mts +11 -0
  97. package/src/pantheon/v2-unsupported.mjs +90 -0
  98. package/src/pantheon/v2-vision.ts +622 -0
  99. package/src/pantheon/vision-local.ts +531 -0
  100. package/src/pantheon/vision.ts +1160 -115
  101. package/src/plugin-v2.ts +1158 -219
  102. package/src/plugin.ts +12 -9
  103. package/src/plugins/tui/dist/server.d.ts +1 -2
  104. package/src/plugins/tui/dist/server.d.ts.map +1 -1
  105. package/src/plugins/tui/dist/tui-v2.d.ts +154 -0
  106. package/src/plugins/tui/dist/tui-v2.d.ts.map +1 -0
  107. package/src/plugins/tui/dist/tui-v2.js +659 -0
  108. package/src/plugins/tui/dist/tui-v2.js.map +1 -0
  109. package/src/plugins/tui/dist/tui.d.ts +85 -82
  110. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  111. package/src/plugins/tui/dist/tui.js +72 -59
  112. package/src/plugins/tui/index-v2.ts +6 -0
  113. package/src/plugins/tui/package-lock.json +5516 -1353
  114. package/src/plugins/tui/package.json +22 -12
  115. package/src/plugins/tui/src/index-v2.tsx +734 -0
  116. package/src/plugins/tui/src/index.tsx +106 -85
  117. package/src/plugins/tui/src/opencode-v2.d.ts +13 -0
  118. package/src/plugins/tui/tsconfig.build.json +5 -0
  119. package/src/plugins/tui/tsconfig.typecheck.json +5 -0
  120. package/src/plugins/tui/tsdown.config.ts +22 -2
  121. package/src/plugins/tui/tui-v2.ts +2 -0
  122. package/src/plugins/tui/tui.ts +3 -0
  123. package/src/routing.yml +11 -21
  124. package/src/skills/README.md +3 -3
  125. package/src/skills/agent-coordination/SKILL.md +7 -2
  126. package/src/skills/artifact-management/SKILL.md +12 -5
  127. package/src/skills/auto-continue/SKILL.md +52 -35
  128. package/src/skills/code-review-checklist/SKILL.md +5 -5
  129. package/src/skills/context-compression/SKILL.md +26 -879
  130. package/src/skills/session-goal/SKILL.md +14 -5
  131. package/src/skills/spec-driven-development/SKILL.md +3 -1
  132. package/src/skills/tdd-with-agents/SKILL.md +18 -10
  133. package/opencode.json +0 -58
package/README.pt-BR.md CHANGED
@@ -53,6 +53,10 @@ Com o OpenCode em execução, descreva o resultado que você quer:
53
53
  ```
54
54
 
55
55
  O Pantheon ajuda a transformar esse pedido em um plano e em etapas revisadas.
56
+ Quando você pede um plano ou inicia `/pantheon-deepwork`, Athena devolve o plano
57
+ completo para Zeus, que o apresenta na conversa principal antes de pedir
58
+ aprovação. O pedido de aprovação não ficará separado de um plano escondido na
59
+ conversa de um subagente.
56
60
 
57
61
  ## Para quem é?
58
62
 
@@ -122,16 +126,137 @@ registrados juntos.
122
126
  |---|---|---|
123
127
  | Chave de config do OpenCode | `plugin` singular | `plugins` plural |
124
128
  | Registro Pantheon | `src/plugin.ts` mais `src/plugins/pantheon-hooks.ts` | `pantheon-opencode/plugin-v2` (`src/plugin-v2.ts`) |
125
- | Contrato de runtime | Plugin Pantheon V1: 6 ferramentas (`hashline_edit`, as 3 ferramentas de goal, `pantheon_cost`, `pantheon_model`), hooks de evento/ferramenta e tratamento de compactação V1 | Plugin V2 completo: 6 ferramentas de orquestração, 4 assinaturas de eventos, session hooks (`prompt`, `context`), tool hooks (`execute.before`/`after`), além de transforms de configuração |
129
+ | Contrato de runtime | Plugin Pantheon V1: 6 ferramentas (`hashline_edit`, as 3 ferramentas de goal, `pantheon_cost`, `pantheon_model`), hooks de evento/ferramenta e tratamento de compactação V1 | Plugin V2 completo: 3 ferramentas de orquestração mais `vision_describe` quando o catálogo de modelos está disponível, 5 assinaturas de eventos, session hooks (`prompt`, `context`), um hook `execute.before` que impõe sessões somente-leitura, além de transforms de configuração |
126
130
  | APIs V1 | Registradas | Definições próprias de ferramentas via `ctx.tool.transform()` — não pelo caminho do plugin V1 |
127
131
 
128
- O plugin V2 fornece 6 ferramentas de orquestração (`hashline_edit`,
129
- `pantheon_goal_create`, `pantheon_goal_get`, `pantheon_goal_update`,
132
+ O plugin V2 fornece 3 ferramentas de orquestração (`hashline_edit`,
130
133
  `pantheon_cost`, `pantheon_model`), 4 assinaturas de eventos (`session.created`,
131
134
  `session.idle`, `session.error`, `session.compacted`), session hooks (`prompt`,
132
- `context`) e tool hooks (`execute.before`, `execute.after`). Os recursos V2 sem
133
- suporte são `legacy-hooks` (a superfície de hooks específica do V1),
134
- `catalog-transform`, `integration-transform` e `skill-transform`.
135
+ `context`) e um hook de ferramenta `execute.before` que impõe sessões
136
+ somente-leitura. As 3 ferramentas de goal **não** são
137
+ registradas no V2: o goal loop precisa de um `GoalStore`, de um
138
+ `GoalLoopClient` e de um `BackgroundJobBoard`, nenhum dos quais o
139
+ `PluginContext` do V2 expõe, então a lacuna é reportada como o marcador de
140
+ recurso sem suporte `goal-tools`. Use o contrato V1 se precisar do goal loop.
141
+
142
+ O V2 também registra `vision_describe` quando `ctx.model.list()` está
143
+ disponível. A ferramenta verifica a entrada de imagem no catálogo atual,
144
+ prioriza o modelo pago `opencode-go/mimo-v2.6-flash` (modelos free-tier são
145
+ excluídos da seleção automática — o gateway direto os rejeita com HTTP 403, só
146
+ funcionam de dentro do OpenCode; um pin explícito em
147
+ `PANTHEON_VISION_MODEL`/config/preset continua sendo honrado, então seu 403 vira
148
+ um erro acionável em vez de uma troca silenciosa) e envia a imagem
149
+ diretamente ao endpoint autenticado do OpenCode Zen. A requisição carrega o
150
+ session id vivo no header Zen-only `x-opencode-session` exigido pelo gateway
151
+ (nunca enviado a um provider não-Zen, e nunca logado). As credenciais vêm de
152
+ `opencode auth login` ou `PANTHEON_OPENCODE_API_KEY`; sem modelo compatível ou
153
+ credenciais, a ferramenta retorna um erro explícito em vez de inventar uma
154
+ descrição. Esse caminho funciona sem a ponte V1 opcional.
155
+ O hook de prompt V2 também preserva as referências dos anexos e orienta Zeus a
156
+ inspecionar imagens relevantes e repassar a referência e observações concisas a
157
+ Aphrodite. Bytes inline não são copiados para o texto persistido do prompt, e a
158
+ imagem não autoriza apagar nem reverter alterações fora do escopo.
159
+
160
+ O caminho nativo do turno de chat escolhe o modelo de visão numa precedência
161
+ fixa: a variável `PANTHEON_VISION_MODEL`, depois `visionModel` em
162
+ `opencode-vision.json`, depois `vision.model` do preset ativo, depois descoberta
163
+ automática no catálogo de providers do host (`GET /config/providers`) e, por
164
+ fim, o padrão embutido. A descoberta é conservadora e somente paga: mantém
165
+ apenas entradas com imagem (`capabilities.input.image`) cujo provider é Zen
166
+ confiável ou cujo pacote `npm` é OpenAI-compatível (`@ai-sdk/openai-compatible` /
167
+ `@ai-sdk/openai`), EXCLUI entradas free-tier (`-free` ou `cost.input <= 0` — o
168
+ gateway direto as rejeita com HTTP 403), escolhe a entrada mais barata restante
169
+ e faz cache do catálogo por 60
170
+ segundos por processo; um modelo incompatível simplesmente cai no fallback da
171
+ tool MCP de visão. A base URL vem do `api.url` da entrada do catálogo, e os
172
+ endpoints Zen legados permanecem como fallback quando não há catálogo. As
173
+ credenciais seguem o endpoint: para um endpoint Zen, as variáveis compartilhadas
174
+ `PANTHEON_OPENCODE_API_KEY` / `OPENCODE_API_KEY` são tentadas primeiro e depois o
175
+ auth store do OpenCode (o provider resolvido primeiro, depois as entradas legadas
176
+ `opencode-go` / `opencode`); para um endpoint não-Zen, apenas a entrada do próprio
177
+ provider no auth store é usada — as variáveis compartilhadas e as entradas Zen
178
+ legadas nunca são anexadas a uma base URL não-Zen. Um modelo de visão
179
+ configurado explicitamente e qualificado sempre vence a descoberta — um pin que
180
+ não qualifica (não-Zen, text-only ou fora do catálogo) é descartado e a seleção
181
+ automática assume. O `pantheon-opencode init` oferece um
182
+ passo opcional de vision em sessões TTY interativas — a descoberta automática é
183
+ o default e não grava nada; pinar faz merge de `visionModel` no
184
+ `opencode-vision.json` global; fluxos não-interativos nunca perguntam nem gravam.
185
+
186
+ A pergunta do usuário é injetada no prompt de visão nos caminhos nativo e V2,
187
+ aparada e limitada a 2000 caracteres; a mesma imagem com uma pergunta diferente
188
+ gera nova descrição, enquanto uma repetição idêntica dentro do TTL de cache
189
+ reutiliza o texto guardado. O describe nativo é robusto por construção: o
190
+ timeout escala de uma base de 20 segundos a cerca de 10 segundos por MB de
191
+ payload até um teto de 300 segundos, e uma checagem de orientação EXIF sem
192
+ dependências (JPEG) adiciona uma dica textual para o vision model descrever a
193
+ imagem rotacionada ou espelhada na posição correta — só detecção, os pixels
194
+ nunca são reescritos, e a orientação entra na chave do cache.
195
+
196
+ A visão degrada numa cadeia explícita — nativa → tool MCP de visão → local.
197
+ Quando a nativa está indisponível e a tool MCP de visão não está presente, a
198
+ imagem é substituída por extração local determinística: dimensões, formato e
199
+ tamanho do header PNG/JPEG/GIF/WebP, cores dominantes para PNG RGB/RGBA e OCR
200
+ somente quando o binário `tesseract` está no `PATH`. Nunca fabrica conteúdo — a
201
+ degradação é declarada explicitamente. As tools `vision_describe` e `vision_ocr`
202
+ do MCP degradam para esse modo local quando não há credencial de gateway, sem
203
+ dependências novas. (A `vision_analyze` exige credencial de gateway e retorna um
204
+ erro explícito.) Trade-offs deliberados, registrados para não serem
205
+ revertidos por acidente: o Pantheon descreve cada imagem uma vez e mantém o
206
+ texto no histórico (council 2026-08-03) em vez de preservar a imagem original
207
+ para re-observação a cada turno; o describe-always prioriza latência zero em vez
208
+ de uma tool `describe_image` sob demanda. A sanitização multi-turno
209
+ (`experimental.chat.messages.transform`) é invariante obrigatória — garante que
210
+ `image_url` / `FilePart` nunca cheguem a um provider textual e não é
211
+ configurável. O Bifrost permanece opt-in, nunca padrão, conforme o incidente de
212
+ credencial em [SECURITY.md](SECURITY.md).
213
+
214
+ O hook `execute.before` do V2 é o ponto de imposição de sessões somente-leitura
215
+ dessa superfície: o host inclui o agente ativo no evento, um agente somente-leitura
216
+ (`apollo`, `gaia`) registra sua sessão, e o `createEnforcementGuard`
217
+ compartilhado lança para negar `edit`, `write`, `bash`, `task`,
218
+ `hashline_edit` e `pantheon_model`. Ele **não** depende do hook
219
+ `tool.execute.before` do V1, que vive em `src/plugin.ts` e não é carregado
220
+ quando apenas `plugin-v2` está configurado. Quando o host registra o hook
221
+ `permission.evaluate`, ele aplica no V2 a matriz caller/target a todos os itens
222
+ do array `resources` do host para destinos gerenciados pelo Pantheon. Sem o
223
+ registro do hook, a matriz não fica disponível e o host mantém o controle.
224
+ Recursos exclusivamente nativos são repassados sem alteração à política de
225
+ permissões do OpenCode; pedidos mistos (nativos/Pantheon) ou com vários destinos
226
+ são negados.
227
+ Para destinos Pantheon, a política exige uma chamada nativa `subagent` cujo
228
+ session ID, message ID e tool-call ID no `source` coincidam com o evento do host
229
+ `tool.execute.before`; também confirma que a sessão não tem `parentID`. O campo
230
+ `permission.evaluate.agent`, sozinho, não identifica o chamador. Origins
231
+ ausentes, expirados, repetidos ou incompatíveis são negados. A canary no
232
+ OpenCode v2.0.29 admite Zeus→Apollo na raiz e nega dispatches inválidos ou
233
+ originados em uma filha; veja o [relatório de F4A](bench/results/f4a-v229-admission-identity-2026-10-14.md).
234
+ Uma negação explícita do host é final, e dados de recursos ausentes ou
235
+ desconhecidos são negados por padrão. O `execute.after` do
236
+ V2 executa a cadeia de resultados concluídos `task-result-guard` →
237
+ `context-sandbox` → `read-enhancer`; eventos de erro permanecem inalterados.
238
+
239
+ #### Leitura bounded do workspace (F3b)
240
+
241
+ No OpenCode v2.0.29, o plugin V2 pode registrar `pantheon_bounded_read` quando
242
+ o host oferece as APIs nativas de permissão e sessão. Só uma sessão root do
243
+ Zeus pode usá-la. Cada chamada pede ao host permissão `read` para um arquivo
244
+ relativo exato do workspace, com escopo de persistência vazio; o conteúdo só é
245
+ lido após aprovação e passa pelo scanner de segredos fail-closed existente.
246
+ Os limites são duas chamadas e dois arquivos distintos, 400 linhas e 32 KiB
247
+ por mensagem do assistant. Travessia de diretórios, symlinks, diretórios,
248
+ escopos sensíveis, formatos binários/mídia reconhecidos e falhas do scanner são
249
+ negados. O escopo suportado é um workspace local sem escritores concorrentes;
250
+ isso não promete isolamento contra filesystem adversarial nem classificação
251
+ binária do arquivo inteiro. Evidência host-backed:
252
+ [`bench/results/f3b-v229-bounded-read-2026-10-15.md`](bench/results/f3b-v229-bounded-read-2026-10-15.md).
253
+
254
+ Toda ferramenta V2 declara um schema `output`. O OpenCode 2.0.x exige que a
255
+ declaração e o resultado resolvido concordem nos dois sentidos, então uma
256
+ ferramenta sem declaração — ou que retorne `output` sem declará-lo — falha em
257
+ toda chamada. Os recursos V2 sem suporte são `legacy-hooks` (a superfície de
258
+ hooks específica do V1), `catalog-transform`, `integration-transform`,
259
+ `skill-transform` e `goal-tools`.
135
260
 
136
261
  O pacote expõe os dois contratos como exports importáveis:
137
262
  `pantheon-opencode/plugin` (V1), `pantheon-opencode/plugin-v2` (V2) e
@@ -151,26 +276,68 @@ npx pantheon-opencode init --opencode-version v2
151
276
  npx pantheon-opencode init --opencode-version auto
152
277
  ```
153
278
 
154
- `--version v1|v2|auto` é aceito como a grafia antiga do seletor quando usado
155
- depois de `init`. `auto` é conservador, não uma autodetecção geral de
156
- plataforma: `OPENCODE_VERSION=v1|v2` vence; caso contrário, um `OPENCODE_BIN`
157
- terminando em `opencode2` seleciona V2; qualquer outro caso seleciona V1. O
158
- instalador remove referências Pantheon das duas formas de config antes de
279
+ `auto` é o padrão. `--version v1|v2|auto` é aceito como a grafia antiga do seletor
280
+ quando usado depois de `init`. `auto` resolve a geração a partir do host nesta
281
+ ordem: um `OPENCODE_VERSION=v1|v2` explícito vence; caso contrário, um
282
+ `OPENCODE_BIN` terminando em `opencode2` seleciona V2; caso contrário o binário
283
+ do host é consultado com `--version` e uma major >= 2 seleciona V2. Qualquer
284
+ outro caso — uma sonda ilegível, um banner não interpretável, ou um banner
285
+ cujos tokens de versão se contradizem sem um nome de ferramenta que desfaça o
286
+ empate — avisa uma vez e volta para V1, porque uma entrada de diretório em
287
+ `plugins` plural em um host 1.x desconhecido perde o plugin inteiro.
288
+
289
+ A sonda prefere o token que vem logo após o nome da ferramenta, então um token
290
+ de runtime antes dele (`node v22.1.0 (opencode 1.18.33)`) ou uma data de build
291
+ no final (`opencode v1.18.33 built 2026.10.04`) não conseguem virar a geração.
292
+ O instalador remove referências Pantheon das duas formas de config antes de
159
293
  gravar apenas o registro Pantheon selecionado. Entradas de terceiros não são
160
294
  convertidas nem reivindicadas por essa regra.
161
295
 
162
- O relatório `pantheon_cost`, disponível nos plugins V1 e V2, pode selecionar o banco com
163
- `PANTHEON_OPENCODE_VERSION=v1` ou `v2` (`opencode.db` ou `opencode-v2.db`).
164
- `PANTHEON_COST_DB=/caminho/absoluto/para/opencode.db` tem precedência sobre o
165
- seletor de versão, e um `dbPath` explícito fornecido pelo chamador da
166
- ferramenta tem precedência sobre ambos. O resolver nunca consulta o banco da
167
- outra versão e reporta um erro acionável quando o banco selecionado está
168
- ausente ou com schema incompatível.
296
+ O relatório `pantheon_cost`, disponível nos plugins V1 e V2, resolve o banco por
297
+ CAMINHO, nesta ordem: um `dbPath` explícito fornecido pelo chamador da
298
+ ferramenta, depois `PANTHEON_COST_DB=/caminho/absoluto/para/opencode.db`,
299
+ depois `OPENCODE_DB`, e por fim o padrão do XDG `opencode.db`. Não há nome de
300
+ arquivo por versão nessa cadeia: `opencode-v2.db` não é um banco de um host 2.x
301
+ — é o nome que um sandbox dá ao próprio banco de estado via `OPENCODE_DB`.
302
+
303
+ A distinção v1/v2 é o SCHEMA DETECTADO, não o nome do arquivo. O relatório
304
+ procura as tabelas `message` e `session_message` e lê aquele que o banco aberto
305
+ realmente tiver, porque um banco migrado carrega as duas famílias ao mesmo
306
+ tempo. `PANTHEON_OPENCODE_VERSION=v1|v2` não seleciona arquivo nenhum: apenas
307
+ estreita um conjunto já detectado para uma família de tabelas, e falha rápido
308
+ quando o valor não é `v1` nem `v2` ou quando a família pedida não existe lá.
309
+ Sem a variável, todas as famílias detectadas são lidas. Um banco sem nenhuma
310
+ das duas tabelas volta como erro acionável (`CORRUPT_DATA`), e um ledger que
311
+ existe mas não produz tokens legíveis como `UNSUPPORTED` — nunca como um
312
+ relatório vazio com sucesso.
169
313
 
170
314
  O instalador continua gravando as configurações de compatibilidade exigidas
171
315
  pelo host OpenCode selecionado, como `experimental.subagent_depth`; isso não
172
316
  converte um plugin V1 em V2 nem dá hooks V1 ao V2.
173
317
 
318
+ O Pantheon não define mais um teto de passos para os agentes. No OpenCode 2 o
319
+ campo `steps` é opcional e não tem default: ausente significa que o host não
320
+ aplica nenhum limite nativo. Uma config escrita antes dessa mudança ainda
321
+ carrega o valor antigo, e o caminho de instalação v2 o remove de todos os
322
+ agentes que o Pantheon gerencia — um agente definido por você nunca é tocado,
323
+ e o bloco V1 singular `agent`, em retirada, é deixado intacto. Rode o
324
+ instalador uma vez para limpar um valor antigo de uma config existente:
325
+
326
+ ```bash
327
+ npx pantheon-opencode init --opencode-version=v2
328
+ ```
329
+
330
+ O controle de contexto não é uma configuração do Pantheon. Qualquer orçamento
331
+ que um agente terá vem das settings de compactação do próprio host OpenCode,
332
+ e o Pantheon não as define.
333
+
334
+ Quando um bloco V1 `agent` e um bloco V2 `agents` coexistem — o que acontece
335
+ em qualquer `opencode.json` já instalado — a conversão V1→V2 mescla os dois em
336
+ vez de deixar um substituir o outro. O bloco V2 é a base, os campos
337
+ gerenciados que vêm do V1 sobrescrevem, e os seus próprios campos são
338
+ preservados, com a mesma precedência que o instalador já aplica em outros
339
+ pontos. A ordem das chaves não muda o resultado.
340
+
174
341
  ## Atualizando entre releases (beta.5+)
175
342
 
176
343
  Um comando mantém uma instalação existente em dia:
@@ -193,7 +360,15 @@ pacote. Duas garantias de freshness sustentam isso:
193
360
  - **Sync de artifacts no postinstall** — depois de qualquer `npm install`, os
194
361
  artefatos de cópia (agents, skills, AGENTS.md, commands, scripts MCP,
195
362
  payload code-mode) são atualizados automaticamente no config dir existente;
196
- você nunca precisa re-rodar `init` só para atualizar arquivos.
363
+ você nunca precisa re-rodar `init` só para atualizar arquivos. O config dir
364
+ global recebe o **artefato global** agnóstico de repositório
365
+ (`AGENTS.global.md`: tabela de agentes + regras compartilhadas), enquanto o
366
+ `AGENTS.md` de cada projeto carrega apenas o setup específico do projeto —
367
+ os dois são esculpidos para serem disjuntos (trabalho de latência beta-10),
368
+ então nenhuma regra é paga duas vezes por sessão. A instalação é
369
+ conservadora: conteúdo fora da região gerenciada (regras próprias do
370
+ usuário) é preservado verbatim, um `AGENTS.md` legado totalmente gerado é
371
+ atualizado in place, e qualquer merge é reportado com um aviso.
197
372
  - **Detecção de drift** — o instalador grava a versão instalada em
198
373
  `.pantheon/install-state.json` e o `doctor` avisa quando o pacote é mais
199
374
  novo que o último sync, apontando para o `update`.
@@ -213,6 +388,10 @@ de PR, push, merge e tag nunca publicam nada, e todos os gates de validação s
213
388
  fail-closed: somente um PASS explícito autoriza release evidence. Consulte
214
389
  [docs/RELEASING.md](docs/RELEASING.md) para detalhes de validação e recuperação.
215
390
 
391
+ Se a seção de uma versão candidata já existir, atualize as notas com
392
+ `node scripts/versioning.mjs changelog <versão>`; o comando mescla `[Unreleased]`
393
+ na versão indicada sem avançar o número do pacote.
394
+
216
395
  A validação mantém cada manifest junto do seu lockfile: o root
217
396
  `package.json` + `package-lock.json` e o TUI
218
397
  `src/plugins/tui/package.json` + `src/plugins/tui/package-lock.json`. Ambos usam
@@ -267,6 +446,92 @@ de testar silenciosamente a instalação do host.
267
446
  Isso valida somente o sandbox isolado e preparado. Um PASS não prova suporte
268
447
  para todo host real nem para configurações de host que não foram exercitadas.
269
448
 
449
+ ## Benchmark de latência (bench/)
450
+
451
+ O `bench/` mede a latência de sessão de ponta a ponta do CLI `opencode`
452
+ instalado com o plugin V2 do repositório — o baseline contra o qual toda
453
+ mudança de performance de uma release deve ser comparada. Executa dois
454
+ cenários N vezes cada, intercalados (não-interativo, um diretório de sandbox
455
+ novo por run), coleta métricas por run do `opencode.db` em modo READ-ONLY
456
+ (tokens root + sessões filhas sem dupla contagem, tool calls por nome, número
457
+ de sessões filhas) e tempos de hook das linhas `[bench]` que o plugin anexa ao
458
+ `hooks.log`:
459
+
460
+ ```bash
461
+ node bench/bin/bench.mjs # 5 runs por cenário (A e B), ~1h
462
+ BENCH_MODEL=opencode-go/glm-5.3-flash node bench/bin/bench.mjs # fixar modelo
463
+ node bench/bin/bench.mjs --runs 1 # smoke: um run por cenário
464
+ ```
465
+
466
+ Os resultados vão para `bench/results/<label>-<git-sha>.json` (mediana +
467
+ p80 + falhas por cenário); o label padrão é `baseline` e o harness recusa
468
+ sobrescrever um resultado existente, então a identidade do run fica no nome do
469
+ arquivo. O cenário A faz pesquisa web real, então a variância de rede é
470
+ registrada por run.
471
+
472
+ ### Perfis de medição (explícitos, nunca inferidos)
473
+
474
+ As instruções são injetadas por duas superfícies: o diretório de config global
475
+ e o diretório do projeto. O harness declara qual delas exercita, porque um
476
+ resultado só significa algo no perfil onde a mudança atua:
477
+
478
+ | `--profile` | `AGENTS.md` global | `AGENTS.md` de projeto | Uso |
479
+ |---|---|---|---|
480
+ | `host` (padrão) | diretório de config do host (ou `--global-agents`) | nenhum — run dirs ficam vazios | a injeção real do host, sem alteração |
481
+ | `double-injection` | diretório de config do host (ou `--global-agents`) | `--project-agents`, escrito em cada run dir | o perfil no repo onde global+projeto se sobrepõem |
482
+
483
+ O padrão mede os cenários A e B. Use `--scenarios B` para isolar o claim
484
+ one-shot de F1; o cenário selecionado ainda roda N vezes (`--runs 5` por
485
+ padrão).
486
+
487
+ ```bash
488
+ # braço candidato: artefato global carveado + artefato de projeto carveado
489
+ node bench/bin/bench.mjs --runs 5 --scenarios B --profile double-injection \
490
+ --global-agents AGENTS.global.md --project-agents AGENTS.md --label f1-candidate
491
+ ```
492
+
493
+ Cada braço registra, por run e lido do banco, a identidade (caminho, bytes,
494
+ sha256) de cada arquivo de instrução que a sessão de fato carregou
495
+ (`instruction_state` + `instruction_blob`) — a prova de fidelidade de que o
496
+ que foi medido é o que foi gerado. Um claim só pode se apoiar num run cuja
497
+ provenance mostre que os artefatos global **e** de projeto pretendidos foram
498
+ carregados.
499
+
500
+ ### Sinal vs ruído
501
+
502
+ Wall time é pista, não critério: o spread same-day chega a ~8× no cenário A e
503
+ ~13× no B. Um efeito só é **confirmado** quando excede o spread same-day (ou
504
+ exige N maior); caso contrário é registrado como **inconclusivo**. Prefira
505
+ medianas de tokens, bem mais estáveis. O wall do cenário A não é métrica de
506
+ claim — ele faz pesquisa web real, então a variância de rede domina.
507
+
508
+ ### Proxy do 1º request e evidência de F1
509
+
510
+ O payload de instruções injetado só incide no primeiro request, então o
511
+ `tokens.input` total da árvore (somado em todo turno) é dominado pela variância
512
+ de duração da sessão. Cada run também registra `firstRequest` — o
513
+ `tokens.input` + `tokens.cache.read` da primeira linha assistant — o proxy que
514
+ isola o payload.
515
+
516
+ Uma rodada intercalada no OpenCode v2.0.29 (C1/K1…C5/K5, N=5/braço) mediu o
517
+ contexto do 1º request em 17555 tokens no controle e 15489 na candidata
518
+ (**−2066, −11,8%**; spread intra-braço de 14 tokens em cada braço). D4 confirmou
519
+ 22350 B carregados no controle e 13400 B na candidata (−8950 B, −40,0%); o
520
+ oráculo pré-registrado passou em 5/5 nos dois braços. O efeito no primeiro
521
+ request está confirmado contra o spread intra-braço; a aceitação final de F1
522
+ aguarda revisão do Themis. O input total da árvore caiu da mediana de 47594 para
523
+ 38253 tokens, mas os ranges se sobrepõem e a hipótese de ≤30k não foi atingida.
524
+ As medianas de wall foram 30,078 s e 24,130 s, com spread alto; input total e
525
+ wall permanecem **inconclusivos**. Não há claim de ganho de wall ou custo
526
+ monetário. Detalhes e artefatos por run:
527
+ `bench/results/f1-di-v229-interleaved-2026-10-14.md`. O par sequencial anterior
528
+ permanece somente como evidência de apoio em
529
+ `bench/results/f1-di-v229-clean-2026-10-14.md`. As rodadas exploratórias
530
+ anteriores foram preservadas separadamente em
531
+ `bench/results/f1-double-injection.md` e
532
+ `bench/results/f1-di-v229-preliminary-2026-10-14.md`.
533
+
534
+
270
535
  ## Cobertura TypeScript do Plugin V2
271
536
 
272
537
  `npm run coverage:plugin-v2` executa a suíte `tests/pantheon/*.test.ts` com a
@@ -291,7 +556,7 @@ preparado.
291
556
 
292
557
  ### Divergência intencional do memory MCP
293
558
 
294
- `scripts/memory_mcp_server.py` e `src/mcp/memory_mcp_server.py` são
559
+ `scripts/memory_mcp.py` e `src/mcp/memory_mcp.py` são
295
560
  intencionalmente diferentes. A cópia independente em `scripts/` mantém o
296
561
  contrato leve de `memory_*`; a cópia instalada em `src/mcp/` também expõe o
297
562
  schema opcional de codemap e `code_index`, `code_query` e `code_neighbors`. As
@@ -8,8 +8,10 @@
8
8
  * installOpenCode() from scripts/install/opencode.mjs.
9
9
  *
10
10
  * Dual-version install (Phase 3): the generated config is V1-shaped and valid
11
- * under BOTH OpenCode V1 (`opencode` 1.18.x) and V2 (`opencode2`
12
- * v0.0.0-next-17444). V2 reads the same config locations and normalizes V1
11
+ * under BOTH OpenCode V1 (`opencode` 1.18.x) and V2 (`opencode2`, OpenCode 2.x —
12
+ * `opencode2 --version` prints "opencode v2.0.22"; an early 2.x prerelease
13
+ * printed "0.0.0-next-17444", which is history and not a host observation).
14
+ * V2 reads the same config locations and normalizes V1
13
15
  * fields in memory — no native V2 conversion. `--version v2` labels the
14
16
  * install for the operator; state isolation is a runtime concern
15
17
  * (OPENCODE_DB ~/.local/share/opencode/opencode-v2.db, service port 49375).
@@ -19,6 +21,8 @@ import os from 'node:os'
19
21
  import path from 'node:path'
20
22
  import { fileURLToPath } from 'node:url'
21
23
 
24
+ import { resolveInstallExitCode } from '../scripts/install/exit-codes.mjs'
25
+
22
26
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
23
27
  const ROOT = path.resolve(__dirname, '..')
24
28
 
@@ -54,6 +58,9 @@ function printUsage() {
54
58
  console.log(' npx pantheon-opencode init --components agents,skills,instructions')
55
59
  console.log(' npx pantheon-opencode init --opencode-version v1|v2|auto')
56
60
  console.log(' npx pantheon-opencode init --preset <name> # Install and activate model preset')
61
+ console.log(
62
+ ' npx pantheon-opencode init --merge-dev-config <path> # Merge a dev config (off by default)',
63
+ )
57
64
  console.log(' npx pantheon-opencode set-tier <name> # Set active model preset (global)')
58
65
  console.log(
59
66
  ' npx pantheon-opencode update [--stable] # Update package + re-run init (default: beta channel)',
@@ -64,10 +71,24 @@ function printUsage() {
64
71
  console.log(' npx pantheon-opencode prune # List legacy artifacts (dry-run)')
65
72
  console.log(' npx pantheon-opencode prune --apply # Remove stale backups')
66
73
  console.log(' npx pantheon-opencode doctor [--profile] # Run health check')
74
+ console.log(
75
+ ' npx pantheon-opencode restore [--project] [--yes] # Restore an exact installer backup',
76
+ )
67
77
  console.log(' npx pantheon-opencode uninstall --project # Remove project artifacts safely')
68
78
  console.log(' npx pantheon-opencode uninstall --global # Remove global artifacts safely')
69
79
  console.log(' npx pantheon-opencode --version, -v # Print version and exit')
70
80
  console.log(' npx pantheon-opencode --help # Show this help')
81
+ console.log('')
82
+ console.log('Apply authorization:')
83
+ console.log(' A non-interactive run without --yes prints the plan and applies NOTHING.')
84
+ console.log(' Re-run with --yes to apply; an interactive TTY run prompts (default: no).')
85
+ console.log('')
86
+ console.log('Exit codes:')
87
+ console.log(' 0 success, or plan/no-apply (declined)')
88
+ console.log(' 1 failure')
89
+ console.log(' 2 apply was requested without authorization (non-interactive, no --yes)')
90
+ console.log(' 3 drift detected (a planned file changed after the plan)')
91
+ console.log(' 130 canceled')
71
92
  }
72
93
 
73
94
  async function main() {
@@ -104,6 +125,17 @@ async function main() {
104
125
  process.exit(result.status ?? 1)
105
126
  }
106
127
 
128
+ // restore delegates to scripts/restore.mjs (same spawnSync pattern as prune).
129
+ if (command === 'restore') {
130
+ const { spawnSync } = await import('node:child_process')
131
+ const restoreScript = path.join(ROOT, 'scripts', 'restore.mjs')
132
+ const result = spawnSync(process.execPath, [restoreScript, ...args.slice(1)], {
133
+ stdio: 'inherit',
134
+ cwd: process.cwd(),
135
+ })
136
+ process.exit(result.status ?? 1)
137
+ }
138
+
107
139
  // doctor delegates to scripts/doctor.mjs (same spawnSync pattern as prune).
108
140
  // Forwarding the FULL args (profile, target, --fix, ...) keeps the packaged
109
141
  // CLI a thin wrapper; the doctor banner reports the resolved profile.
@@ -287,6 +319,7 @@ async function main() {
287
319
  '--version',
288
320
  '--opencode-version',
289
321
  '--components',
322
+ '--merge-dev-config',
290
323
  ])
291
324
  for (let i = 1; i < args.length; i++) {
292
325
  const arg = args[i]
@@ -368,6 +401,22 @@ async function main() {
368
401
  }
369
402
  }
370
403
 
404
+ // --merge-dev-config <path> opts IN to merging a contributor's personal
405
+ // OpenCode config into the generated one. Only third-party plugin/provider/
406
+ // compaction entries are merged; Pantheon-owned entries are always stripped.
407
+ // Absent this flag no repository-root config is read (product defaults come
408
+ // from the installer code, never from a dev checkout).
409
+ const mergeDevConfigIndex = args.indexOf('--merge-dev-config')
410
+ const inlineMergeDevConfig = args.find((arg) => arg.startsWith('--merge-dev-config='))
411
+ const mergeDevConfigOpt =
412
+ mergeDevConfigIndex >= 0
413
+ ? (args[mergeDevConfigIndex + 1] ?? null)
414
+ : (inlineMergeDevConfig?.split('=', 2)[1] ?? null)
415
+ if (mergeDevConfigIndex >= 0 && mergeDevConfigOpt === null) {
416
+ console.error('❌ --merge-dev-config requires a file path')
417
+ process.exit(1)
418
+ }
419
+
371
420
  const components = requestedComponents ?? [
372
421
  'agents',
373
422
  'skills',
@@ -392,17 +441,19 @@ async function main() {
392
441
  console.log('')
393
442
  }
394
443
 
444
+ let result
395
445
  try {
396
446
  const { installOpenCode } = await import('../scripts/install/opencode.mjs')
397
447
  const target = isProject ? process.cwd() : undefined
398
- await installOpenCode(target, isDryRun, forceReinstall, components, {
448
+ result = await installOpenCode(target, isDryRun, forceReinstall, components, {
399
449
  interactive: forceInteractive,
400
450
  headless: forceHeadless,
401
451
  yes: autoYes,
402
452
  model: modelOpt,
403
453
  smallModel: smallModelOpt,
404
454
  preset: presetOpt,
405
- version: versionOpt ?? 'v1',
455
+ version: versionOpt ?? 'auto',
456
+ mergeDevConfig: mergeDevConfigOpt,
406
457
  })
407
458
  } catch (err) {
408
459
  if (err?.message === 'Canceled') {
@@ -413,6 +464,21 @@ async function main() {
413
464
  process.exit(1)
414
465
  }
415
466
 
467
+ // Beta 11 Setup phases 3-4: a run that did NOT apply (declined /
468
+ // unauthorized / drift) must never print a success banner. Exit codes:
469
+ // 0 ok / no-apply · 2 apply-requested-without-authorization · 3 drift
470
+ // 130 canceled · 1 failure (thrown above).
471
+ if (result?.applied === false) {
472
+ if (!isDryRun) {
473
+ if (result.reason === 'canceled') console.error(S.canceled)
474
+ process.exit(resolveInstallExitCode(result, isDryRun))
475
+ }
476
+ // Dry-run preview: report honestly instead of the "installed!" banner.
477
+ console.log('')
478
+ console.log(` ${S.dryRunNothingWritten}`)
479
+ return
480
+ }
481
+
416
482
  if (runDoctor && !isDryRun) {
417
483
  console.log('')
418
484
  console.log(` ${S.runningHealthCheck}`)
@@ -15,7 +15,13 @@ agent: "zeus"
15
15
  ## Layers
16
16
 
17
17
  ### Layer 1 — Heuristic Scan
18
- `python3 scripts/themis_heuristic_scan.py [--path=<dir>]`
18
+ Resolve the installed helper first (do not assume the current project contains
19
+ Pantheon source). Check, in order: `$OPENCODE_CONFIG_DIR/.opencode/scripts/`,
20
+ `$OPENCODE_CONFIG_DIR/scripts/`, `${XDG_CONFIG_HOME:-$HOME/.config}/opencode/scripts/`,
21
+ `$HOME/.opencode/scripts/`, `.opencode/scripts/`, then
22
+ `$(npm root -g)/pantheon-opencode/scripts/`. Resolve the first existing file to
23
+ an absolute path and run `python3 "$helper" --path="$PWD"`; if none exists,
24
+ report the helper as missing and stop Layer 1 rather than claiming it passed.
19
25
  Zero tokens, <2s. Checks: ruff, biome, anti-pattern slop, anti-overengineering (YAGNI), hash-anchored edits. Returns score 0-100 + APPROVED/BLOCKING.
20
26
 
21
27
  ### Layer 2 — Themis Deep Review
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Merge and consolidate memory entries"
3
- agent: zeus
3
+ agent: mnemosyne
4
4
  ---
5
5
 
6
6
  Merge related memory entries and consolidate duplicates:
@@ -15,8 +15,15 @@ Structured, checkpointed workflow for complex multi-step tasks spanning multiple
15
15
  ## Workflow
16
16
 
17
17
  ```
18
- Phase 0: SCOPING
19
- └─ Zeus + Athena define scope, phases, acceptance criteria → PLAN.md
18
+ Phase 0: PLAN AND APPROVAL
19
+ └─ @athena returns the complete, detailed Markdown plan to Zeus
20
+ └─ Zeus publishes the full plan as a normal message in the main conversation (not only in the child-agent transcript)
21
+ └─ GATE 1: open the approval UI only after that plan message is visible; never bundle or reverse the two
22
+ └─ If the runtime cannot guarantee that order, ask for approval in plain text after the plan and wait
23
+ └─ Wait for explicit approval or requested changes
24
+ └─ For requested changes, redisplay the entire revised plan and repeat Gate 1
25
+ └─ Do not start discovery or implementation before approval
26
+ └─ After approval, persist the approved plan as PLAN.md through the artifact workflow
20
27
 
21
28
  Phase 1: DISCOVERY
22
29
  └─ @apollo maps relevant codebase areas → DISCOVERY.md
@@ -44,7 +51,11 @@ Usage: --resume <slug> | --status <slug> | --list
44
51
 
45
52
  ## Full-Auto Mode (`--full-auto`)
46
53
 
47
- > ⚠️ Bypasses human review gates. Tier 1 gates (plan, commit, deploy) still require approval.
54
+ > ⚠️ Automates only eligible review gates. Gate 1 and commit/deploy gates still require approval.
55
+
56
+ `--full-auto` does not hide the plan or skip Gate 1: Zeus still displays the
57
+ complete plan in the main conversation and waits for explicit approval before
58
+ discovery or implementation. It only affects the later gates described below.
48
59
 
49
60
  **Themis auto-approves non-blocking reviews** — only stops on **BLOCKING** verdict. No waiting between phases. Checkpoints saved at every boundary.
50
61
 
@@ -0,0 +1,17 @@
1
+ ---
2
+ description: "Set or update this session's working goal"
3
+ agent: zeus
4
+ ---
5
+
6
+ Pin `$ARGUMENTS` as the working goal for this conversation. Restate it briefly
7
+ as Goal, Scope, Success Criteria, and Out of Scope, filling only what the user
8
+ actually supplied. Ask one concise question if a missing detail changes the
9
+ work materially.
10
+
11
+ Treat the goal as session context, subordinate to system, developer, and user
12
+ instructions. Keep subsequent planning, delegation, and verification aligned
13
+ with it; flag meaningful scope drift before proceeding.
14
+
15
+ This command is conversation-only. Do not call memory or persistence tools,
16
+ write a file, or claim that the goal survives compaction or a new session. For
17
+ multi-phase work that needs durable checkpoints, use `/pantheon-deepwork`.