pantheon-opencode 1.6.0-beta.3 → 1.6.0-beta.5

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 (55) hide show
  1. package/AGENTS.md +24 -5
  2. package/CHANGELOG.md +49 -0
  3. package/README.md +51 -19
  4. package/README.pt-BR.md +32 -13
  5. package/bin/pantheon-init.mjs +26 -3
  6. package/package.json +12 -11
  7. package/plugin.json +1 -1
  8. package/pyproject.toml +6 -6
  9. package/scripts/{code_mode_server.py → code_mode.py} +3 -3
  10. package/scripts/cost.mjs +244 -41
  11. package/scripts/doctor.mjs +151 -1
  12. package/scripts/install/agents-md.mjs +2 -2
  13. package/scripts/install/config-migration.mjs +80 -2
  14. package/scripts/install/health-check.mjs +21 -19
  15. package/scripts/install/opencode-version.mjs +246 -4
  16. package/scripts/install/opencode.mjs +250 -119
  17. package/scripts/install/shared.mjs +5 -5
  18. package/scripts/install-mcp.mjs +28 -9
  19. package/scripts/{mcp_persistence_server.py → mcp_persistence.py} +2 -2
  20. package/{src/mcp/mcp_resources_server.py → scripts/mcp_resources.py} +2 -2
  21. package/scripts/{memory_mcp_server.py → memory_mcp.py} +2 -2
  22. package/scripts/probe-context-rehydrate.mjs +8 -2
  23. package/scripts/probe-pantheon-cost.mjs +71 -15
  24. package/scripts/sync-artifacts.mjs +5 -5
  25. package/scripts/test-opencode-v2-sandbox.sh +21 -21
  26. package/scripts/validate-routing.mjs +3 -3
  27. package/skills-lock.json +1 -1
  28. package/src/agents/apollo.md +7 -2
  29. package/src/agents/zeus.md +29 -1
  30. package/src/instructions/memory-protocol.instructions.md +15 -4
  31. package/src/instructions/zeus-anti-stall.instructions.md +9 -1
  32. package/src/mcp/{code_mode_server.py → code_mode.py} +3 -3
  33. package/src/mcp/eval_store.py +2 -2
  34. package/src/mcp/{mcp_persistence_server.py → mcp_persistence.py} +2 -2
  35. package/{scripts/mcp_resources_server.py → src/mcp/mcp_resources.py} +2 -2
  36. package/src/mcp/{memory_mcp_server.py → memory_mcp.py} +2 -2
  37. package/src/mcp/{pantheon_vision_server.py → pantheon_vision.py} +1 -1
  38. package/src/mcp/requirements-mcp.txt +6 -4
  39. package/src/mcp/requirements-vision.txt +2 -2
  40. package/src/pantheon/cost-command.ts +399 -101
  41. package/src/pantheon/presets.mjs +3 -3
  42. package/src/pantheon/v2-unsupported.d.mts +11 -0
  43. package/src/pantheon/v2-unsupported.mjs +113 -0
  44. package/src/plugin-v2.ts +269 -100
  45. package/src/plugins/tui/dist/server.d.ts +1 -2
  46. package/src/plugins/tui/dist/server.d.ts.map +1 -1
  47. package/src/plugins/tui/dist/tui.d.ts +74 -74
  48. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  49. package/src/plugins/tui/dist/tui.js +17 -20
  50. package/src/plugins/tui/package-lock.json +364 -317
  51. package/src/plugins/tui/package.json +8 -5
  52. package/src/plugins/tui/tsconfig.build.json +5 -0
  53. package/src/plugins/tui/tsdown.config.ts +3 -0
  54. package/src/skills/context-compression/SKILL.md +1 -1
  55. package/opencode.json +0 -58
package/AGENTS.md CHANGED
@@ -275,8 +275,14 @@ Para otimizar decisoes de delegacao e reduzir gasto de tokens:
275
275
  2. Se score > 0.85 → reutiliza agente + background_mode do cache
276
276
  3. Se score ≤ 0.85 → aplica regras estaticas e memory_store() com:
277
277
  - key: deleg:<task_type>
278
- - value: {agent, background, pattern}
279
- - metadata: {type: "decision", score: N}
278
+ - value: JSON.stringify({agent, background, pattern})
279
+ - metadata: JSON.stringify({type: "decision", score: N})
280
+
281
+ `value` E `metadata` sao JSON-encoded strings — o runtime exige que o
282
+ chamador serialize os dois. Objeto cru em `metadata` e rejeitado pelo
283
+ `BeforeValidator` do servidor com `metadata must be a JSON object encoded
284
+ as a string (got object). json.dumps it first. Example: metadata='{"type":
285
+ "decision", "score": 0.9}'`.
280
286
 
281
287
  4. **kv_store("deleg:<pattern>", ...)** para padroes recorrentes de delegacao
282
288
  5. **kv_get("deleg:<pattern>")** para reusar decisoes ja tomadas
