pantheon-opencode 1.4.2 → 1.5.0-beta.1
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/.pantheon/code-mode/.gitkeep +0 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-1.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-10.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-11.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-2.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-3.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-4.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-5.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-6.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-7.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-8.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-9.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/heartbeat.json +8 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/session.json +11 -0
- package/.pantheon/code-mode/README.md +93 -0
- package/.pantheon/code-mode/checkpoint-session.py +7 -0
- package/.pantheon/code-mode/checkpoint-session.sh +11 -0
- package/.pantheon/code-mode/checkpoint_session.py +481 -0
- package/.pantheon/code-mode/compress-inline.py +450 -0
- package/.pantheon/code-mode/eval-llm-judge.py +255 -0
- package/.pantheon/code-mode/eval-monte-carlo.py +368 -0
- package/.pantheon/code-mode/eval-run.py +197 -0
- package/.pantheon/code-mode/eval-static.py +321 -0
- package/.pantheon/code-mode/example-sync.sh +5 -0
- package/.pantheon/code-mode/session-end-save.py +186 -0
- package/.pantheon/code-mode/session-end-save.sh +18 -0
- package/.pantheon/code-mode/test-allowed.sh +5 -0
- package/.pantheon/code-mode/test-auto-continue-checkpoint.sh +179 -0
- package/.pantheon/code-mode/test-env.sh +7 -0
- package/.pantheon/code-mode/test-prlimit.sh +9 -0
- package/.pantheon/code-mode/test-timeout.sh +6 -0
- package/README.md +158 -1189
- package/README.pt-BR.md +222 -0
- package/bin/pantheon-init.mjs +30 -11
- package/opencode.json +1 -0
- package/package.json +14 -3
- package/plugin.json +15 -0
- package/scripts/_pantheon_paths.py +48 -7
- package/scripts/ci-validate-yaml.py +7 -2
- package/scripts/code_mode_server.py +33 -2
- package/scripts/doctor.mjs +135 -127
- package/scripts/generate-preset-docs.mjs +127 -66
- package/scripts/generate-prompts.sh +2 -2
- package/scripts/install/cli-ui.mjs +8 -1
- package/scripts/install/config-migration.mjs +401 -0
- package/scripts/install/health-check.mjs +3 -3
- package/scripts/install/model-picker.mjs +72 -23
- package/scripts/install/opencode-version.mjs +47 -0
- package/scripts/install/opencode.mjs +261 -88
- package/scripts/install/plugin.mjs +112 -42
- package/scripts/install/shared.mjs +13 -158
- package/scripts/install-mcp.mjs +44 -232
- package/scripts/mcp_persistence_server.py +344 -15
- package/scripts/mcp_resources_server.py +144 -40
- package/scripts/memory_mcp_server.py +2 -2
- package/scripts/recover-zenodo-deposition.mjs +146 -0
- package/scripts/sync-tui.mjs +32 -7
- package/scripts/test-opencode-v1-v2-sandbox.sh +591 -0
- package/scripts/uninstall.mjs +298 -954
- package/scripts/validate-zenodo-release.mjs +154 -0
- package/scripts/zenodo-artifact.mjs +21 -0
- package/scripts/zenodo-release-state.mjs +53 -0
- package/src/mcp/_pantheon_paths.py +48 -7
- package/src/mcp/code_mode_server.py +33 -2
- package/src/mcp/eval_store.py +1 -1
- package/src/mcp/mcp_persistence_server.py +344 -15
- package/src/mcp/mcp_resources_server.py +144 -40
- package/src/mcp/pantheon_vision_server.py +1 -1
- package/src/mcp/toon_codec.py +491 -0
- package/src/pantheon/background-job-board.ts +67 -1
- package/src/pantheon/cost-command.ts +6 -2
- package/src/pantheon/delegate-manager.ts +384 -0
- package/src/pantheon/delegate-task-adapter.ts +444 -0
- package/src/pantheon/delegation-notify.ts +35 -20
- package/src/pantheon/model-command.ts +86 -32
- package/src/pantheon/token-opt.ts +555 -0
- package/src/pantheon/toon-codec.ts +481 -0
- package/src/pantheon/tui-version-check.ts +1 -3
- package/src/pantheon/v2-bridge.ts +129 -0
- package/src/pantheon/v2-events.ts +131 -0
- package/src/pantheon/v2-hooks.ts +287 -0
- package/src/pantheon/v2-tool-definitions.ts +331 -0
- package/src/plugin-v2/index.ts +21 -0
- package/src/plugin-v2.ts +836 -0
- package/src/plugin.ts +57 -10
- package/src/plugins/tui/dist/server.d.ts +1 -1
- package/src/plugins/tui/dist/server.d.ts.map +1 -1
- package/src/plugins/tui/dist/server.js +1 -1
- package/src/plugins/tui/dist/server.js.map +1 -1
- package/src/plugins/tui/dist/tui.d.ts +10 -7
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +11 -8
- package/src/plugins/tui/dist/tui.js.map +1 -1
- package/src/plugins/tui/dist/tui.tsx +22 -16
- package/src/plugins/tui/package-lock.json +51 -51
- package/src/plugins/tui/package.json +7 -7
- package/src/plugins/tui/src/index.tsx +22 -16
- package/src/skills/README.md +0 -2
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test-opencode-v1-v2-sandbox.sh — Pantheon global-install sandbox validator
|
|
3
|
+
# for OpenCode V1 (`opencode`) and V2 (`opencode2`) side-by-side.
|
|
4
|
+
#
|
|
5
|
+
# Validates the installed pantheon-opencode package AS A REAL USER inside an
|
|
6
|
+
# isolated sandbox (own HOME, npm prefix and venv), never the dev environment.
|
|
7
|
+
#
|
|
8
|
+
# Modes (combinable):
|
|
9
|
+
# --prepare Build tarball, install pantheon-opencode + OpenCode V1/V2
|
|
10
|
+
# binaries in the sandbox prefix, headless init, and generate
|
|
11
|
+
# the per-version project configs and sandbox run-test.sh.
|
|
12
|
+
# --run v1|v2 Base validation for one version: opencode mcp list + doctor
|
|
13
|
+
# (config is regenerated for that version first).
|
|
14
|
+
# --prompts Prompt battery via `opencode run --format json` for every
|
|
15
|
+
# requested version (with --run vX) or both versions alone.
|
|
16
|
+
# --reset Wipe the sandbox root.
|
|
17
|
+
# --help Usage.
|
|
18
|
+
#
|
|
19
|
+
# Env overrides:
|
|
20
|
+
# PANTHEON_SANDBOX_ROOT Sandbox root (default: ~/pantheon-sandbox)
|
|
21
|
+
# OPENCODE_V1_SPEC npm spec providing the `opencode` binary
|
|
22
|
+
# (default: opencode-ai@1.18.18)
|
|
23
|
+
# OPENCODE_V2_SPEC npm spec providing the `opencode2` binary
|
|
24
|
+
# (default: @opencode-ai/cli@beta)
|
|
25
|
+
# PANTHEON_SANDBOX_MODEL Model used by init/prompts
|
|
26
|
+
# (default: opencode-go/mimo-v2.5)
|
|
27
|
+
# PANTHEON_PROMPT_TIMEOUT Per-prompt timeout in seconds (default: 300)
|
|
28
|
+
# PANTHEON_RETRY_COOLDOWN Cooldown seconds before the single FAIL retry
|
|
29
|
+
# (default: 60; retry only for real FAIL, never AMBIENTAL)
|
|
30
|
+
#
|
|
31
|
+
# Exit codes: 0 = no real failures (AMBIENTAL issues do not fail the run),
|
|
32
|
+
# 1 = real FAIL (see prompts-report.md in the sandbox root),
|
|
33
|
+
# 2 = usage error, 3 = sandbox not prepared.
|
|
34
|
+
set -euo pipefail
|
|
35
|
+
|
|
36
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
37
|
+
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
38
|
+
|
|
39
|
+
SANDBOX_ROOT="${PANTHEON_SANDBOX_ROOT:-$HOME/pantheon-sandbox}"
|
|
40
|
+
SANDBOX_HOME="$SANDBOX_ROOT/home"
|
|
41
|
+
NPM_PREFIX="$SANDBOX_HOME/.npm-global"
|
|
42
|
+
|
|
43
|
+
OPENCODE_V1_SPEC="${OPENCODE_V1_SPEC:-opencode-ai@1.18.18}"
|
|
44
|
+
OPENCODE_V2_SPEC="${OPENCODE_V2_SPEC:-@opencode-ai/cli@beta}"
|
|
45
|
+
PANTHEON_SANDBOX_MODEL="${PANTHEON_SANDBOX_MODEL:-opencode-go/mimo-v2.5}"
|
|
46
|
+
PANTHEON_PROMPT_TIMEOUT="${PANTHEON_PROMPT_TIMEOUT:-300}"
|
|
47
|
+
|
|
48
|
+
V1_BIN="${OPENCODE_V1_BIN:-opencode}"
|
|
49
|
+
V2_BIN="${OPENCODE_V2_BIN:-opencode2}"
|
|
50
|
+
|
|
51
|
+
REPORT_FILE="$SANDBOX_ROOT/prompts-report.md"
|
|
52
|
+
EXTRACT_PY="$SANDBOX_ROOT/.prompt-extract-json.py"
|
|
53
|
+
|
|
54
|
+
MODE_PREPARE=0
|
|
55
|
+
MODE_RESET=0
|
|
56
|
+
MODE_PROMPTS=0
|
|
57
|
+
RUN_VERSION=""
|
|
58
|
+
|
|
59
|
+
usage() {
|
|
60
|
+
sed -n '2,40p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
61
|
+
exit 2
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
log() { printf '%s\n' "$*"; }
|
|
65
|
+
warn() { printf 'WARN: %s\n' "$*" >&2; }
|
|
66
|
+
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
|
67
|
+
|
|
68
|
+
bin_for() {
|
|
69
|
+
case "$1" in
|
|
70
|
+
v1) printf '%s' "$V1_BIN" ;;
|
|
71
|
+
v2) printf '%s' "$V2_BIN" ;;
|
|
72
|
+
*) return 1 ;;
|
|
73
|
+
esac
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
project_for() { printf '%s/project-%s' "$SANDBOX_ROOT" "$1"; }
|
|
77
|
+
|
|
78
|
+
# Resolve a binary STRICTLY inside the sandbox npm prefix — never fall back to
|
|
79
|
+
# the dev PATH, otherwise a non-prepared sandbox would silently test the host
|
|
80
|
+
# installation.
|
|
81
|
+
sandbox_bin_for() { # name → prints path; rc 1 if not inside sandbox prefix
|
|
82
|
+
local p
|
|
83
|
+
p="$(command -v "$1" 2>/dev/null || true)"
|
|
84
|
+
case "$p" in
|
|
85
|
+
"$NPM_PREFIX"/*) printf '%s' "$p" ;;
|
|
86
|
+
*) return 1 ;;
|
|
87
|
+
esac
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
sandbox_bin_v() { # v → echoes path; rc 1 if missing
|
|
91
|
+
sandbox_bin_for "$(bin_for "$1")"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# ── Sandbox environment isolation ─────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
sandbox_env() {
|
|
97
|
+
export HOME="$SANDBOX_HOME"
|
|
98
|
+
export PATH="$NPM_PREFIX/bin:$HOME/.config/opencode/.venv/bin:$PATH"
|
|
99
|
+
export npm_config_prefix="$NPM_PREFIX"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# ── Gate (b): sandbox run-test.sh with pantheon://agents content validation ──
|
|
103
|
+
|
|
104
|
+
write_run_test_sh() {
|
|
105
|
+
cat > "$SANDBOX_ROOT/run-test.sh" <<'RUNTEST'
|
|
106
|
+
#!/usr/bin/env bash
|
|
107
|
+
# Generated by pantheon-opencode scripts/test-opencode-v1-v2-sandbox.sh --prepare
|
|
108
|
+
# Validates the global install: binaries, 5/5 MCP connectivity, doctor 0 errors,
|
|
109
|
+
# AND the CONTENT of the pantheon://agents resource (zeus/hermes present).
|
|
110
|
+
set -euo pipefail
|
|
111
|
+
|
|
112
|
+
SANDBOX_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
113
|
+
SANDBOX_HOME="$SANDBOX_DIR/home"
|
|
114
|
+
NPM_PREFIX="$SANDBOX_HOME/.npm-global"
|
|
115
|
+
PANTHEON_GLOBAL="$SANDBOX_HOME/.config/opencode"
|
|
116
|
+
SANDBOX_VENV="$PANTHEON_GLOBAL/.venv"
|
|
117
|
+
REPO_DIR="${PANTHEON_REPO:-$(cd "$SANDBOX_DIR/../pantheon" && pwd)}"
|
|
118
|
+
|
|
119
|
+
echo "=== Pantheon Sandbox Test ==="
|
|
120
|
+
echo "Sandbox: $SANDBOX_DIR"
|
|
121
|
+
echo "Repo: $REPO_DIR"
|
|
122
|
+
|
|
123
|
+
export HOME="$SANDBOX_HOME"
|
|
124
|
+
export PATH="$NPM_PREFIX/bin:$SANDBOX_VENV/bin:$PATH"
|
|
125
|
+
export npm_config_prefix="$NPM_PREFIX"
|
|
126
|
+
|
|
127
|
+
echo "--- Binaries ---"
|
|
128
|
+
opencode --version
|
|
129
|
+
opencode2 --version
|
|
130
|
+
|
|
131
|
+
echo "--- Building tarball ---"
|
|
132
|
+
cd "$REPO_DIR"
|
|
133
|
+
npm pack 2>/dev/null || { echo "ERROR: npm pack failed"; exit 1; }
|
|
134
|
+
TGZ=$(ls -t pantheon-opencode-*.tgz | head -1)
|
|
135
|
+
npm rm -g pantheon-opencode 2>/dev/null || true
|
|
136
|
+
npm install -g "$TGZ"
|
|
137
|
+
|
|
138
|
+
echo "--- Init (headless) ---"
|
|
139
|
+
pantheon-opencode init --headless -y
|
|
140
|
+
|
|
141
|
+
echo "--- MCP List (V1) ---"
|
|
142
|
+
opencode mcp list
|
|
143
|
+
|
|
144
|
+
echo "--- MCP List (V2) ---"
|
|
145
|
+
opencode2 mcp list
|
|
146
|
+
|
|
147
|
+
echo "--- Doctor ---"
|
|
148
|
+
pantheon-opencode doctor --target "$SANDBOX_DIR"
|
|
149
|
+
|
|
150
|
+
echo "--- Gate (b): pantheon://agents CONTENT check ---"
|
|
151
|
+
RESOURCES_SRV="$PANTHEON_GLOBAL/scripts/mcp_resources_server.py"
|
|
152
|
+
if [ ! -f "$RESOURCES_SRV" ]; then
|
|
153
|
+
echo "ERROR: resources server not found at $RESOURCES_SRV"; exit 1
|
|
154
|
+
fi
|
|
155
|
+
AGENTS_CONTENT="$("$SANDBOX_VENV/bin/python" - "$RESOURCES_SRV" <<'PYEOF'
|
|
156
|
+
import asyncio, importlib.util, sys
|
|
157
|
+
spec = importlib.util.spec_from_file_location("mrs", sys.argv[1])
|
|
158
|
+
mod = importlib.util.module_from_spec(spec)
|
|
159
|
+
spec.loader.exec_module(mod)
|
|
160
|
+
print(asyncio.run(mod.list_agents()))
|
|
161
|
+
PYEOF
|
|
162
|
+
)"
|
|
163
|
+
printf '%s\n' "$AGENTS_CONTENT" | head -8
|
|
164
|
+
if printf '%s\n' "$AGENTS_CONTENT" | grep -qi 'zeus' \
|
|
165
|
+
&& printf '%s\n' "$AGENTS_CONTENT" | grep -qi 'hermes'; then
|
|
166
|
+
echo "OK: pantheon://agents content includes zeus and hermes"
|
|
167
|
+
else
|
|
168
|
+
echo "ERROR: pantheon://agents content missing zeus/hermes (connectivity alone is not enough)"; exit 1
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
echo ""
|
|
172
|
+
echo "=== VALIDATION COMPLETE ==="
|
|
173
|
+
echo "Next: open the isolated TUI with: $SANDBOX_DIR/start-pantheon.sh"
|
|
174
|
+
RUNTEST
|
|
175
|
+
chmod +x "$SANDBOX_ROOT/run-test.sh"
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# ── Reset / Prepare ───────────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
cmd_reset() {
|
|
181
|
+
case "$SANDBOX_ROOT" in
|
|
182
|
+
"/" | "$HOME" | "$REPO_DIR" | "$REPO_DIR"/*)
|
|
183
|
+
die "refusing to reset unsafe sandbox root: $SANDBOX_ROOT" ;;
|
|
184
|
+
esac
|
|
185
|
+
log "Resetting sandbox: $SANDBOX_ROOT"
|
|
186
|
+
rm -rf "$SANDBOX_ROOT"
|
|
187
|
+
log "Sandbox removed."
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
install_binaries() {
|
|
191
|
+
log "--- Installing pantheon-opencode (from repo tarball) ---"
|
|
192
|
+
cd "$REPO_DIR"
|
|
193
|
+
npm pack 2>/dev/null || die "npm pack failed"
|
|
194
|
+
local tgz
|
|
195
|
+
tgz=$(ls -t pantheon-opencode-*.tgz | head -1)
|
|
196
|
+
npm rm -g pantheon-opencode 2>/dev/null || true
|
|
197
|
+
npm install -g "$REPO_DIR/$tgz" || die "install of $tgz failed"
|
|
198
|
+
log "--- Installing OpenCode V1 ($OPENCODE_V1_SPEC) ---"
|
|
199
|
+
npm install -g "$OPENCODE_V1_SPEC" \
|
|
200
|
+
|| warn "V1 binary install failed ($OPENCODE_V1_SPEC) — v1 prompts will be AMBIENTAL"
|
|
201
|
+
log "--- Installing OpenCode V2 ($OPENCODE_V2_SPEC) ---"
|
|
202
|
+
npm install -g "$OPENCODE_V2_SPEC" \
|
|
203
|
+
|| warn "V2 binary install failed ($OPENCODE_V2_SPEC) — v2 prompts will be AMBIENTAL"
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
prepare_project() {
|
|
207
|
+
local v="$1" dir pan
|
|
208
|
+
dir="$(project_for "$v")"
|
|
209
|
+
mkdir -p "$dir"
|
|
210
|
+
pan="$(sandbox_bin_for "pantheon-opencode")" \
|
|
211
|
+
|| die "pantheon-opencode not installed in sandbox prefix — run $0 --prepare first"
|
|
212
|
+
log "--- Regenerating config for $v in $dir ---"
|
|
213
|
+
(cd "$dir" && "$pan" init --project --version "$v" --model "$PANTHEON_SANDBOX_MODEL" -y) \
|
|
214
|
+
|| die "init --project --version $v failed in $dir"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
cmd_prepare() {
|
|
218
|
+
mkdir -p "$SANDBOX_HOME" "$NPM_PREFIX"
|
|
219
|
+
write_run_test_sh
|
|
220
|
+
sandbox_env
|
|
221
|
+
install_binaries
|
|
222
|
+
local pan
|
|
223
|
+
pan="$(sandbox_bin_for "pantheon-opencode")" \
|
|
224
|
+
|| die "pantheon-opencode not found in sandbox prefix after install"
|
|
225
|
+
log "--- Headless init (global venv + runtime) ---"
|
|
226
|
+
"$pan" init --headless -y --model "$PANTHEON_SANDBOX_MODEL" \
|
|
227
|
+
|| die "pantheon-opencode init --headless failed"
|
|
228
|
+
prepare_project v1
|
|
229
|
+
prepare_project v2
|
|
230
|
+
write_extract_py
|
|
231
|
+
log "Prepare complete. Sandbox: $SANDBOX_ROOT"
|
|
232
|
+
log "Next: $0 --run v1 --prompts | $0 --run v2 --prompts"
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
# ── JSON text extraction (schema-agnostic) ────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
write_extract_py() {
|
|
238
|
+
cat > "$EXTRACT_PY" <<'PYEOF'
|
|
239
|
+
"""Collect every string value from opencode run --format json output."""
|
|
240
|
+
import json
|
|
241
|
+
import sys
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def walk(obj, out):
|
|
245
|
+
if isinstance(obj, str):
|
|
246
|
+
out.append(obj)
|
|
247
|
+
elif isinstance(obj, dict):
|
|
248
|
+
for value in obj.values():
|
|
249
|
+
walk(value, out)
|
|
250
|
+
elif isinstance(obj, list):
|
|
251
|
+
for value in obj:
|
|
252
|
+
walk(value, out)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
raw = sys.stdin.read()
|
|
256
|
+
chunks = []
|
|
257
|
+
try:
|
|
258
|
+
walk(json.loads(raw), chunks)
|
|
259
|
+
except Exception:
|
|
260
|
+
for line in raw.splitlines():
|
|
261
|
+
line = line.strip()
|
|
262
|
+
if not line:
|
|
263
|
+
continue
|
|
264
|
+
try:
|
|
265
|
+
walk(json.loads(line), chunks)
|
|
266
|
+
except Exception:
|
|
267
|
+
chunks.append(line)
|
|
268
|
+
print("\n".join(chunks))
|
|
269
|
+
PYEOF
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
# ── Prompt battery ────────────────────────────────────────────────────────────
|
|
273
|
+
|
|
274
|
+
# Known environmental failure signatures (never fail the run).
|
|
275
|
+
AMBIENTAL_RE='bifrost|unauthorized|authentication failure|api key|credentials|401|docker|econnrefused|enotfound|etimedout|econnreset|connection refused|rate.?limit|429|quota|overloaded|edgesout|npm err'
|
|
276
|
+
|
|
277
|
+
PROMPT_IDS=()
|
|
278
|
+
PROMPT_TEXTS=()
|
|
279
|
+
PROMPT_MARKERS=()
|
|
280
|
+
PROMPT_AMB_MARKERS=()
|
|
281
|
+
PROMPT_VERIFY=()
|
|
282
|
+
|
|
283
|
+
add_prompt() { # id marker ambiental_marker verify text
|
|
284
|
+
PROMPT_IDS+=("$1")
|
|
285
|
+
PROMPT_MARKERS+=("$2")
|
|
286
|
+
PROMPT_AMB_MARKERS+=("$3")
|
|
287
|
+
PROMPT_VERIFY+=("$4")
|
|
288
|
+
PROMPT_TEXTS+=("$5")
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
build_battery() {
|
|
292
|
+
local v="$1"
|
|
293
|
+
PROMPT_IDS=()
|
|
294
|
+
PROMPT_TEXTS=()
|
|
295
|
+
PROMPT_MARKERS=()
|
|
296
|
+
PROMPT_AMB_MARKERS=()
|
|
297
|
+
PROMPT_VERIFY=()
|
|
298
|
+
add_prompt "agents-resource" "AGENTS-OK" "AGENTS-MISSING" "" \
|
|
299
|
+
"List your MCP resources and read the resource pantheon://agents. If the returned agent list includes an agent named 'zeus' and one named 'hermes', reply with exactly: AGENTS-OK. Otherwise reply with exactly: AGENTS-MISSING."
|
|
300
|
+
add_prompt "memory-store" "MEMORY-STORE-OK" "" "" \
|
|
301
|
+
"Use the pantheon-memory memory_store tool to store this exact entry: namespace 'default', key 'pantheon-sandbox-probe-$v', value 'alive'. Then reply with exactly: MEMORY-STORE-OK"
|
|
302
|
+
add_prompt "memory-recall" "MEMORY-RECALL-OK" "" "" \
|
|
303
|
+
"Use the pantheon-memory memory_recall tool (or memory_search) to look up key 'pantheon-sandbox-probe-$v' in namespace 'default'. If the stored value is 'alive', reply with exactly: MEMORY-RECALL-OK"
|
|
304
|
+
add_prompt "tmp-write-read" "TMP-OK" "" \
|
|
305
|
+
"${TMPDIR:-/tmp}/pantheon-sandbox-probe-$v.txt::TMP-OK" \
|
|
306
|
+
"Using the bash tool, run a python3 -c command that writes the text TMP-OK to the file ${TMPDIR:-/tmp}/pantheon-sandbox-probe-$v.txt (overwrite it), then run another python3 -c command to read that file back. Do not use any other tool or command to write or read the file — only python3 -c. If what you read back is exactly TMP-OK, reply with exactly: TMP-OK"
|
|
307
|
+
add_prompt "delegation" "DELEGATION-OK" "DELEGATION-UNAVAILABLE" "" \
|
|
308
|
+
"Delegate a tiny task to the @talos agent via the task tool: ask it to reply with the single word PONG. If the delegated response contains PONG, reply with exactly: DELEGATION-OK. If you cannot delegate to agents in this context, reply with exactly: DELEGATION-UNAVAILABLE"
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
declare -A RESULTS
|
|
312
|
+
declare -A DETAILS
|
|
313
|
+
|
|
314
|
+
record() { # v id result detail
|
|
315
|
+
RESULTS["$1:$2"]="$3"
|
|
316
|
+
DETAILS["$1:$2"]="$4"
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
extract_text() { "$HOME/.config/opencode/.venv/bin/python" "$EXTRACT_PY" < "$1"; }
|
|
320
|
+
|
|
321
|
+
ATTEMPT_RESULT=""
|
|
322
|
+
ATTEMPT_DETAIL=""
|
|
323
|
+
|
|
324
|
+
run_prompt_attempt() { # v idx current_bin — sets ATTEMPT_RESULT / ATTEMPT_DETAIL
|
|
325
|
+
local v="$1" idx="$2" bin="$3" id marker amb_marker verify text
|
|
326
|
+
id="${PROMPT_IDS[$idx]}"
|
|
327
|
+
marker="${PROMPT_MARKERS[$idx]}"
|
|
328
|
+
amb_marker="${PROMPT_AMB_MARKERS[$idx]}"
|
|
329
|
+
verify="${PROMPT_VERIFY[$idx]}"
|
|
330
|
+
text="${PROMPT_TEXTS[$idx]}"
|
|
331
|
+
|
|
332
|
+
# Remove the probe file first so post-run verification reflects THIS run.
|
|
333
|
+
if [ -n "$verify" ]; then
|
|
334
|
+
rm -f "${verify%%::*}"
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
local out_file err_file rc=0
|
|
338
|
+
out_file="$(mktemp)"
|
|
339
|
+
err_file="$(mktemp)"
|
|
340
|
+
|
|
341
|
+
set +e
|
|
342
|
+
(cd "$(project_for "$v")" \
|
|
343
|
+
&& timeout "$PANTHEON_PROMPT_TIMEOUT" "$bin" run --auto --format json "$text") \
|
|
344
|
+
> "$out_file" 2> "$err_file"
|
|
345
|
+
rc=$?
|
|
346
|
+
set -e
|
|
347
|
+
|
|
348
|
+
local extracted
|
|
349
|
+
extracted="$(extract_text "$out_file" || true)"
|
|
350
|
+
local raw
|
|
351
|
+
raw="$( { tr '\n' ' ' < "$out_file"; tr '\n' ' ' < "$err_file"; } | cut -c1-400)"
|
|
352
|
+
rm -f "$out_file" "$err_file"
|
|
353
|
+
|
|
354
|
+
if [ "$rc" -eq 124 ]; then
|
|
355
|
+
ATTEMPT_RESULT="AMBIENTAL"
|
|
356
|
+
ATTEMPT_DETAIL="timeout after ${PANTHEON_PROMPT_TIMEOUT}s (provider latency)"
|
|
357
|
+
elif printf '%s' "$extracted" | grep -q "$marker"; then
|
|
358
|
+
ATTEMPT_RESULT="PASS"
|
|
359
|
+
ATTEMPT_DETAIL="marker $marker found"
|
|
360
|
+
elif [ -n "$amb_marker" ] && printf '%s' "$extracted" | grep -q "$amb_marker"; then
|
|
361
|
+
ATTEMPT_RESULT="AMBIENTAL"
|
|
362
|
+
ATTEMPT_DETAIL="explicit $amb_marker"
|
|
363
|
+
elif [ -n "$verify" ] \
|
|
364
|
+
&& [ "$(cat "${verify%%::*}" 2>/dev/null)" = "${verify##*::}" ]; then
|
|
365
|
+
ATTEMPT_RESULT="PASS"
|
|
366
|
+
ATTEMPT_DETAIL="probe file verified: ${verify%%::*} contains ${verify##*::}"
|
|
367
|
+
elif printf '%s' "$raw" | grep -Eiq "$AMBIENTAL_RE"; then
|
|
368
|
+
local sig
|
|
369
|
+
sig="$(printf '%s' "$raw" | grep -Eio "$AMBIENTAL_RE" | head -1)"
|
|
370
|
+
ATTEMPT_RESULT="AMBIENTAL"
|
|
371
|
+
ATTEMPT_DETAIL="environmental signature: $sig"
|
|
372
|
+
else
|
|
373
|
+
ATTEMPT_RESULT="FAIL"
|
|
374
|
+
ATTEMPT_DETAIL="marker $marker absent; rc=$rc; snippet: $(printf '%s' "$raw" | cut -c1-160)"
|
|
375
|
+
fi
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
RETRY_COOLDOWN="${PANTHEON_RETRY_COOLDOWN:-60}"
|
|
379
|
+
RETRIES_USED=0
|
|
380
|
+
|
|
381
|
+
run_prompt() { # v idx current_bin — ONE retry after cooldown for real FAIL only
|
|
382
|
+
local v="$1" idx="$2" bin="$3" id first_detail
|
|
383
|
+
id="${PROMPT_IDS[$idx]}"
|
|
384
|
+
run_prompt_attempt "$v" "$idx" "$bin"
|
|
385
|
+
if [ "$ATTEMPT_RESULT" = "FAIL" ]; then
|
|
386
|
+
first_detail="$ATTEMPT_DETAIL"
|
|
387
|
+
log "[$v] prompt $id FAIL on first attempt — retrying once after ${RETRY_COOLDOWN}s cooldown"
|
|
388
|
+
sleep "$RETRY_COOLDOWN"
|
|
389
|
+
run_prompt_attempt "$v" "$idx" "$bin"
|
|
390
|
+
RETRIES_USED=$((RETRIES_USED + 1))
|
|
391
|
+
if [ "$ATTEMPT_RESULT" != "FAIL" ]; then
|
|
392
|
+
ATTEMPT_DETAIL="$ATTEMPT_DETAIL [retry used — first attempt: $first_detail]"
|
|
393
|
+
else
|
|
394
|
+
ATTEMPT_DETAIL="$ATTEMPT_DETAIL [FAIL persisted after retry — first attempt: $first_detail]"
|
|
395
|
+
fi
|
|
396
|
+
fi
|
|
397
|
+
record "$v" "$id" "$ATTEMPT_RESULT" "$ATTEMPT_DETAIL"
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
check_mcp_list() { # v
|
|
401
|
+
local v="$1" bin out rc=0
|
|
402
|
+
if ! bin="$(sandbox_bin_v "$v")"; then
|
|
403
|
+
record "$v" "mcp-list" "AMBIENTAL" "binary not installed in sandbox prefix"
|
|
404
|
+
return 0
|
|
405
|
+
fi
|
|
406
|
+
set +e
|
|
407
|
+
out="$(cd "$(project_for "$v")" && "$bin" mcp list 2>&1)"
|
|
408
|
+
rc=$?
|
|
409
|
+
set -e
|
|
410
|
+
if [ "$rc" -ne 0 ]; then
|
|
411
|
+
record "$v" "mcp-list" "FAIL" "opencode mcp list exited $rc"
|
|
412
|
+
elif printf '%s' "$out" | grep -qi "connected" \
|
|
413
|
+
&& ! printf '%s' "$out" | grep -Eiq "failed|✘"; then
|
|
414
|
+
record "$v" "mcp-list" "PASS" "$(printf '%s' "$out" | grep -ci 'connected') connected"
|
|
415
|
+
else
|
|
416
|
+
record "$v" "mcp-list" "FAIL" "no connected MCPs or failures reported"
|
|
417
|
+
fi
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
check_doctor() { # v
|
|
421
|
+
local v="$1" pan rc=0
|
|
422
|
+
if ! pan="$(sandbox_bin_for "pantheon-opencode")"; then
|
|
423
|
+
record "$v" "doctor" "AMBIENTAL" "pantheon-opencode not installed in sandbox prefix"
|
|
424
|
+
return 0
|
|
425
|
+
fi
|
|
426
|
+
set +e
|
|
427
|
+
(cd "$(project_for "$v")" && "$pan" doctor --target "$(project_for "$v")") > /dev/null 2>&1
|
|
428
|
+
rc=$?
|
|
429
|
+
set -e
|
|
430
|
+
if [ "$rc" -eq 0 ]; then
|
|
431
|
+
record "$v" "doctor" "PASS" "exit 0"
|
|
432
|
+
else
|
|
433
|
+
record "$v" "doctor" "FAIL" "doctor exited $rc (blocking errors)"
|
|
434
|
+
fi
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
run_version_prompts() { # v
|
|
438
|
+
local v="$1" bin
|
|
439
|
+
if ! bin="$(sandbox_bin_v "$v")"; then
|
|
440
|
+
log "Binary '$(bin_for "$v")' not in sandbox prefix — marking all $v prompts AMBIENTAL."
|
|
441
|
+
build_battery "$v"
|
|
442
|
+
local i
|
|
443
|
+
for i in "${!PROMPT_IDS[@]}"; do
|
|
444
|
+
record "$v" "${PROMPT_IDS[$i]}" "AMBIENTAL" "binary $(bin_for "$v") not installed in sandbox"
|
|
445
|
+
done
|
|
446
|
+
record "$v" "mcp-list" "AMBIENTAL" "binary $(bin_for "$v") not installed in sandbox"
|
|
447
|
+
record "$v" "doctor" "AMBIENTAL" "binary $(bin_for "$v") not installed in sandbox"
|
|
448
|
+
return 0
|
|
449
|
+
fi
|
|
450
|
+
prepare_project "$v"
|
|
451
|
+
build_battery "$v"
|
|
452
|
+
local i
|
|
453
|
+
for i in "${!PROMPT_IDS[@]}"; do
|
|
454
|
+
log "[$v] prompt ${PROMPT_IDS[$i]} ..."
|
|
455
|
+
run_prompt "$v" "$i" "$bin"
|
|
456
|
+
log "[$v] prompt ${PROMPT_IDS[$i]} → ${RESULTS["$v:${PROMPT_IDS[$i]}"]}"
|
|
457
|
+
done
|
|
458
|
+
check_mcp_list "$v"
|
|
459
|
+
check_doctor "$v"
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
write_report() { # versions...
|
|
463
|
+
local versions=("$@") total_fail=0 total_pass=0 total_amb=0
|
|
464
|
+
{
|
|
465
|
+
printf '# Pantheon Sandbox Prompts Report\n\n'
|
|
466
|
+
printf -- '- **Date:** %s\n' "$(date -u '+%Y-%m-%d %H:%M UTC')"
|
|
467
|
+
printf -- '- **Model:** %s\n' "$PANTHEON_SANDBOX_MODEL"
|
|
468
|
+
printf -- '- **Sandbox:** %s\n' "$SANDBOX_ROOT"
|
|
469
|
+
printf -- '- **V1 binary:** %s (%s) | **V2 binary:** %s (%s)\n\n' \
|
|
470
|
+
"$V1_BIN" "$OPENCODE_V1_SPEC" "$V2_BIN" "$OPENCODE_V2_SPEC"
|
|
471
|
+
local v id res
|
|
472
|
+
for v in "${versions[@]}"; do
|
|
473
|
+
printf '## Version %s (`%s`)\n\n' "$v" "$(bin_for "$v")"
|
|
474
|
+
printf '| Prompt | Result | Detail |\n|---|---|---|\n'
|
|
475
|
+
for id in "${PROMPT_IDS[@]}" mcp-list doctor; do
|
|
476
|
+
res="${RESULTS["$v:$id"]:-SKIP}"
|
|
477
|
+
case "$res" in
|
|
478
|
+
PASS) total_pass=$((total_pass + 1)) ;;
|
|
479
|
+
AMBIENTAL) total_amb=$((total_amb + 1)) ;;
|
|
480
|
+
FAIL) total_fail=$((total_fail + 1)) ;;
|
|
481
|
+
esac
|
|
482
|
+
printf '| %s | %s | %s |\n' "$id" "$res" "${DETAILS["$v:$id"]:-—}"
|
|
483
|
+
done
|
|
484
|
+
printf '\n'
|
|
485
|
+
done
|
|
486
|
+
printf '## Summary\n\n'
|
|
487
|
+
printf -- '- PASS: %d\n- FAIL (real): %d\n- AMBIENTAL: %d\n- Retries used: %d\n\n' \
|
|
488
|
+
"$total_pass" "$total_fail" "$total_amb" "$RETRIES_USED"
|
|
489
|
+
if [ "$total_fail" -eq 0 ]; then
|
|
490
|
+
printf '**Verdict: PASS** — no real failures (AMBIENTAL issues do not block).\n'
|
|
491
|
+
else
|
|
492
|
+
printf '**Verdict: FAIL** — real failures detected (see table above).\n'
|
|
493
|
+
fi
|
|
494
|
+
} > "$REPORT_FILE"
|
|
495
|
+
log "Report written to $REPORT_FILE"
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
run_battery() { # versions...
|
|
499
|
+
local versions=("$@")
|
|
500
|
+
mkdir -p "$SANDBOX_ROOT"
|
|
501
|
+
write_extract_py
|
|
502
|
+
local v
|
|
503
|
+
for v in "${versions[@]}"; do
|
|
504
|
+
run_version_prompts "$v"
|
|
505
|
+
done
|
|
506
|
+
write_report "${versions[@]}"
|
|
507
|
+
local v fail=0 id
|
|
508
|
+
for v in "${versions[@]}"; do
|
|
509
|
+
for id in "${PROMPT_IDS[@]}" mcp-list doctor; do
|
|
510
|
+
[ "${RESULTS["$v:$id"]:-}" = "FAIL" ] && fail=$((fail + 1)) || true
|
|
511
|
+
done
|
|
512
|
+
done
|
|
513
|
+
if [ "$fail" -gt 0 ]; then
|
|
514
|
+
log "Prompts battery: $fail real FAIL(s)."
|
|
515
|
+
exit 1
|
|
516
|
+
fi
|
|
517
|
+
log "Prompts battery: no real failures."
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
# ── Base validation (no prompts) ──────────────────────────────────────────────
|
|
521
|
+
|
|
522
|
+
run_base() { # v
|
|
523
|
+
local v="$1" bin
|
|
524
|
+
if ! bin="$(sandbox_bin_v "$v")"; then
|
|
525
|
+
printf 'ERROR: sandbox not prepared for %s (binary '\''%s'\'' missing from sandbox prefix) — run %s --prepare first\n' \
|
|
526
|
+
"$v" "$(bin_for "$v")" "$0" >&2
|
|
527
|
+
exit 3
|
|
528
|
+
fi
|
|
529
|
+
prepare_project "$v"
|
|
530
|
+
build_battery "$v"
|
|
531
|
+
log "[$v] opencode mcp list"
|
|
532
|
+
check_mcp_list "$v"
|
|
533
|
+
log "[$v] mcp-list → ${RESULTS["$v:mcp-list"]}"
|
|
534
|
+
log "[$v] doctor"
|
|
535
|
+
check_doctor "$v"
|
|
536
|
+
log "[$v] doctor → ${RESULTS["$v:doctor"]}"
|
|
537
|
+
local fail=0
|
|
538
|
+
[ "${RESULTS["$v:mcp-list"]}" = "FAIL" ] && fail=$((fail + 1)) || true
|
|
539
|
+
[ "${RESULTS["$v:doctor"]}" = "FAIL" ] && fail=$((fail + 1)) || true
|
|
540
|
+
if [ "$fail" -gt 0 ]; then
|
|
541
|
+
log "Base validation $v: $fail real FAIL(s)."
|
|
542
|
+
exit 1
|
|
543
|
+
fi
|
|
544
|
+
log "Base validation $v: OK."
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
# ── CLI ───────────────────────────────────────────────────────────────────────
|
|
548
|
+
|
|
549
|
+
while [ $# -gt 0 ]; do
|
|
550
|
+
case "$1" in
|
|
551
|
+
--prepare) MODE_PREPARE=1 ;;
|
|
552
|
+
--reset) MODE_RESET=1 ;;
|
|
553
|
+
--prompts) MODE_PROMPTS=1 ;;
|
|
554
|
+
--run)
|
|
555
|
+
shift
|
|
556
|
+
case "${1:-}" in
|
|
557
|
+
v1 | v2) RUN_VERSION="$1" ;;
|
|
558
|
+
*) usage ;;
|
|
559
|
+
esac
|
|
560
|
+
;;
|
|
561
|
+
--help | -h) usage ;;
|
|
562
|
+
*) usage ;;
|
|
563
|
+
esac
|
|
564
|
+
shift
|
|
565
|
+
done
|
|
566
|
+
|
|
567
|
+
if [ "$MODE_RESET" -eq 1 ]; then
|
|
568
|
+
cmd_reset
|
|
569
|
+
exit 0
|
|
570
|
+
fi
|
|
571
|
+
|
|
572
|
+
if [ "$MODE_PREPARE" -eq 0 ] && [ -z "$RUN_VERSION" ] && [ "$MODE_PROMPTS" -eq 0 ]; then
|
|
573
|
+
usage
|
|
574
|
+
fi
|
|
575
|
+
|
|
576
|
+
if [ "$MODE_PREPARE" -eq 1 ]; then
|
|
577
|
+
cmd_prepare
|
|
578
|
+
fi
|
|
579
|
+
|
|
580
|
+
if [ -n "$RUN_VERSION" ]; then
|
|
581
|
+
if [ "$MODE_PROMPTS" -eq 1 ]; then
|
|
582
|
+
sandbox_env
|
|
583
|
+
run_battery "$RUN_VERSION"
|
|
584
|
+
else
|
|
585
|
+
sandbox_env
|
|
586
|
+
run_base "$RUN_VERSION"
|
|
587
|
+
fi
|
|
588
|
+
elif [ "$MODE_PROMPTS" -eq 1 ]; then
|
|
589
|
+
sandbox_env
|
|
590
|
+
run_battery v1 v2
|
|
591
|
+
fi
|