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.
- package/AGENTS.md +24 -5
- package/CHANGELOG.md +27 -0
- package/README.md +31 -11
- package/README.pt-BR.md +15 -6
- package/bin/pantheon-init.mjs +26 -3
- package/package.json +1 -2
- package/plugin.json +1 -1
- package/pyproject.toml +6 -6
- package/scripts/{code_mode_server.py → code_mode.py} +2 -2
- package/scripts/doctor.mjs +151 -1
- package/scripts/install/config-migration.mjs +80 -2
- package/scripts/install/health-check.mjs +12 -17
- package/scripts/install/opencode-version.mjs +246 -4
- package/scripts/install/opencode.mjs +250 -119
- package/scripts/install-mcp.mjs +28 -9
- package/scripts/{mcp_persistence_server.py → mcp_persistence.py} +1 -1
- package/{src/mcp/mcp_resources_server.py → scripts/mcp_resources.py} +1 -1
- package/scripts/{memory_mcp_server.py → memory_mcp.py} +1 -1
- package/scripts/probe-context-rehydrate.mjs +2 -2
- package/scripts/sync-artifacts.mjs +5 -5
- package/scripts/test-opencode-v2-sandbox.sh +21 -21
- package/skills-lock.json +1 -1
- package/src/agents/apollo.md +7 -2
- package/src/agents/zeus.md +29 -1
- package/src/instructions/memory-protocol.instructions.md +15 -4
- package/src/instructions/zeus-anti-stall.instructions.md +9 -1
- package/src/mcp/{code_mode_server.py → code_mode.py} +2 -2
- package/src/mcp/eval_store.py +2 -2
- package/src/mcp/{mcp_persistence_server.py → mcp_persistence.py} +1 -1
- package/{scripts/mcp_resources_server.py → src/mcp/mcp_resources.py} +1 -1
- package/src/mcp/{memory_mcp_server.py → memory_mcp.py} +1 -1
- package/src/pantheon/v2-unsupported.d.mts +11 -0
- package/src/pantheon/v2-unsupported.mjs +113 -0
- package/src/plugin-v2.ts +149 -53
- package/src/plugins/tui/dist/tui.js +1 -1
- package/src/plugins/tui/package-lock.json +2 -2
- package/src/plugins/tui/package.json +1 -1
- package/src/skills/context-compression/SKILL.md +1 -1
- package/opencode.json +0 -58
- /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
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
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
|
-
'
|
|
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
|
-
"
|
|
245
|
-
"
|
|
246
|
-
"
|
|
247
|
-
"
|
|
248
|
-
"
|
|
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
|
-
"
|
|
519
|
-
"
|
|
520
|
-
"
|
|
521
|
-
"
|
|
522
|
-
"
|
|
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": "
|
|
646
|
-
"pantheon-memory": "
|
|
647
|
-
"pantheon-persistence": "
|
|
648
|
-
"pantheon-resources": "
|
|
649
|
-
"pantheon-vision": "
|
|
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/
|
|
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": "
|
|
1017
|
-
"pantheon-memory": "
|
|
1018
|
-
"pantheon-persistence": "
|
|
1019
|
-
"pantheon-resources": "
|
|
1020
|
-
"pantheon-vision": "
|
|
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": "
|
|
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",
|
package/src/agents/apollo.md
CHANGED
|
@@ -38,7 +38,10 @@ mcp_tools:
|
|
|
38
38
|
- Generate structured summaries (not raw dumps)
|
|
39
39
|
|
|
40
40
|
### 2. External Research
|
|
41
|
-
- Web search
|
|
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
|
-
-
|
|
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=)
|
package/src/agents/zeus.md
CHANGED
|
@@ -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 (
|
|
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":
|
|
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/
|
|
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/
|
|
13
|
+
args: ["scripts/code_mode.py"]
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
from __future__ import annotations
|
package/src/mcp/eval_store.py
CHANGED
|
@@ -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/
|
|
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
|
|
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/
|
|
8
|
+
python scripts/mcp_persistence.py
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
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
|
+
}
|