@@ -305,14 +311,19 @@ memory_store({
305
311
  precedent_used: false,
306
312
  timestamp: "<ISO-8601>"
307
313
  },
308
- metadata: {
314
+ metadata: JSON.stringify({
309
315
  type: "council_decision",
310
316
  specialist_count: N,
311
317
  model_tier_used: "premium|default|fast"
312
- }
318
+ })
313
319
  })
314
320
  ```
315
321
  `value` is JSON-serialized before storing (the MCP `memory_store.value` argument is a string).
322
+ `metadata` works the same way — it is a JSON object encoded as a **string**, so pass
323
+ `JSON.stringify({...})`. A raw object is rejected with `metadata must be a JSON object
324
+ encoded as a string (got object). json.dumps it first. Example: metadata='{"type":
325
+ "decision", "score": 0.9}'` (enforced by a `BeforeValidator` in
326
+ `memory_mcp.py`). **Both `value` and `metadata` must be serialized by the caller.**
316
327
 
317
328
  ### Read Path (Precedent Fast-Path)
318
329
  Before dispatching a new council, Zeus runs:
@@ -425,9 +436,17 @@ No file I/O, no checkpoint_session.py — TTL (4h) handles cleanup automatically
425
436
  Before ANY delegate dispatch, save a checkpoint:
426
437
  ```
427
438
  context_save(slug, "phase:N", json({
428
- "phase": N, "turn_count": N, "agent": "...", "summary": "..."
439
+ "phase": {"current": N, "total": M, "name": "..."},
440
+ "turn_count": N, "agent": "...", "summary": "..."
429
441
  }), session_id=SESSION_ID)
430
442
  ```
443
+ **`content.phase` must be an OBJECT, not a bare number.** `{"phase": 1}` is rejected with
444
+ `context_save: content.phase must be an object (got number)`. The server reads `current`
445
+ and `total` (non-negative ints) and `name` (string, max 256 **bytes** — the guard
446
+ measures `len(value.encode())`, so multi-byte characters count as more than one). That
447
+ set is not closed: any other key inside `phase` is accepted and ignored, never an
448
+ error. The `"phase:N"` slot KEY above is unrelated to `content.phase` and stays a plain
449
+ string.
431
450
  All checkpoints auto-expire after 4h (TTL=14400).
432
451
 
433
452
  ### Gatilho de Pré-Compactação (Anti-perda de estado)
package/CHANGELOG.md CHANGED
@@ -18,6 +18,55 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
18
18
 
19
19
  ## ✅ Closed Issues
20
20
 
