pantheon-opencode 1.6.0-beta.4 → 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 (40) hide show
  1. package/AGENTS.md +24 -5
  2. package/CHANGELOG.md +27 -0
  3. package/README.md +31 -11
  4. package/README.pt-BR.md +15 -6
  5. package/bin/pantheon-init.mjs +26 -3
  6. package/package.json +1 -2
  7. package/plugin.json +1 -1
  8. package/pyproject.toml +6 -6
  9. package/scripts/{code_mode_server.py → code_mode.py} +2 -2
  10. package/scripts/doctor.mjs +151 -1
  11. package/scripts/install/config-migration.mjs +80 -2
  12. package/scripts/install/health-check.mjs +12 -17
  13. package/scripts/install/opencode-version.mjs +246 -4
  14. package/scripts/install/opencode.mjs +250 -119
  15. package/scripts/install-mcp.mjs +28 -9
  16. package/scripts/{mcp_persistence_server.py → mcp_persistence.py} +1 -1
  17. package/{src/mcp/mcp_resources_server.py → scripts/mcp_resources.py} +1 -1
  18. package/scripts/{memory_mcp_server.py → memory_mcp.py} +1 -1
  19. package/scripts/probe-context-rehydrate.mjs +2 -2
  20. package/scripts/sync-artifacts.mjs +5 -5
  21. package/scripts/test-opencode-v2-sandbox.sh +21 -21
  22. package/skills-lock.json +1 -1
  23. package/src/agents/apollo.md +7 -2
  24. package/src/agents/zeus.md +29 -1
  25. package/src/instructions/memory-protocol.instructions.md +15 -4
  26. package/src/instructions/zeus-anti-stall.instructions.md +9 -1
  27. package/src/mcp/{code_mode_server.py → code_mode.py} +2 -2
  28. package/src/mcp/eval_store.py +2 -2
  29. package/src/mcp/{mcp_persistence_server.py → mcp_persistence.py} +1 -1
  30. package/{scripts/mcp_resources_server.py → src/mcp/mcp_resources.py} +1 -1
  31. package/src/mcp/{memory_mcp_server.py → memory_mcp.py} +1 -1
  32. package/src/pantheon/v2-unsupported.d.mts +11 -0
  33. package/src/pantheon/v2-unsupported.mjs +113 -0
  34. package/src/plugin-v2.ts +149 -53
  35. package/src/plugins/tui/dist/tui.js +1 -1
  36. package/src/plugins/tui/package-lock.json +2 -2
  37. package/src/plugins/tui/package.json +1 -1
  38. package/src/skills/context-compression/SKILL.md +1 -1
  39. package/opencode.json +0 -58
  40. /package/src/mcp/{pantheon_vision_server.py → pantheon_vision.py} +0 -0