21
+ ## [v1.6.0-beta.5] - 2026-10-05
22
+
23
+ &lt;!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
24
+ move this section to a versioned entry and reset the template below. --&gt;
25
+
26
+ ## 🆕 What's New
27
+
28
+ - **O gate de geração do plugin passa a ler a versão do host — e a superfície de ferramentas deixa de desaparecer num host OpenCode 2.x.** O seletor de geração tinha três defaults `'v1'` (`bin/pantheon-init.mjs`, `scripts/install/opencode.mjs` e a assinatura do resolver em `scripts/install/opencode-version.mjs`). Nesse ramo, o installer grava caminhos de arquivo `.ts` na chave singular `plugin`; um host 2.x rejeita caminhos de arquivo ali (`configured plugin path must be a directory`), **descarta** as entradas e, ao fazer isso, **remove** a entrada de diretório V2 que já estava registrada e funcionando. O sintoma era um host aparentemente saudável — a frota MCP subia normalmente — com um único WARN no log e **nenhuma** ferramenta do plugin no catálogo: sem `pantheon_cost`, sem `hashline_edit`, sem `pantheon_model`, sem BackgroundJobBoard e sem hooks. Os três defaults passam a `'auto'`, e `auto` consulta a versão do host. Precedência: flag explícita > `OPENCODE_VERSION` > basename `opencode2` > probe de `--version` > aviso e fallback `v1`. Isto restaura as **3** ferramentas que o contrato V2 registra (`hashline_edit`, `pantheon_cost`, `pantheon_model`); não é paridade com o V1 — ver **Issues Conhecidas**.
29
+ - **Uma instalação na geração V2 e o `doctor` agora declaram a superfície reduzida em vez de deixá-la ser descoberta.** A lista de recursos não suportados no V2 (`getUnsupportedFeatures()`) não tinha nenhum consumidor em produção — só testes a liam. O installer passa a imprimi-la ao lado do aviso de destino V2, e o `doctor` a reporta como um achado próprio, sem alterar o código de saída (uma instalação V2 é uma configuração suportada, não quebrada). A V2 registra 3 ferramentas, não 6, e não tem BackgroundJobBoard; as 3 ferramentas de goal não são registradas na V2 por desenho. O texto agora é único, vindo de `src/pantheon/v2-unsupported.mjs`.
30
+ - **Renomeação dos entrypoints MCP (issue #198).** Os cinco entrypoints Python deixaram de compartilhar o token `server.py`, então um `pkill -f server.py` amplo não derruba mais a frota inteira como efeito colateral. Inclui uma migração em tempo de instalação para que as instalações existentes não fiquem órfãs.
31
+ - **O pacote npm deixou de versionar e distribuir a configuração pessoal de desenvolvimento do OpenCode.** `opencode.json` e `.opencode/` (comandos, plugins e skills) saíram do controle de versão: a config carregava um endpoint MCP privado e overrides de permissão por desenvolvedor, e o harness V1 de comandos/plugins foi substituído por `src/plugin-v2.ts`. O `init` continua gerando a config do projeto do usuário, então instalações não são afetadas; para contribuidores, um `opencode.json` pessoal deve ir em `.git/info/exclude`.
32
+ - **Documentação: `ROADMAP.md` reescrito e um novo documento de fronteira.** O roadmap foi reescrito com as causas-raiz verificadas, e um novo documento — `docs/PERMISSIONS-QUALITY-DELEGATION.md` — mapeia os gates de qualidade, as quatro camadas de permissão e a delegação num só lugar, marcando explicitamente o que ainda não foi verificado.
33
+
34
+ ## 🐞 Fixed
35
+
36
+ - **A leitura do banner de versão do host era quadrática.** Um banner longo custava **11,1 s** para ser interpretado, contra o timeout de **5 s** do spawn de `--version`; o timeout guarda o processo, não a leitura, então um host verboso conseguia estourá-lo. A interpretação passou a ser **linear**.
37
+ - **O `readOnlyRegistry` do V2 não era podado no `session.deleted`.** O registro é global ao processo e nunca era limpo quando uma sessão do V2 terminava, então entradas de sessões encerradas acumulavam. O `session.deleted` do V2 passa a podar o registro, como a V1 já fazia.
38
+
39
+ ## ⚠️ Known Issues
40
+
41
+ - **Ação necessária: re-executar o `init` numa instalação afetada.** O `postinstall` **não** roda o `init`. Quem teve a configuração corrompida por uma instalação `v1` anterior **não** é auto-corrigido apenas por atualizar o pacote — é preciso rodar o installer de novo (`npx pantheon-opencode init`). Uma escolha correta de geração **auto-corrige** a configuração (o ramo V2 já remove as referências `.ts` do V1), mas só quando o installer roda. O mesmo vale para a renomeação dos entrypoints: instalações existentes continuam funcionando porque os arquivos antigos são mantidos, mas os caminhos só são corrigidos numa nova execução do installer; até lá, esses usuários permanecem nos entrypoints antigos e sujeitos ao efeito colateral do `server.py`.
42
+ - **A superfície V2 é menor que a V1 por desenho.** A V2 registra 3 ferramentas (`hashline_edit`, `pantheon_cost`, `pantheon_model`), não 6, e não tem BackgroundJobBoard; as 3 `pantheon_goal_*` são V1-only e a matriz de delegação caller→target não é replicada. O installer e o `doctor` agora **dizem** isso (ver What's New), mas a funcionalidade ausente não é restaurada.
43
+ - **O fallback continua a ser `v1`.** Quando o probe do host falha, não consegue ser interpretado, ou traz versões contraditórias, o resolver avisa **uma vez** e cai para `v1`, em vez de arriscar um host 1.x com uma entrada `plugins` que ele ignoraria. É um erro alto e visível, mas ainda é uma queda para a configuração V1.
44
+
45
+ ## ✅ Closed Issues
46
+
47
+ - **A justificativa da matriz de delegação V1→V2 foi corrigida.** O texto dizia que o V2 "não expõe esse caminho de semeadura"; os tipos do SDK refutam isso. A causa real da matriz não replicada não é a ausência de um caminho de semeadura (`session.created` do V2 traz `properties.info: Session` com `parentID?`, o mesmo campo que a V1 semeia), mas a *segunda* fonte da V1 — o seed de `client.session.list()` para sessões anteriores ao carregamento do plugin — porque o `PluginContext` do V2 não expõe `client`. Se um host 2.0.22 ao vivo popula `info.parentID` nesse evento continua **não verificado** (nenhum canário lê payload de evento). A linha publicada em `v1.6.0-beta.4` fica como registro.
48
+ ## [v1.6.0-beta.4] - 2026-10-03
49
+
50
+ &lt;!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
51
+ move this section to a versioned entry and reset the template below. --&gt;
52
+
53
+ ## 🆕 What's New
54
+
55
+ - **Canário host-backed que dirige `hashline_edit` end-to-end** (`tests/canary/plugin-v2-tool-canary.test.mjs`, 12 casos): carrega `src/plugin-v2.ts` pelo loader real do host `opencode 2.0.22` e registra `Object.keys(ctx)` de dentro do host — medidos, `tool`/`event`/`permission`/`session` existem e `catalog` não. É o primeiro gate que dirige uma ferramenta do Pantheon por um host real. O canário existente só dirige o `read` embutido, e foi por isso que `draft.add()` sem `output` sobreviveu 26 dias com todos os testes verdes.
56
+ - **Os quatro domínios que o SDK `@opencode-ai/plugin` não tipa** (`tool`, `event`, `permission`, `session`) passaram a uma declaração estrutural local (`HostPluginContext`) e **um** cast documentado (`hostDomains()`), no lugar de seis `as unknown as Record<string, unknown>` que não verificavam tipo nenhum. Todo membro continua opcional e todo chamador reconferir antes de usar. A linha estável do SDK é `1.18.x` e não carrega o contrato do host — `1.18.34` (`latest`) é idêntica a `1.18.33`, então seguir o `dev` diário resolveria pior, não melhor.
57
+
58
+ ## 🐞 Fixed
59
+
60
+ - `pantheon_cost` respondia `CORRUPT_DATA` em **toda** chamada contra o host 2.0.22: exigia a tabela `message`, que não existe nesse banco. Os tokens de assistant **estão** legíveis em `session_message`. **Não é renomeação:** `session_message` tem coluna `data`, mas não tem a chave `role` dentro dela — `json_type(data,'$.role')` é nulo em **0 de 40 307** linhas válidas. O V1 marca turnos de assistant com `data.role`; o V2 marca pela **coluna** `type`. Trocar apenas o nome da tabela casaria com nada e devolveria `status: OK` em silêncio. A detecção passa a ser por **presença de tabela**, porque as duas famílias coexistem em bancos migrados.
61
+ - `PANTHEON_OPENCODE_VERSION` nunca selecionou o schema: só mudava o **nome do arquivo**, e `opencode-v2.db` é o isolamento do sandbox via `OPENCODE_DB`, não um banco de host 2.x. A distinção v1/v2 é o **schema detectado**; a variável agora apenas restringe um conjunto já detectado e falha rápido quando pede uma família ausente.
62
+ - `scripts/cost.mjs` duplicava o mesmo bug literal, com o mesmo assert — o fallback continuava morto. `probe-pantheon-cost.mjs` acompanha.
63
+ - O report agora **declara a cobertura temporal** que ele mediu (janela varrida, linha mais antiga), em vez de apresentar totais como se cobrissem meses.
64
+
65
+ ## ⚠️ Known Issues
66
+
67
+ - O canário **host-backed pula no CI**: o runner não tem binário `opencode`. O skip é **visível** no log (`::notice`), e os dois casos sem host rodam incondicionalmente, com `PANTHEON_TOOL_CANARY_BIN` apontado para um caminho inexistente. Rodar de verdade exige um modo no script de sandbox — **não feito**.
68
+ - A cobertura de custo do banco v2 é de **~8,3 dias** nesta máquina (primeiro registro em 2026-09-25). O histórico v1 (~96 dias) fica num diretório irmão com nome de epoch, que **não** é contrato e por isso não é descoberto automaticamente. Quem quiser aponta `PANTHEON_COST_DB`.
69
+ - `metadata.phase` nunca existiu em v1 nem em v2 — medido: 0 linhas de `session_message` têm `$.metadata.phase` nem `$.phase`. A coluna Phase sempre foi `unknown`. Não é regressão, é feature que nunca funcionou.
21
70
  ## [v1.6.0-beta.3] - 2026-10-03
22
71
 
23
72
  &lt;!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
package/README.md CHANGED
@@ -50,6 +50,8 @@ opencode
50
50
  The installer guides you through the available setup. For optional MCP servers,
51
51
  project-local installation, or non-interactive setup, see the
52
52
  [installation guide](docs/INSTALLATION.md).
53
+ The bundled Python MCP servers use the pinned MCP SDK 2.2.0 and standalone
54
+ FastMCP 4.0.10 runtime; see the [MCP guide](docs/MCP.md).
53
55
 
54
56
  ## A simple example
55
57
 
@@ -186,12 +188,13 @@ per installation; V1 and V2 Pantheon plugins must never be registered together.
186
188
  |---|---|---|
187
189
  | OpenCode config key | singular `plugin` | plural `plugins` |
188
190
  | Pantheon registration | `src/plugin.ts` plus `src/plugins/pantheon-hooks.ts` | `<installed>/src/plugin-v2` directory (`index.ts` re-exports `src/plugin-v2.ts`) |
189
- | Runtime contract | Pantheon V1 plugin: 6 tools (`hashline_edit`, the 3 goal tools, `pantheon_cost`, `pantheon_model`), event/tool hooks and V1 compaction handling | Full V2 plugin: 3 tools (`hashline_edit`, `pantheon_cost`, `pantheon_model`), 4 event subscriptions, session hooks (`prompt`, `context`), a read-only-enforcing tool `execute.before` hook, plus configuration transforms |
191
+ | Runtime contract | Pantheon V1 plugin: 6 tools (`hashline_edit`, the 3 goal tools, `pantheon_cost`, `pantheon_model`), event/tool hooks and V1 compaction handling | Full V2 plugin: 3 tools (`hashline_edit`, `pantheon_cost`, `pantheon_model`), 5 event subscriptions, session hooks (`prompt`, `context`), a read-only-enforcing tool `execute.before` hook, plus configuration transforms |
190
192
  | V1 APIs | Registered | Own tool definitions via `ctx.tool.transform()` — not the V1 plugin path |
191
193
 
192
194
  The V2 plugin provides 3 orchestration tools (`hashline_edit`, `pantheon_cost`,
193
- `pantheon_model`), 4 event subscriptions (`session.created`, `session.idle`,
194
- `session.error`, `session.compacted`), session hooks (`prompt`, `context`),
195
+ `pantheon_model`), 5 event subscriptions (`session.created`, `session.idle`,
196
+ `session.deleted`, `session.error`, `session.compacted`), session hooks
197
+ (`prompt`, `context`),
195
198
  and a tool `execute.before` hook that enforces read-only sessions. The 3 goal
196
199
  tools are **not**
197
200
  registered on V2: the goal loop needs a `GoalStore`, a `GoalLoopClient` and a
@@ -234,21 +237,40 @@ npx pantheon-opencode init --opencode-version v2
234
237
  npx pantheon-opencode init --opencode-version auto
235
238
  ```
236
239
 
237
- `--version v1|v2|auto` is accepted as the older selector spelling when used
238
- after `init`. `auto` is conservative, not general platform autodetection:
239
- `OPENCODE_VERSION=v1|v2` wins; otherwise an `OPENCODE_BIN` ending in
240
- `opencode2` selects V2; every other case selects V1. The installer removes
241
- Pantheon references from both config shapes before writing only the selected
242
- Pantheon registration. Third-party entries are not converted or claimed by
243
- this rule.
244
-
245
- The V1-only `pantheon_cost` report can select its database with
246
- `PANTHEON_OPENCODE_VERSION=v1` or `v2` (`opencode.db` or `opencode-v2.db`).
247
- `PANTHEON_COST_DB=/absolute/path/to/opencode.db` takes precedence over the
248
- version selector, and an explicit `dbPath` supplied by the tool caller takes
249
- precedence over both. The resolver never probes the other version's database
250
- and reports an actionable error when the selected DB is missing or has an
251
- incompatible schema.
240
+ `auto` is the default. `--version v1|v2|auto` is accepted as the older
241
+ selector spelling when used after `init`. `auto` resolves the generation from
242
+ the host in this order: an explicit `OPENCODE_VERSION=v1|v2` wins; otherwise an
243
+ `OPENCODE_BIN` path ending in `opencode2` selects V2; otherwise the host
244
+ binary is asked for its `--version` and a major of 2 or more selects V2. Every
245
+ other case — an unreadable probe, an unparseable banner, or a banner whose
246
+ version-like tokens contradict each other with no tool name to break the tie —
247
+ warns once and falls back to V1, because a plural `plugins` directory entry on
248
+ an unknown 1.x host loses the plugin entirely.
249
+
250
+ The probe prefers the token that follows the tool name, so a runtime token
251
+ ahead of it (`node v22.1.0 (opencode 1.18.33)`) or a trailing build date
252
+ (`opencode v1.18.33 built 2026.10.04`) cannot flip the generation. The
253
+ installer removes Pantheon references from both config shapes before writing
254
+ only the selected Pantheon registration. Third-party entries are not converted
255
+ or claimed by this rule.
256
+
257
+ The `pantheon_cost` report resolves its database by PATH, in this order: an
258
+ explicit `dbPath` supplied by the tool caller, then
259
+ `PANTHEON_COST_DB=/absolute/path/to/opencode.db`, then `OPENCODE_DB`, then the
260
+ XDG default `opencode.db`. There is no per-version filename in that chain:
261
+ `opencode-v2.db` is not a host 2.x database — it is the name a sandbox gives
262
+ its own state database through `OPENCODE_DB`.
263
+
264
+ The v1/v2 distinction is the DETECTED SCHEMA, not the file name. The report
265
+ looks for the `message` and `session_message` tables and reads whichever the
266
+ open database actually holds, because a migrated database carries both
267
+ families at once. `PANTHEON_OPENCODE_VERSION=v1|v2` selects no file: it only
268
+ narrows an already-detected set to one table family, and fails fast when the
269
+ value is neither `v1` nor `v2` or when the requested family is not there. Left
270
+ unset, every detected family is read. A database carrying neither table comes
271
+ back as an actionable error (`CORRUPT_DATA`), and a ledger that exists but
272
+ yields no readable tokens as `UNSUPPORTED` — never as a successful empty
273
+ report.
252
274
 
253
275
  The installer still writes the compatibility settings required by the selected
254
276
  OpenCode host, such as `experimental.subagent_depth`; this does not convert a
@@ -438,7 +460,7 @@ in the sandbox root) · `2` usage error · `3` sandbox not prepared.
438
460
 
439
461
  ### Intentional memory MCP divergence
440
462
 
441
- `scripts/memory_mcp_server.py` and `src/mcp/memory_mcp_server.py` are
463
+ `scripts/memory_mcp.py` and `src/mcp/memory_mcp.py` are
442
464
  intentionally different. The standalone `scripts/` copy keeps the lightweight
443
465
  `memory_*` contract; the installed `src/mcp/` copy additionally exposes the
444
466
  optional codemap schema and `code_index`, `code_query`, and `code_neighbors`.
@@ -487,6 +509,16 @@ Ideas, bug reports, documentation improvements, and code contributions are
487
509
  welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening an issue
488
510
  or pull request.
489
511
 
512
+ ### Project-level `opencode.json`
513
+
514
+ The published package carries no `opencode.json` template. `init` still writes
515
+ your project's `opencode.json` itself, and the few product defaults an install
516
+ seeds — `default_agent: zeus` and `permission.skill` — come from the installer
517
+ code, not from a packaged file. Contributors who keep a project-level
518
+ `opencode.json` for their own OpenCode sessions should leave it untracked —
519
+ `.git/info/exclude` per clone, or a shared `.gitignore` decision — rather than
520
+ versioning it.
521
+
490
522
  ## Citation and DOI
491
523
 
492
524
  Pantheon is released under the [MIT License](LICENSE). Cite the
package/README.pt-BR.md CHANGED
@@ -171,21 +171,40 @@ npx pantheon-opencode init --opencode-version v2
171
171
  npx pantheon-opencode init --opencode-version auto
172
172
  ```
173
173
 
174
- `--version v1|v2|auto` é aceito como a grafia antiga do seletor quando usado
175
- depois de `init`. `auto` é conservador, não uma autodetecção geral de
176
- plataforma: `OPENCODE_VERSION=v1|v2` vence; caso contrário, um `OPENCODE_BIN`
177
- terminando em `opencode2` seleciona V2; qualquer outro caso seleciona V1. O
178
- instalador remove referências Pantheon das duas formas de config antes de
174
+ `auto` é o padrão. `--version v1|v2|auto` é aceito como a grafia antiga do seletor
175
+ quando usado depois de `init`. `auto` resolve a geração a partir do host nesta
176
+ ordem: um `OPENCODE_VERSION=v1|v2` explícito vence; caso contrário, um
177
+ `OPENCODE_BIN` terminando em `opencode2` seleciona V2; caso contrário o binário
178
+ do host é consultado com `--version` e uma major >= 2 seleciona V2. Qualquer
179
+ outro caso — uma sonda ilegível, um banner não interpretável, ou um banner
180
+ cujos tokens de versão se contradizem sem um nome de ferramenta que desfaça o
181
+ empate — avisa uma vez e volta para V1, porque uma entrada de diretório em
182
+ `plugins` plural em um host 1.x desconhecido perde o plugin inteiro.
183
+
184
+ A sonda prefere o token que vem logo após o nome da ferramenta, então um token
185
+ de runtime antes dele (`node v22.1.0 (opencode 1.18.33)`) ou uma data de build
186
+ no final (`opencode v1.18.33 built 2026.10.04`) não conseguem virar a geração.
187
+ O instalador remove referências Pantheon das duas formas de config antes de
179
188
  gravar apenas o registro Pantheon selecionado. Entradas de terceiros não são
180
189
  convertidas nem reivindicadas por essa regra.
181
190
 
182
- O relatório `pantheon_cost`, disponível nos plugins V1 e V2, pode selecionar o banco com
183
- `PANTHEON_OPENCODE_VERSION=v1` ou `v2` (`opencode.db` ou `opencode-v2.db`).
184
- `PANTHEON_COST_DB=/caminho/absoluto/para/opencode.db` tem precedência sobre o
185
- seletor de versão, e um `dbPath` explícito fornecido pelo chamador da
186
- ferramenta tem precedência sobre ambos. O resolver nunca consulta o banco da
187
- outra versão e reporta um erro acionável quando o banco selecionado está
188
- ausente ou com schema incompatível.
191
+ O relatório `pantheon_cost`, disponível nos plugins V1 e V2, resolve o banco por
192
+ CAMINHO, nesta ordem: um `dbPath` explícito fornecido pelo chamador da
193
+ ferramenta, depois `PANTHEON_COST_DB=/caminho/absoluto/para/opencode.db`,
194
+ depois `OPENCODE_DB`, e por fim o padrão do XDG `opencode.db`. Não há nome de
195
+ arquivo por versão nessa cadeia: `opencode-v2.db` não é um banco de um host 2.x
196
+ — é o nome que um sandbox dá ao próprio banco de estado via `OPENCODE_DB`.
197
+
198
+ A distinção v1/v2 é o SCHEMA DETECTADO, não o nome do arquivo. O relatório
199
+ procura as tabelas `message` e `session_message` e lê aquele que o banco aberto
200
+ realmente tiver, porque um banco migrado carrega as duas famílias ao mesmo
201
+ tempo. `PANTHEON_OPENCODE_VERSION=v1|v2` não seleciona arquivo nenhum: apenas
202
+ estreita um conjunto já detectado para uma família de tabelas, e falha rápido
203
+ quando o valor não é `v1` nem `v2` ou quando a família pedida não existe lá.
204
+ Sem a variável, todas as famílias detectadas são lidas. Um banco sem nenhuma
205
+ das duas tabelas volta como erro acionável (`CORRUPT_DATA`), e um ledger que
206
+ existe mas não produz tokens legíveis como `UNSUPPORTED` — nunca como um
207
+ relatório vazio com sucesso.
189
208
 
190
209
  O instalador continua gravando as configurações de compatibilidade exigidas
191
210
  pelo host OpenCode selecionado, como `experimental.subagent_depth`; isso não
@@ -334,7 +353,7 @@ preparado.
334
353
 
335
354
  ### Divergência intencional do memory MCP
336
355
 
337
- `scripts/memory_mcp_server.py` e `src/mcp/memory_mcp_server.py` são
356
+ `scripts/memory_mcp.py` e `src/mcp/memory_mcp.py` são
338
357
  intencionalmente diferentes. A cópia independente em `scripts/` mantém o
339
358
  contrato leve de `memory_*`; a cópia instalada em `src/mcp/` também expõe o
340
359
  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).
@@ -54,6 +56,9 @@ function printUsage() {
54
56
  console.log(' npx pantheon-opencode init --components agents,skills,instructions')
55
57
  console.log(' npx pantheon-opencode init --opencode-version v1|v2|auto')
56
58
  console.log(' npx pantheon-opencode init --preset <name> # Install and activate model preset')
59
+ console.log(
60
+ ' npx pantheon-opencode init --merge-dev-config <path> # Merge a dev config (off by default)',
61
+ )
57
62
  console.log(' npx pantheon-opencode set-tier <name> # Set active model preset (global)')
58
63
  console.log(
59
64
  ' npx pantheon-opencode update [--stable] # Update package + re-run init (default: beta channel)',
@@ -287,6 +292,7 @@ async function main() {
287
292
  '--version',
288
293
  '--opencode-version',
289
294
  '--components',
295
+ '--merge-dev-config',
290
296
  ])
291
297
  for (let i = 1; i < args.length; i++) {
292
298
  const arg = args[i]
@@ -368,6 +374,22 @@ async function main() {
368
374
  }
369
375
  }
370
376
 
377
+ // --merge-dev-config <path> opts IN to merging a contributor's personal
378
+ // OpenCode config into the generated one. Only third-party plugin/provider/
379
+ // compaction entries are merged; Pantheon-owned entries are always stripped.
380
+ // Absent this flag no repository-root config is read (product defaults come
381
+ // from the installer code, never from a dev checkout).
382
+ const mergeDevConfigIndex = args.indexOf('--merge-dev-config')
383
+ const inlineMergeDevConfig = args.find((arg) => arg.startsWith('--merge-dev-config='))
384
+ const mergeDevConfigOpt =
385
+ mergeDevConfigIndex >= 0
386
+ ? (args[mergeDevConfigIndex + 1] ?? null)
387
+ : (inlineMergeDevConfig?.split('=', 2)[1] ?? null)
388
+ if (mergeDevConfigIndex >= 0 && mergeDevConfigOpt === null) {
389
+ console.error('❌ --merge-dev-config requires a file path')
390
+ process.exit(1)
391
+ }
392
+
371
393
  const components = requestedComponents ?? [
372
394
  'agents',
373
395
  'skills',
@@ -402,7 +424,8 @@ async function main() {
402
424
  model: modelOpt,
403
425
  smallModel: smallModelOpt,
404
426
  preset: presetOpt,
405
- version: versionOpt ?? 'v1',
427
+ version: versionOpt ?? 'auto',
428
+ mergeDevConfig: mergeDevConfigOpt,
406
429
  })
407
430
  } catch (err) {
408
431
  if (err?.message === 'Canceled') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pantheon-opencode",
3
- "version": "1.6.0-beta.3",
3
+ "version": "1.6.0-beta.5",
4
4
  "description": "Multi-agent orchestration for OpenCode — 14 specialized agents, TDD, Themis review gates.",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -46,6 +46,7 @@
46
46
  "coverage:plugin-v2": "node scripts/check-plugin-v2-coverage.mjs",
47
47
  "test:ts": "for f in tests/pantheon/*.test.ts; do if [ \"$f\" = tests/pantheon/plugin-v2-contract.test.ts ]; then NODE_OPTIONS=--conditions=import tsx \"$f\"; else tsx \"$f\"; fi || exit 1; done",
48
48
  "test:hooks": "node --test tests/pantheon/plugin-v2-hook-canary.test.mjs",
49
+ "test:tool-canary": "node --test tests/canary/plugin-v2-tool-canary.test.mjs",
49
50
  "test:all": "npm run test:ci && npm run test:node && npm run test:ts",
50
51
  "test": "npm run test:all",
51
52
  "test:ci": "python3 -m pytest -q --tb=short 2>&1"
@@ -75,7 +76,6 @@
75
76
  "bin/*.mjs",
76
77
  "AGENTS.md",
77
78
  "CHANGELOG.md",
78
- "opencode.json",
79
79
  "src/mcp/*.py",
80
80
  "src/instructions/*.instructions.md",
81
81
  "src/mcp/*.mjs",
@@ -97,21 +97,22 @@
97
97
  "!**/*.pyc"
98
98
  ],