@@ -31,13 +31,13 @@ import {
31
31
 
32
32
  /** Canonical MCP script sources — mirrors installOpenCode phase 2.10. */
33
33
  const MCP_SCRIPTS = {
34
- 'mcp_resources_server.py': () => join(ROOT, 'src', 'mcp', 'mcp_resources_server.py'),
35
- 'code_mode_server.py': () => join(ROOT, 'src', 'mcp', 'code_mode_server.py'),
36
- 'memory_mcp_server.py': () => join(ROOT, 'src', 'mcp', 'memory_mcp_server.py'),
37
- 'mcp_persistence_server.py': () => join(ROOT, 'src', 'mcp', 'mcp_persistence_server.py'),
34
+ 'mcp_resources.py': () => join(ROOT, 'src', 'mcp', 'mcp_resources.py'),
35
+ 'code_mode.py': () => join(ROOT, 'src', 'mcp', 'code_mode.py'),
36
+ 'memory_mcp.py': () => join(ROOT, 'src', 'mcp', 'memory_mcp.py'),
37
+ 'mcp_persistence.py': () => join(ROOT, 'src', 'mcp', 'mcp_persistence.py'),
38
38
  '_pantheon_paths.py': () => join(ROOT, 'src', 'mcp', '_pantheon_paths.py'),
39
39
  'mcp_codemap_module.py': () => join(ROOT, 'src', 'mcp', 'mcp_codemap_module.py'),
40
- 'pantheon_vision_server.py': () => join(ROOT, 'src', 'mcp', 'pantheon_vision_server.py'),
40
+ 'pantheon_vision.py': () => join(ROOT, 'src', 'mcp', 'pantheon_vision.py'),
41
41
  'eval_store.py': () => join(ROOT, 'src', 'mcp', 'eval_store.py'),
42
42
  'scrub-secrets.py': () => join(ROOT, 'scripts', 'scrub-secrets.py'),
43
43
  }
@@ -241,11 +241,11 @@ try:
241
241
  except OSError:
242
242
  raise SystemExit(1)
243
243
  expected = {
244
- "code_mode_server.py",
245
- "memory_mcp_server.py",
246
- "mcp_persistence_server.py",
247
- "mcp_resources_server.py",
248
- "pantheon_vision_server.py",
244
+ "code_mode.py",
245
+ "memory_mcp.py",
246
+ "mcp_persistence.py",
247
+ "mcp_resources.py",
248
+ "pantheon_vision.py",
249
249
  }
250
250
  pattern = re.compile(
251
251
  r"spawning process \{\s*"
@@ -515,11 +515,11 @@ try:
515
515
  except OSError:
516
516
  raise SystemExit(1)
517
517
  expected = {
518
- "code_mode_server.py",
519
- "memory_mcp_server.py",
520
- "mcp_persistence_server.py",
521
- "mcp_resources_server.py",
522
- "pantheon_vision_server.py",
518
+ "code_mode.py",
519
+ "memory_mcp.py",
520
+ "mcp_persistence.py",
521
+ "mcp_resources.py",
522
+ "pantheon_vision.py",
523
523
  }
524
524
  pattern = re.compile(
525
525
  r"spawning process \{\s*"
@@ -642,11 +642,11 @@ config_path = Path(os.environ["CONFIG_PATH"])
642
642
  runtime_dir = os.environ["RUNTIME_DIR"]
643
643
  python_bin = os.environ["MCP_PYTHON"]
644
644
  managed = {
645
- "pantheon-code-mode": "code_mode_server.py",
646
- "pantheon-memory": "memory_mcp_server.py",
647
- "pantheon-persistence": "mcp_persistence_server.py",
648
- "pantheon-resources": "mcp_resources_server.py",
649
- "pantheon-vision": "pantheon_vision_server.py",
645
+ "pantheon-code-mode": "code_mode.py",
646
+ "pantheon-memory": "memory_mcp.py",
647
+ "pantheon-persistence": "mcp_persistence.py",
648
+ "pantheon-resources": "mcp_resources.py",
649
+ "pantheon-vision": "pantheon_vision.py",
650
650
  }
651
651
  try:
652
652
  config = json.loads(config_path.read_text(encoding="utf-8"))
@@ -829,7 +829,7 @@ else
829
829
  fi
830
830
 
831
831
  echo "--- Gate (b): pantheon://agents CONTENT check ---"
832
- RESOURCES_SRV="$PANTHEON_GLOBAL/scripts/mcp_resources_server.py"
832
+ RESOURCES_SRV="$PANTHEON_GLOBAL/scripts/mcp_resources.py"
833
833
  if [ ! -f "$RESOURCES_SRV" ]; then
834
834
  record_status "pantheon://agents resource" "FAIL" "resources server not found at $RESOURCES_SRV"
835
835
  else
@@ -1013,11 +1013,11 @@ config_path = Path(os.environ["CONFIG_PATH"])
1013
1013
  runtime_dir = os.environ["RUNTIME_DIR"]
1014
1014
  python_bin = os.environ["MCP_PYTHON"]
1015
1015
  managed = {
1016
- "pantheon-code-mode": "code_mode_server.py",
1017
- "pantheon-memory": "memory_mcp_server.py",
1018
- "pantheon-persistence": "mcp_persistence_server.py",
1019
- "pantheon-resources": "mcp_resources_server.py",
1020
- "pantheon-vision": "pantheon_vision_server.py",
1016
+ "pantheon-code-mode": "code_mode.py",
1017
+ "pantheon-memory": "memory_mcp.py",
1018
+ "pantheon-persistence": "mcp_persistence.py",
1019
+ "pantheon-resources": "mcp_resources.py",
1020
+ "pantheon-vision": "pantheon_vision.py",
1021
1021
  }
1022
1022
 
1023
1023
  try:
package/skills-lock.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "src/skills/clonedeps/SKILL.md": "3e0b43cf96da98c83699dc48db3fd58fbfda84b9022bb70d3747875c81c3e124",
6
6
  "src/skills/code-review-checklist/SKILL.md": "0e59367c65ef4608db80a0674279f32cfc3f67dca7236f233b61a47fe506579f",
7
7
  "src/skills/codemap/SKILL.md": "87af5271e1576e154cadff313eab16264b726e43d094520736b7a5b03b409a4a",
8
- "src/skills/context-compression/SKILL.md": "ebc721c715c1fa20e4ebf5177007fde94e001540b16cef4316393a665633c16f",
8
+ "src/skills/context-compression/SKILL.md": "9c65c132beb0dc7bd20335cfd7f41c589f5168a72dcf5d51740bc66f2865e027",
9
9
  "src/skills/git-workflow-and-versioning/SKILL.md": "685a94041f1b4ce2a430301f517b89102055696267c369a7e8fb191606659276",
10
10
  "src/skills/incremental-implementation/SKILL.md": "7065ddb45da3c6b6751de07f290e92e6cb072e949a2e3aaae13ee7762f0a70fc",
11
11
  "src/skills/loop-engineering/SKILL.md": "f804e545821b9cca8fe0ec517307ed902446f28c2ca8ff9194c0f987cf7229e3",
@@ -38,7 +38,10 @@ mcp_tools:
38
38
  - Generate structured summaries (not raw dumps)
39
39
 
40
40
  ### 2. External Research
41
- - Web search via bifrost MCP tools for documentation, blog posts, GitHub repos
41
+ - Web search through whatever web-search MCP the USER has configured — Pantheon
42
+ bundles none and does not assume one. If no search tool is available, skip
43
+ external search and say so; never present an unverified recollection as a
44
+ citation.
42
45
  - Context7 for library documentation
43
46
  - Read URLs with webfetch for known resource URLs
44
47
 
@@ -50,7 +53,9 @@ mcp_tools:
50
53
  ## TOOLS NOT AVAILABLE
51
54
  - bash - forbidden (cannot run commands)
52
55
  - edit - forbidden (read-only agent)
53
- - bifrost - use bifrost MCP tools for search
56
+ - Web search by default - no search MCP is bundled or pre-configured. Use one
57
+ ONLY if the user's own config exposes it; otherwise state that external search
58
+ was unavailable instead of improvising.
54
59
 
55
60
  ## MCP Security
56
61
  - Never embed credentials in URLs (grep for token=, key=, secret=)
@@ -284,7 +284,35 @@ in `task-result-guard.ts` (plugin `tool.execute.after` chain): an
284
284
  empty/whitespace-only result is converted into an explicit error so a silent
285
285
  child failure is never mistaken for success. No manual retry helper is required.
286
286
 
287
- ### /cost — pantheon_cost tool (WIRED no plugin)
287
+ ### /cost — pantheon_cost tool (disponivel com o version gate corrigido)
288
+ `pantheon_cost` volta a existir assim que o version gate corrigido estiver
289
+ instalado **e após o usuário re-rodar `init`**: em host 2.x, `auto` resolve
290
+ para `v2` e registra o entry de diretorio `src/plugin-v2`, que expoe
291
+ exatamente 3 tools — `hashline_edit`, `pantheon_cost` e `pantheon_model`
292
+ (V2 nao registra as 3 goal tools; ver `docs/INSTALLATION.md` ->
293
+ "OpenCode V1/V2 — contrato de plugin").
294
+ **Caveat**: a tool so reaparece para quem re-rodar `init` depois do fix. O
295
+ `postinstall` nao re-executa `init`, entao ate la o config existente continua
296
+ resolvendo para V1 e a tool segue ausente — nesse estado, tratar `/cost` como
297
+ indisponivel.
298
+
299
+ Causa raiz como era na tag `1.6.0-beta.4` (registro para quem ainda tem um
300
+ install quebrado): o host `opencode v2.0.22` rejeita caminhos de arquivo `.ts` nos
301
+ arrays de plugin do config (recusa `configured plugin path must be a directory`,
302
+ medida em runtime nesse host; o repo so registra a parafrase `must be a
303
+ directory`), e a selecao de geracao caia sempre em V1 — a geracao V1 escreve
304
+ entradas `.ts` sob a chave singular `plugin` (`config.plugin`, o bloco de merge
305
+ em `scripts/install/opencode.mjs`) e o host descarta todas, logo nenhum
306
+ modulo Pantheon entra no processo Node. **O que mudou**: `resolveOpenCodeVersion`
307
+ agora tem default `'auto'` nos dois call sites e no proprio signature, e `auto`
308
+ consulta o host. Precedencia `auto`: flag explicito > `OPENCODE_VERSION` >
309
+ basename `opencode2` > probe `--version` do host (major >= 2 => v2) > soft-fail
310
+ para V1 com warning visivel. Precedencia e criterios do probe estao em
311
+ `docs/INSTALLATION.md` -> "OpenCode V1/V2 — contrato de plugin".
312
+
313
+ Comportamento da tool — **NAO VERIFICADO em runtime**: em 1.6.0-beta.4 o modulo
314
+ (`src/pantheon/cost-command.ts`) nunca era carregado no processo, e o fix nao foi
315
+ exercitado end-to-end, entao este descritivo permanece nao verificado.
288
316
  `pantheon_cost({ days?: number })` le o `opencode.db` READ-ONLY usando o backend
289
317
  unico `node:sqlite`; se indisponivel, retorna `UNSUPPORTED`, sem fallback. Devolve tabela markdown de custo + tokens por
290
318
  agente nos ultimos N dias (default 7). Uso: quando Nyx pedir visibilidade de
@@ -58,8 +58,14 @@ Para otimizar decisoes de delegacao e reduzir gasto de tokens:
58
58
  2. Se score > 0.85 → reutiliza agente + background_mode do cache
59
59
  3. Se score ≤ 0.85 → aplica regras estaticas e memory_store() com:
60
60
  - key: deleg:<task_type>
61
- - value: {agent, background, pattern}
62
- - metadata: {type: "decision", score: N}
61
+ - value: JSON.stringify({agent, background, pattern})
62
+ - metadata: JSON.stringify({type: "decision", score: N})
63
+
64
+ `value` E `metadata` sao JSON-encoded strings — o runtime exige que o
65
+ chamador serialize os dois. Objeto cru em `metadata` e rejeitado pelo
66
+ `BeforeValidator` do servidor com `metadata must be a JSON object encoded
67
+ as a string (got object). json.dumps it first. Example: metadata='{"type":
68
+ "decision", "score": 0.9}'`.
63
69
 
64
70
  4. **kv_store("deleg:<pattern>", ...)** para padroes recorrentes de delegacao
65
71
  5. **kv_get("deleg:<pattern>")** para reusar decisoes ja tomadas
@@ -88,14 +94,19 @@ memory_store({
88
94
  precedent_used: false,
89
95
  timestamp: "<ISO-8601>"
90
96
  },
91
- metadata: {
97
+ metadata: JSON.stringify({
92
98
  type: "council_decision",
93
99
  specialist_count: N,
94
100
  model_tier_used: "premium|default|fast"
95
- }
101
+ })
96
102
  })
97
103
  ```
98
104
  `value` is JSON-serialized before storing (the MCP `memory_store.value` argument is a string).
105
+ `metadata` works the same way — it is a JSON object encoded as a **string**, so pass
106
+ `JSON.stringify({...})`. A raw object is rejected with `metadata must be a JSON object
107
+ encoded as a string (got object). json.dumps it first. Example: metadata='{"type":
108
+ "decision", "score": 0.9}'` (enforced by a `BeforeValidator` in
109
+ `memory_mcp.py`). **Both `value` and `metadata` must be serialized by the caller.**
99
110
 