99
99
  "dependencies": {
100
- "@opencode-ai/plugin": "1.18.30",
101
- "js-yaml": "^4.3.2",
102
- "zod": "4.1.8"
100
+ "@opencode-ai/plugin": "1.18.33",
101
+ "js-yaml": "^5.4.2",
102
+ "zod": "4.1.8",
103
+ "@opencode-ai/sdk": "1.18.33"
103
104
  },
104
105
  "devDependencies": {
105
- "@biomejs/biome": "2.5.13",
106
- "@commitlint/cli": "21.2.2",
107
- "@commitlint/config-conventional": "21.2.2",
106
+ "@biomejs/biome": "2.5.14",
107
+ "@commitlint/cli": "21.2.3",
108
+ "@commitlint/config-conventional": "21.2.3",
108
109
  "ajv": "8.20.0",
109
110
  "husky": "^9.1.7",
110
- "tsx": "4.23.13",
111
- "typescript": "6.0.3"
111
+ "tsx": "4.23.15",
112
+ "typescript": "7.0.2"
112
113
  },
113
114
  "overrides": {
114
- "rolldown": "1.2.0"
115
+ "rolldown": "1.2.7"
115
116
  },
116
117
  "repository": {
117
118
  "type": "git",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pantheon",
3
3
  "publisher": "ils15",
4
- "version": "1.6.0-beta.3",
4
+ "version": "1.6.0-beta.5",
5
5
  "displayName": "Pantheon",
6
6
  "description": "Conductor-Delegate orchestration platform with 14 specialized agents, 21 skills, and OpenCode support.",
7
7
  "agents": "./src/agents",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend"
4
4
 
5
5
  [project]
6
6
  name = "pantheon"
7
- version = "1.6.0-beta.3"
7
+ version = "1.6.0-beta.5"
8
8
  description = "Multi-agent orchestration framework"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = []
@@ -39,11 +39,11 @@ select = ["E", "F", "W", "I", "N", "UP", "B", "SIM", "PL", "RUF", "C901"]
39
39
  "tests/test_memory_mcp_server.py" = ["PLC0415", "PLR2004"]
40
40
  # Existing public APIs and test fixtures are intentionally broad/simple; refactoring
41
41
  # them would be behavior-affecting rather than a mechanical lint correction.
42
- "scripts/code_mode_server.py" = ["PLR0913", "PLR0917"]
43
- "src/mcp/code_mode_server.py" = ["PLR0913", "PLR0917"]
44
- "scripts/mcp_persistence_server.py" = ["PLR0913", "PLR0917"]
45
- "src/mcp/mcp_persistence_server.py" = ["PLR0913", "PLR0917"]
46
- "scripts/memory_mcp_server.py" = ["C901"]
42
+ "scripts/code_mode.py" = ["PLR0913", "PLR0917"]
43
+ "src/mcp/code_mode.py" = ["PLR0913", "PLR0917"]
44
+ "scripts/mcp_persistence.py" = ["PLR0913", "PLR0917"]
45
+ "src/mcp/mcp_persistence.py" = ["PLR0913", "PLR0917"]
46
+ "scripts/memory_mcp.py" = ["C901"]
47
47
  "src/mcp/eval_store.py" = ["PLR2004"]
48
48
  "tests/test_code_mode_frontmatter.py" = ["PLR2004"]
49
49
  "tests/test_eval.py" = ["PLR2004"]
@@ -5,12 +5,12 @@ Provides a confined execution environment for orchestration scripts
5
5
  via MCP tools and resources.
6
6
 
7
7
  Usage:
8
- python scripts/code_mode_server.py
8
+ python scripts/code_mode.py
9
9
 
10
10
  Or via MCP client (stdio transport):
11
11
  pantheon-code-mode:
12
12
  command: python
13
- args: ["scripts/code_mode_server.py"]
13
+ args: ["scripts/code_mode.py"]
14
14
  """
15
15
 
16
16
  from __future__ import annotations
@@ -36,7 +36,7 @@ from typing import Any
36
36
 
37
37
  import yaml
38
38
  from _pantheon_paths import pantheon_home, pantheon_project
39
- from mcp.server.fastmcp import FastMCP
39
+ from fastmcp import FastMCP
40
40
 
41
41
  # ── Constants ─────────────────────────────────────────────────────────────────
42
42
  ALLOWED_EXTENSIONS: frozenset[str] = frozenset({".sh", ".py"})