100
111
  ### Read Path (Precedent Fast-Path)
101
112
  Before dispatching a new council, Zeus runs:
@@ -66,9 +66,17 @@ No file I/O, no checkpoint_session.py — TTL (4h) handles cleanup automatically
66
66
  Before ANY delegate dispatch, save a checkpoint:
67
67
  ```
68
68
  context_save(slug, "phase:N", json({
69
- "phase": N, "turn_count": N, "agent": "...", "summary": "..."
69
+ "phase": {"current": N, "total": M, "name": "..."},
70
+ "turn_count": N, "agent": "...", "summary": "..."
70
71
  }), session_id=SESSION_ID)
71
72
  ```
73
+ **`content.phase` must be an OBJECT, not a bare number.** `{"phase": 1}` is rejected with
74
+ `context_save: content.phase must be an object (got number)`. The server reads `current`
75
+ and `total` (non-negative ints) and `name` (string, max 256 **bytes** — the guard
76
+ measures `len(value.encode())`, so multi-byte characters count as more than one). That
77
+ set is not closed: any other key inside `phase` is accepted and ignored, never an
78
+ error. The `"phase:N"` slot KEY above is unrelated to `content.phase` and stays a plain
79
+ string.
72
80
  All checkpoints auto-expire after 4h (TTL=14400).
73
81
 
74
82
  ### Gatilho de Pré-Compactação (Anti-perda de estado)
@@ -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
@@ -2,7 +2,7 @@
2
2
  """Plugin eval certification storage — ``plugin_eval`` memory namespace.
3
3
 
4
4
  Thin stdlib-only wrapper around the memory SQLite DB (same schema as
5
- ``src/mcp/memory_mcp_server.py``) for plugin-eval certification results.
5
+ ``src/mcp/memory_mcp.py``) for plugin-eval certification results.
6
6
 
7
7
  Namespace: ``plugin_eval``
8
8
  Key format: ``eval:<name>:<date>`` (e.g. ``eval:tdd-with-agents:2026-08-21``)
@@ -30,7 +30,7 @@ from typing import Any
30
30
 
31
31
  NAMESPACE = "plugin_eval"
32
32
 
33
- # Minimal schema — matches memory_mcp_server.SCHEMA_SQL for the memories
33
+ # Minimal schema — matches memory_mcp.SCHEMA_SQL for the memories
34
34
  # table (vec/FTS tables are initialized by the memory MCP server on first
35
35
  # run; eval entries remain readable via list/recall regardless).
36
36
  _SCHEMA_SQL = """
@@ -5,7 +5,7 @@ Key-Value store with FTS5 full-text search, TTL-based expiration,
5
5
  and namespace isolation. Uses SQLite with zero external dependencies.
6
6
 
7
7
  Usage:
8
- python scripts/mcp_persistence_server.py
8
+ python scripts/mcp_persistence.py
9
9
  """
10
10
 
11
11
  from __future__ import annotations
@@ -7,7 +7,7 @@ Provides MCP resources for the Pantheon agent framework:
7
7
  plans/status, memory-bank files
8
8
 
9
9
  Usage:
10
- python scripts/mcp_resources_server.py
10
+ python scripts/mcp_resources.py
11
11
  """
12
12
 
13
13
  from __future__ import annotations
@@ -19,7 +19,7 @@ Tools:
19
19
  memory_stats — DB statistics
20
20
 
21
21
  Usage:
22
- python scripts/memory_mcp_server.py
22
+ python scripts/memory_mcp.py
23
23
  """
24
24
 
25
25
  from __future__ import annotations
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Type declarations for v2-unsupported.mjs (the V2 feature-reduction seed).
3
+ * Consumed by src/plugin-v2.ts via `import ... from './pantheon/v2-unsupported.mjs'`,
4
+ * and by the plain-`.mjs` install/health scripts at runtime.
5
+ */
6
+
7
+ /** Statically-known V2 feature reductions, in report order. */
8
+ export declare const V2_UNSUPPORTED_FEATURE_SEED: readonly string[]
9
+
10
+ /** A fresh, mutable copy of the seed, so a reporter cannot mutate the shared list. */
11
+ export declare function listV2UnsupportedFeatures(): string[]
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * v2-unsupported.mjs — the SEED of V2 feature reductions, in plain ESM.
4
+ *
5
+ * WHY A SEPARATE PLAIN-`.mjs` MODULE: these strings must be readable by three
6
+ * very different consumers, and only one of them can import TypeScript.
7
+ *
8
+ * - `src/plugin-v2.ts` (TypeScript) — owns the LIVE list, appending to it at
9
+ * runtime via `markUnsupported()` when a host API turns out to be missing,
10
+ * and exposes it through `getUnsupportedFeatures()`.
11
+ * - `scripts/install/opencode.mjs` — reports the reduction at install time
12
+ * for a V2-generation install.
13
+ * - `scripts/doctor.mjs` — reports it in the health gate.
14
+ *
15
+ * The scripts are plain Node ESM and must stay that way, and `.mjs` is a
16
+ * REPO-WIDE INVARIANT rather than a judgement about Node: `tsx` is a
17
+ * devDependency only, and no plain-Node script in `scripts/` or `bin/` imports
18
+ * a `.ts` module — every `.ts` path there is a string to copy, stat or read as
19
+ * text (`scripts/install/opencode.mjs`, `scripts/probe-pantheon-cost.mjs`,
20
+ * `scripts/package-evidence.mjs`, `scripts/check-plugin-v2-coverage.mjs`).
21
+ * So a `.mjs` import is the only form all three consumers can share, and it
22
+ * matches the existing `src/pantheon/presets.mjs` + `.d.mts` precedent.
23
+ *
24
+ * NOT the reason: type-stripping. `package.json#engines` admits
25
+ * `^22.22.2 || ^24.15.0 || >=26.0.0`, every admitted version already defaults to
26
+ * unflagged type-stripping (it landed in 22.18.0), so that argument would not
27
+ * have held on any admitted version. The live hazard is that `engines` is not
28
+ * ENFORCED: a Node 20 user can run `doctor` and would get no type-stripping,
29
+ * which is an argument for a runtime version check, not for this file's shape.
30
+ *
31
+ * The SEED is the statically-known half of `getUnsupportedFeatures()`: what a
32
+ * migrating user loses by choosing the V2 contract, knowable with no host at
33
+ * all. Runtime-appended entries (`tool-transform`, `event-stream`, …) are
34
+ * observations about one live host and are deliberately NOT seeded here — they
35
+ * are not known until the plugin runs against a real V2 host.
36
+ *
37
+ * ONE SOURCE OF TRUTH: install and doctor both read the seed from here, so the
38
+ * two cannot drift apart, and what they report IS what this file lists. Note the
39
+ * scope of that claim: it holds between the SEED and the two reporters. The
40
+ * LIVE list `getUnsupportedFeatures()` returns is this seed PLUS markers appended
41
+ * at runtime (`tool-transform`, `event-stream`, …), so it is a superset and the
42
+ * two are expected to differ there. Do not copy these strings into a script;
43
+ * import them.
44
+ *
45
+ * @module v2-unsupported
46
+ */
47
+
48
+ /**
49
+ * Statically-known V2 feature reductions, in the order a reader should meet
50
+ * them. Frozen: `plugin-v2.ts` copies it into its own mutable live array, and
51
+ * every other consumer goes through {@link listV2UnsupportedFeatures}.
52
+ *
53
+ * @type {readonly string[]}
54
+ */
55
+ export const V2_UNSUPPORTED_FEATURE_SEED = Object.freeze([
56
+ 'legacy-hooks',
57
+ // `ctx.catalog` is the one domain a live 2.0.22 host does NOT have: it is
58
+ // absent from `Object.keys(ctx)`, measured in
59
+ // tests/canary/plugin-v2-tool-canary.test.mjs. (`catalog` IS in the 1.18.33
60
+ // SDK's PluginContext, so the SDK types it and the host does not provide it.)
61
+ 'catalog-transform',
62
+ // Adapter support, NOT host availability: 2.0.22 exposes ctx.integration and
63
+ // ctx.skill with callable transforms, and Pantheon deliberately registers
64
+ // neither. These entries record what this plugin does not implement.
65
+ 'integration-transform',
66
+ // The inspected SkillEditor shape has no `source()` helper — i.e. no
67
+ // `SkillEditor.source()` — for adding a directory source. This is narrower
68
+ // than (and distinct from) ctx.skill's host availability or callable
69
+ // transform, which a live 2.0.22 host does provide.
70
+ 'skill-transform',
71
+ // Adapter limitation, not a host gap: the goal loop needs a GoalStore, a
72
+ // GoalLoopClient and a BackgroundJobBoard, none of which the V2
73
+ // PluginContext exposes, and the V1 bridge resolves to null outside V1.
74
+ // pantheon_goal_create/get/update are therefore absent from the V2 surface
75
+ // rather than registered as non-functional placeholders.
76
+ 'goal-tools',
77
+ // Adapter limitation, not a host gap — TWO separate reasons, which are easy
78
+ // to conflate and were once stated wrongly (see the v2EnforcementGuard
79
+ // comment in src/plugin-v2.ts for the long form):
80
+ // - MISSING WIRING: V2 keeps no session→agent map (the host puts the agent
81
+ // on the execute.before event), so there is no `getSessionAgent` to pass.
82
+ // The guard would then call isDelegationAllowed(undefined, target) → false
83
+ // and throw "caller agent is unavailable" on EVERY task() call.
84
+ // - UNWIRED HIERARCHY: V2 seeds no session→parent map anywhere, and an
85
+ // unseeded SessionHierarchyRegistry gates exactly two things — the depth-2
86
+ // child deny and the root-session gate. Unseeded, isChild is always false,
87
+ // so neither can be trusted. UNWIRED, not impossible: the V2
88
+ // `session.created` event carries `properties.info: Session` with
89
+ // `parentID?`, the same field V1 seeds from on the same event, and one
90
+ // line in onSessionCreated would seed live sessions. V1's OTHER source —
91
+ // the client.session.list() startup seed for sessions predating plugin
92
+ // load — has no V2 equivalent at all, because V2's PluginContext exposes
93
+ // no `client`. Whether a live 2.0.22 host populates info.parentID on that
94
+ // event is UNVERIFIED: the canary records Object.keys(ctx), not payloads.
95
+ // Note the hierarchy is NOT what would deny: isRoot reports `true` for an
96
+ // unknown session while unseeded, which PASSES the root gate.
97
+ // Read-only depth-2 still holds via the blocked-tool list in the guard, so
98
+ // this marker records an unenforced matrix, not an unenforced depth limit.
99
+ 'delegation-matrix',
100
+ ])
101
+
102
+ /**
103
+ * A fresh, mutable copy of the seed.
104
+ *
105
+ * Callers get a copy so a reporter that sorts, filters or appends cannot reach
106
+ * back into the shared list — this is what makes "both surfaces report the same
107
+ * strings" a structural property rather than a convention.
108
+ *
109
+ * @returns {string[]}
110
+ */
111
+ export function listV2UnsupportedFeatures() {
112
+ return [...V2_UNSUPPORTED_FEATURE_SEED]
113
+ }