pantheon-opencode 1.5.2 → 1.6.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/README.md +4 -2
- package/.pantheon/code-mode/checkpoint_session.py +138 -102
- package/.pantheon/code-mode/compress-inline.py +102 -57
- package/.pantheon/code-mode/manifest.json +2 -8
- package/CHANGELOG.md +17 -0
- package/README.md +86 -52
- package/README.pt-BR.md +63 -43
- package/commands/pantheon-optimize.md +2 -2
- package/package.json +13 -1
- package/plugin.json +1 -1
- package/pyproject.toml +8 -15
- package/scripts/build-agents-md.mjs +2 -2
- package/scripts/check-plugin-v2-coverage.mjs +213 -0
- package/scripts/ci-validate-yaml.py +7 -5
- package/scripts/code_mode_server.py +116 -24
- package/scripts/doctor.mjs +85 -28
- package/scripts/install/cli-ui.mjs +1 -1
- package/scripts/install/config-migration.mjs +2 -2
- package/scripts/install/health-check.mjs +6 -19
- package/scripts/install/interactive.mjs +6 -6
- package/scripts/install/migrate.mjs +1 -1
- package/scripts/mcp_persistence_server.py +390 -76
- package/scripts/memory_mcp_server.py +370 -196
- package/scripts/postinstall.mjs +0 -7
- package/scripts/probe-context-rehydrate.mjs +28 -11
- package/scripts/prune.mjs +2 -2
- package/scripts/test-opencode-v2-sandbox.sh +1759 -0
- package/scripts/validate-routing.mjs +40 -0
- package/skills-lock.json +9 -10
- package/src/agents/mnemosyne.md +15 -16
- package/src/agents/zeus.md +1 -2
- package/src/mcp/code_mode_server.py +116 -24
- package/src/mcp/mcp_codemap_module.py +559 -129
- package/src/mcp/mcp_persistence_server.py +390 -76
- package/src/mcp/memory_mcp_server.py +371 -248
- package/src/mcp/pantheon_vision_server.py +136 -24
- package/src/mcp/requirements-mcp.txt +5 -3
- package/src/mcp/requirements-vision.txt +2 -0
- package/src/pantheon/model-command.ts +18 -7
- package/src/pantheon/todo-enforcer.ts +1 -1
- package/src/pantheon/vision.ts +1 -1
- package/src/plugin-v2.ts +134 -104
- package/src/plugins/tui/dist/tui.js +1 -1
- package/src/plugins/tui/package-lock.json +20 -20
- package/src/plugins/tui/package.json +12 -12
- package/src/routing.yml +0 -1
- package/src/skills/README.md +31 -36
- package/src/skills/auto-continue/SKILL.md +25 -75
- package/.pantheon/code-mode/eval-llm-judge.py +0 -255
- package/.pantheon/code-mode/eval-monte-carlo.py +0 -368
- package/.pantheon/code-mode/eval-run.py +0 -197
- package/.pantheon/code-mode/eval-static.py +0 -321
- package/.pantheon/code-mode/session-end-save.py +0 -186
- package/.pantheon/code-mode/session-end-save.sh +0 -18
- package/scripts/recover-zenodo-deposition.mjs +0 -146
- package/scripts/test-opencode-v1-v2-sandbox.sh +0 -862
- package/scripts/validate-zenodo-release.mjs +0 -154
- package/scripts/zenodo-artifact.mjs +0 -94
- package/scripts/zenodo-release-state.mjs +0 -53
- package/src/skills/orchestration-workflow/SKILL.md +0 -311
|
@@ -1,862 +0,0 @@
|
|
|
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
|
-
# --cost Offline pantheon_cost probe for the requested
|
|
17
|
-
# version (with --run vX) or both versions alone.
|
|
18
|
-
# The probe's synthetic checks run in explicit --fixture mode.
|
|
19
|
-
# --rehydrate Offline context_rehydrate + context_session_summary
|
|
20
|
-
# probe for the requested version (with --run vX) or both.
|
|
21
|
-
# --reset Wipe the sandbox root.
|
|
22
|
-
# --help Usage.
|
|
23
|
-
#
|
|
24
|
-
# Env overrides:
|
|
25
|
-
# PANTHEON_SANDBOX_ROOT Sandbox root (default: ~/pantheon-sandbox)
|
|
26
|
-
# OPENCODE_V1_SPEC npm spec providing the `opencode` binary
|
|
27
|
-
# (default: opencode-ai@1.18.18)
|
|
28
|
-
# OPENCODE_V2_SPEC npm spec providing the `opencode2` binary
|
|
29
|
-
# (default: @opencode-ai/cli@beta)
|
|
30
|
-
# PANTHEON_SANDBOX_MODEL Model used by init/prompts
|
|
31
|
-
# (default: opencode-go/mimo-v2.5)
|
|
32
|
-
# PANTHEON_PROMPT_TIMEOUT Per-prompt timeout in seconds (default: 300)
|
|
33
|
-
# Exit codes: 0 = every required check returned explicit PASS,
|
|
34
|
-
# 1 = any failure or untested check (see reports in the sandbox root),
|
|
35
|
-
# 2 = usage error, 3 = sandbox not prepared.
|
|
36
|
-
set -euo pipefail
|
|
37
|
-
|
|
38
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
39
|
-
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
40
|
-
|
|
41
|
-
SANDBOX_ROOT="${PANTHEON_SANDBOX_ROOT:-$HOME/pantheon-sandbox}"
|
|
42
|
-
SANDBOX_HOME="$SANDBOX_ROOT/home"
|
|
43
|
-
NPM_PREFIX="$SANDBOX_HOME/.npm-global"
|
|
44
|
-
|
|
45
|
-
OPENCODE_V1_SPEC="${OPENCODE_V1_SPEC:-opencode-ai@1.18.18}"
|
|
46
|
-
OPENCODE_V2_SPEC="${OPENCODE_V2_SPEC:-@opencode-ai/cli@beta}"
|
|
47
|
-
PANTHEON_SANDBOX_MODEL="${PANTHEON_SANDBOX_MODEL:-opencode-go/mimo-v2.5}"
|
|
48
|
-
PANTHEON_PROMPT_TIMEOUT="${PANTHEON_PROMPT_TIMEOUT:-300}"
|
|
49
|
-
|
|
50
|
-
V1_BIN="${OPENCODE_V1_BIN:-opencode}"
|
|
51
|
-
V2_BIN="${OPENCODE_V2_BIN:-opencode2}"
|
|
52
|
-
|
|
53
|
-
REPORT_FILE="$SANDBOX_ROOT/prompts-report.md"
|
|
54
|
-
COST_REPORT_FILE="$SANDBOX_ROOT/pantheon-cost-report.md"
|
|
55
|
-
REHYDRATE_REPORT_FILE="$SANDBOX_ROOT/context-rehydrate-report.md"
|
|
56
|
-
EXTRACT_PY="$SANDBOX_ROOT/.prompt-extract-json.py"
|
|
57
|
-
|
|
58
|
-
MODE_PREPARE=0
|
|
59
|
-
MODE_RESET=0
|
|
60
|
-
MODE_PROMPTS=0
|
|
61
|
-
MODE_COST=0
|
|
62
|
-
MODE_REHYDRATE=0
|
|
63
|
-
RUN_VERSION=""
|
|
64
|
-
|
|
65
|
-
usage() {
|
|
66
|
-
sed -n '2,40p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
67
|
-
exit 2
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
log() { printf '%s\n' "$*"; }
|
|
71
|
-
warn() { printf 'WARN: %s\n' "$*" >&2; }
|
|
72
|
-
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
|
73
|
-
|
|
74
|
-
bin_for() {
|
|
75
|
-
case "$1" in
|
|
76
|
-
v1) printf '%s' "$V1_BIN" ;;
|
|
77
|
-
v2) printf '%s' "$V2_BIN" ;;
|
|
78
|
-
*) return 1 ;;
|
|
79
|
-
esac
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
project_for() { printf '%s/project-%s' "$SANDBOX_ROOT" "$1"; }
|
|
83
|
-
|
|
84
|
-
# Resolve a binary STRICTLY inside the sandbox npm prefix — never fall back to
|
|
85
|
-
# the dev PATH, otherwise a non-prepared sandbox would silently test the host
|
|
86
|
-
# installation.
|
|
87
|
-
sandbox_bin_for() { # name → prints path; rc 1 if not inside sandbox prefix
|
|
88
|
-
local p
|
|
89
|
-
p="$(command -v "$1" 2>/dev/null || true)"
|
|
90
|
-
case "$p" in
|
|
91
|
-
"$NPM_PREFIX"/*) printf '%s' "$p" ;;
|
|
92
|
-
*) return 1 ;;
|
|
93
|
-
esac
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
sandbox_bin_v() { # v → echoes path; rc 1 if missing
|
|
97
|
-
sandbox_bin_for "$(bin_for "$1")"
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
# ── Sandbox environment isolation ─────────────────────────────────────────────
|
|
101
|
-
|
|
102
|
-
sandbox_env() {
|
|
103
|
-
export HOME="$SANDBOX_HOME"
|
|
104
|
-
export PATH="$NPM_PREFIX/bin:$HOME/.config/opencode/.venv/bin:$PATH"
|
|
105
|
-
export npm_config_prefix="$NPM_PREFIX"
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
# ── Gate (b): sandbox run-test.sh with pantheon://agents content validation ──
|
|
109
|
-
|
|
110
|
-
write_run_test_sh() {
|
|
111
|
-
cat > "$SANDBOX_ROOT/run-test.sh" <<'RUNTEST'
|
|
112
|
-
#!/usr/bin/env bash
|
|
113
|
-
# Generated by pantheon-opencode scripts/test-opencode-v1-v2-sandbox.sh --prepare
|
|
114
|
-
# Validates the global install: binaries, exactly 5 connected MCPs per version,
|
|
115
|
-
# doctor 0 errors, AND the CONTENT of pantheon://agents (zeus/hermes present).
|
|
116
|
-
set -euo pipefail
|
|
117
|
-
|
|
118
|
-
SANDBOX_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
119
|
-
SANDBOX_HOME="$SANDBOX_DIR/home"
|
|
120
|
-
NPM_PREFIX="$SANDBOX_HOME/.npm-global"
|
|
121
|
-
PANTHEON_GLOBAL="$SANDBOX_HOME/.config/opencode"
|
|
122
|
-
SANDBOX_VENV="$PANTHEON_GLOBAL/.venv"
|
|
123
|
-
REPO_DIR="${PANTHEON_REPO:-$(cd "$SANDBOX_DIR/../pantheon" && pwd)}"
|
|
124
|
-
|
|
125
|
-
echo "=== Pantheon Sandbox Test ==="
|
|
126
|
-
echo "Sandbox: $SANDBOX_DIR"
|
|
127
|
-
echo "Repo: $REPO_DIR"
|
|
128
|
-
|
|
129
|
-
export HOME="$SANDBOX_HOME"
|
|
130
|
-
export PATH="$NPM_PREFIX/bin:$SANDBOX_VENV/bin:$PATH"
|
|
131
|
-
export npm_config_prefix="$NPM_PREFIX"
|
|
132
|
-
|
|
133
|
-
STATUS_FAIL=0
|
|
134
|
-
STATUS_PASS=0
|
|
135
|
-
|
|
136
|
-
record_status() { # label status detail
|
|
137
|
-
local label="$1" status="$2" detail="$3"
|
|
138
|
-
case "$status" in
|
|
139
|
-
PASS) STATUS_PASS=$((STATUS_PASS + 1)) ;;
|
|
140
|
-
FAIL) STATUS_FAIL=$((STATUS_FAIL + 1)) ;;
|
|
141
|
-
*) STATUS_FAIL=$((STATUS_FAIL + 1)); status="FAIL" ;;
|
|
142
|
-
esac
|
|
143
|
-
printf '%s: %s — %s\n' "$status" "$label" "$detail"
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
check_binary() { # label binary
|
|
147
|
-
local label="$1" bin="$2" out rc=0
|
|
148
|
-
if ! command -v "$bin" >/dev/null 2>&1; then
|
|
149
|
-
record_status "$label" "FAIL" "binary '$bin' is not installed in the sandbox prefix"
|
|
150
|
-
return 0
|
|
151
|
-
fi
|
|
152
|
-
set +e
|
|
153
|
-
out="$("$bin" --version 2>&1)"
|
|
154
|
-
rc=$?
|
|
155
|
-
set -e
|
|
156
|
-
if [ "$rc" -eq 0 ]; then
|
|
157
|
-
record_status "$label" "PASS" "${out:-version command exited 0}"
|
|
158
|
-
else
|
|
159
|
-
record_status "$label" "FAIL" "version check exited $rc"
|
|
160
|
-
fi
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
check_mcp() { # label binary project
|
|
164
|
-
local label="$1" bin="$2" project="$3" out connected_count rc=0
|
|
165
|
-
if ! command -v "$bin" >/dev/null 2>&1; then
|
|
166
|
-
record_status "$label MCP" "FAIL" "binary '$bin' is not installed in the sandbox prefix"
|
|
167
|
-
return 0
|
|
168
|
-
fi
|
|
169
|
-
if [ ! -d "$project" ]; then
|
|
170
|
-
record_status "$label MCP" "FAIL" "project directory is missing: $project"
|
|
171
|
-
return 0
|
|
172
|
-
fi
|
|
173
|
-
set +e
|
|
174
|
-
out="$(cd "$project" && "$bin" mcp list 2>&1)"
|
|
175
|
-
rc=$?
|
|
176
|
-
set -e
|
|
177
|
-
out="$(printf '%s' "$out" | sed -E $'s/\x1B\\[[0-?]*[ -/]*[@-~]//g')"
|
|
178
|
-
printf '%s\n' "$out"
|
|
179
|
-
if [ "$rc" -ne 0 ]; then
|
|
180
|
-
record_status "$label MCP" "FAIL" "mcp list exited $rc"
|
|
181
|
-
return 0
|
|
182
|
-
fi
|
|
183
|
-
connected_count="$(printf '%s' "$out" | grep -Eic '(^|[^[:alnum:]_])connected([^[:alnum:]_]|$)')" || connected_count=0
|
|
184
|
-
if [ "$connected_count" -eq 5 ] \
|
|
185
|
-
&& ! printf '%s' "$out" | grep -Eiq 'failed|✘'; then
|
|
186
|
-
record_status "$label MCP" "PASS" "exactly 5 connected MCPs"
|
|
187
|
-
else
|
|
188
|
-
record_status "$label MCP" "FAIL" "expected exactly 5 connected MCPs; found $connected_count or failures reported"
|
|
189
|
-
fi
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
echo "--- Binaries ---"
|
|
193
|
-
check_binary "V1 binary" opencode
|
|
194
|
-
check_binary "V2 binary" opencode2
|
|
195
|
-
|
|
196
|
-
echo "--- Building tarball ---"
|
|
197
|
-
cd "$REPO_DIR"
|
|
198
|
-
npm pack 2>/dev/null || { echo "ERROR: npm pack failed"; exit 1; }
|
|
199
|
-
TGZ=$(ls -t pantheon-opencode-*.tgz | head -1)
|
|
200
|
-
npm rm -g pantheon-opencode 2>/dev/null || true
|
|
201
|
-
npm install -g "$TGZ"
|
|
202
|
-
if ! git -C "$REPO_DIR" ls-files --error-unmatch -- "$TGZ" >/dev/null 2>&1; then rm -f -- "$TGZ"; fi
|
|
203
|
-
|
|
204
|
-
echo "--- Init (headless) ---"
|
|
205
|
-
pantheon-opencode init --headless -y
|
|
206
|
-
|
|
207
|
-
echo "--- MCP Validation ---"
|
|
208
|
-
check_mcp "V1" opencode "$SANDBOX_DIR/project-v1"
|
|
209
|
-
check_mcp "V2" opencode2 "$SANDBOX_DIR/project-v2"
|
|
210
|
-
|
|
211
|
-
echo "--- Doctor ---"
|
|
212
|
-
if command -v pantheon-opencode >/dev/null 2>&1; then
|
|
213
|
-
set +e
|
|
214
|
-
DOCTOR_OUTPUT="$(pantheon-opencode doctor --target "$SANDBOX_DIR" 2>&1)"
|
|
215
|
-
DOCTOR_RC=$?
|
|
216
|
-
set -e
|
|
217
|
-
if [ "$DOCTOR_RC" -eq 0 ]; then
|
|
218
|
-
record_status "doctor" "PASS" "exit 0"
|
|
219
|
-
else
|
|
220
|
-
record_status "doctor" "FAIL" "doctor exited $DOCTOR_RC"
|
|
221
|
-
fi
|
|
222
|
-
else
|
|
223
|
-
record_status "doctor" "FAIL" "pantheon-opencode is not installed in the sandbox prefix"
|
|
224
|
-
fi
|
|
225
|
-
|
|
226
|
-
echo "--- Gate (b): pantheon://agents CONTENT check ---"
|
|
227
|
-
RESOURCES_SRV="$PANTHEON_GLOBAL/scripts/mcp_resources_server.py"
|
|
228
|
-
if [ ! -f "$RESOURCES_SRV" ]; then
|
|
229
|
-
record_status "pantheon://agents resource" "FAIL" "resources server not found at $RESOURCES_SRV"
|
|
230
|
-
else
|
|
231
|
-
set +e
|
|
232
|
-
AGENTS_CONTENT="$("$SANDBOX_VENV/bin/python" - "$RESOURCES_SRV" <<'PYEOF'
|
|
233
|
-
import asyncio, importlib.util, sys
|
|
234
|
-
from pathlib import Path
|
|
235
|
-
resource_path = Path(sys.argv[1]).resolve()
|
|
236
|
-
sys.path.insert(0, str(resource_path.parent))
|
|
237
|
-
spec = importlib.util.spec_from_file_location("mrs", sys.argv[1])
|
|
238
|
-
mod = importlib.util.module_from_spec(spec)
|
|
239
|
-
spec.loader.exec_module(mod)
|
|
240
|
-
print(asyncio.run(mod.list_agents()))
|
|
241
|
-
PYEOF
|
|
242
|
-
)"
|
|
243
|
-
AGENTS_RC=$?
|
|
244
|
-
set -e
|
|
245
|
-
printf '%s\n' "$AGENTS_CONTENT" | head -8
|
|
246
|
-
if [ "$AGENTS_RC" -ne 0 ]; then
|
|
247
|
-
record_status "pantheon://agents resource" "FAIL" "resource probe exited $AGENTS_RC"
|
|
248
|
-
elif printf '%s\n' "$AGENTS_CONTENT" | grep -qi 'zeus' \
|
|
249
|
-
&& printf '%s\n' "$AGENTS_CONTENT" | grep -qi 'hermes'; then
|
|
250
|
-
record_status "pantheon://agents resource" "PASS" "pantheon://agents content includes zeus and hermes"
|
|
251
|
-
else
|
|
252
|
-
record_status "pantheon://agents resource" "FAIL" "content missing zeus/hermes (connectivity alone is not enough)"
|
|
253
|
-
fi
|
|
254
|
-
fi
|
|
255
|
-
echo ""
|
|
256
|
-
echo "=== VALIDATION COMPLETE ==="
|
|
257
|
-
if [ "$STATUS_FAIL" -gt 0 ] || [ "$STATUS_PASS" -eq 0 ]; then
|
|
258
|
-
FINAL_VERDICT="FAIL"
|
|
259
|
-
else
|
|
260
|
-
FINAL_VERDICT="PASS"
|
|
261
|
-
fi
|
|
262
|
-
printf 'Final verdict: %s\n' "$FINAL_VERDICT"
|
|
263
|
-
if [ "$FINAL_VERDICT" != "PASS" ]; then
|
|
264
|
-
exit 1
|
|
265
|
-
fi
|
|
266
|
-
echo "Next: open the isolated TUI with: $SANDBOX_DIR/start-pantheon.sh"
|
|
267
|
-
RUNTEST
|
|
268
|
-
chmod +x "$SANDBOX_ROOT/run-test.sh"
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
# ── Reset / Prepare ───────────────────────────────────────────────────────────
|
|
272
|
-
|
|
273
|
-
cmd_reset() {
|
|
274
|
-
case "$SANDBOX_ROOT" in
|
|
275
|
-
"/" | "$HOME" | "$REPO_DIR" | "$REPO_DIR"/*)
|
|
276
|
-
die "refusing to reset unsafe sandbox root: $SANDBOX_ROOT" ;;
|
|
277
|
-
esac
|
|
278
|
-
log "Resetting sandbox: $SANDBOX_ROOT"
|
|
279
|
-
rm -rf "$SANDBOX_ROOT"
|
|
280
|
-
log "Sandbox removed."
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
install_binaries() {
|
|
284
|
-
log "--- Installing pantheon-opencode (from repo tarball) ---"
|
|
285
|
-
cd "$REPO_DIR"
|
|
286
|
-
npm pack 2>/dev/null || die "npm pack failed"
|
|
287
|
-
local tgz
|
|
288
|
-
tgz=$(ls -t pantheon-opencode-*.tgz | head -1)
|
|
289
|
-
npm rm -g pantheon-opencode 2>/dev/null || true
|
|
290
|
-
npm install -g "$REPO_DIR/$tgz" || die "install of $tgz failed"
|
|
291
|
-
if ! git -C "$REPO_DIR" ls-files --error-unmatch -- "$tgz" >/dev/null 2>&1; then rm -f -- "$tgz"; fi
|
|
292
|
-
log "--- Installing OpenCode V1 ($OPENCODE_V1_SPEC) ---"
|
|
293
|
-
npm install -g "$OPENCODE_V1_SPEC" || die "install of $OPENCODE_V1_SPEC failed"
|
|
294
|
-
log "--- Installing OpenCode V2 ($OPENCODE_V2_SPEC) ---"
|
|
295
|
-
npm install -g "$OPENCODE_V2_SPEC" || die "install of $OPENCODE_V2_SPEC failed"
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
prepare_project() {
|
|
299
|
-
local v="$1" dir pan
|
|
300
|
-
dir="$(project_for "$v")"
|
|
301
|
-
mkdir -p "$dir"
|
|
302
|
-
pan="$(sandbox_bin_for "pantheon-opencode")" \
|
|
303
|
-
|| die "pantheon-opencode not installed in sandbox prefix — run $0 --prepare first"
|
|
304
|
-
log "--- Regenerating config for $v in $dir ---"
|
|
305
|
-
(cd "$dir" && "$pan" init --project --version "$v" --model "$PANTHEON_SANDBOX_MODEL" -y) \
|
|
306
|
-
|| die "init --project --version $v failed in $dir"
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
cmd_prepare() {
|
|
310
|
-
mkdir -p "$SANDBOX_HOME" "$NPM_PREFIX"
|
|
311
|
-
write_run_test_sh
|
|
312
|
-
sandbox_env
|
|
313
|
-
install_binaries
|
|
314
|
-
local pan
|
|
315
|
-
pan="$(sandbox_bin_for "pantheon-opencode")" \
|
|
316
|
-
|| die "pantheon-opencode not found in sandbox prefix after install"
|
|
317
|
-
log "--- Headless init (global venv + runtime) ---"
|
|
318
|
-
"$pan" init --headless -y --model "$PANTHEON_SANDBOX_MODEL" \
|
|
319
|
-
|| die "pantheon-opencode init --headless failed"
|
|
320
|
-
prepare_project v1
|
|
321
|
-
prepare_project v2
|
|
322
|
-
write_extract_py
|
|
323
|
-
log "Prepare complete. Sandbox: $SANDBOX_ROOT"
|
|
324
|
-
log "Next: $0 --run v1 --prompts | $0 --run v2 --prompts"
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
# ── JSON text extraction (schema-agnostic) ────────────────────────────────────
|
|
328
|
-
|
|
329
|
-
write_extract_py() {
|
|
330
|
-
cat > "$EXTRACT_PY" <<'PYEOF'
|
|
331
|
-
"""Collect every string value from opencode run --format json output."""
|
|
332
|
-
import json
|
|
333
|
-
import sys
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
def walk(obj, out):
|
|
337
|
-
if isinstance(obj, str):
|
|
338
|
-
out.append(obj)
|
|
339
|
-
elif isinstance(obj, dict):
|
|
340
|
-
for value in obj.values():
|
|
341
|
-
walk(value, out)
|
|
342
|
-
elif isinstance(obj, list):
|
|
343
|
-
for value in obj:
|
|
344
|
-
walk(value, out)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
raw = sys.stdin.read()
|
|
348
|
-
chunks = []
|
|
349
|
-
try:
|
|
350
|
-
walk(json.loads(raw), chunks)
|
|
351
|
-
except Exception:
|
|
352
|
-
for line in raw.splitlines():
|
|
353
|
-
line = line.strip()
|
|
354
|
-
if not line:
|
|
355
|
-
continue
|
|
356
|
-
try:
|
|
357
|
-
walk(json.loads(line), chunks)
|
|
358
|
-
except Exception:
|
|
359
|
-
chunks.append(line)
|
|
360
|
-
print("\n".join(chunks))
|
|
361
|
-
PYEOF
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
# ── Prompt battery ────────────────────────────────────────────────────────────
|
|
365
|
-
|
|
366
|
-
PROMPT_IDS=()
|
|
367
|
-
PROMPT_TEXTS=()
|
|
368
|
-
PROMPT_MARKERS=()
|
|
369
|
-
PROMPT_VERIFY=()
|
|
370
|
-
|
|
371
|
-
add_prompt() { # id marker ignored verify text
|
|
372
|
-
PROMPT_IDS+=("$1")
|
|
373
|
-
PROMPT_MARKERS+=("$2")
|
|
374
|
-
PROMPT_VERIFY+=("$4")
|
|
375
|
-
PROMPT_TEXTS+=("$5")
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
build_battery() {
|
|
379
|
-
local v="$1"
|
|
380
|
-
PROMPT_IDS=()
|
|
381
|
-
PROMPT_TEXTS=()
|
|
382
|
-
PROMPT_MARKERS=()
|
|
383
|
-
PROMPT_VERIFY=()
|
|
384
|
-
add_prompt "agents-resource" "AGENTS-OK" "" "" \
|
|
385
|
-
"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."
|
|
386
|
-
add_prompt "memory-store" "MEMORY-STORE-OK" "" "" \
|
|
387
|
-
"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"
|
|
388
|
-
add_prompt "memory-recall" "MEMORY-RECALL-OK" "" "" \
|
|
389
|
-
"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"
|
|
390
|
-
add_prompt "tmp-write-read" "TMP-OK" "" \
|
|
391
|
-
"${TMPDIR:-/tmp}/pantheon-sandbox-probe-$v.txt::TMP-OK" \
|
|
392
|
-
"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"
|
|
393
|
-
add_prompt "delegation" "DELEGATION-OK" "" "" \
|
|
394
|
-
"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"
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
declare -A RESULTS
|
|
398
|
-
declare -A DETAILS
|
|
399
|
-
|
|
400
|
-
record() { # v id result detail
|
|
401
|
-
RESULTS["$1:$2"]="$3"
|
|
402
|
-
DETAILS["$1:$2"]="$4"
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
extract_text() { "$HOME/.config/opencode/.venv/bin/python" "$EXTRACT_PY" < "$1"; }
|
|
406
|
-
|
|
407
|
-
ATTEMPT_RESULT=""
|
|
408
|
-
ATTEMPT_DETAIL=""
|
|
409
|
-
|
|
410
|
-
run_prompt_attempt() { # v idx current_bin — sets ATTEMPT_RESULT / ATTEMPT_DETAIL
|
|
411
|
-
local v="$1" idx="$2" bin="$3" id marker verify text
|
|
412
|
-
id="${PROMPT_IDS[$idx]}"
|
|
413
|
-
marker="${PROMPT_MARKERS[$idx]}"
|
|
414
|
-
verify="${PROMPT_VERIFY[$idx]}"
|
|
415
|
-
text="${PROMPT_TEXTS[$idx]}"
|
|
416
|
-
|
|
417
|
-
# Remove the probe file first so post-run verification reflects THIS run.
|
|
418
|
-
if [ -n "$verify" ]; then
|
|
419
|
-
rm -f "${verify%%::*}"
|
|
420
|
-
fi
|
|
421
|
-
|
|
422
|
-
local out_file err_file rc=0
|
|
423
|
-
out_file="$(mktemp)"
|
|
424
|
-
err_file="$(mktemp)"
|
|
425
|
-
|
|
426
|
-
set +e
|
|
427
|
-
(cd "$(project_for "$v")" \
|
|
428
|
-
&& timeout "$PANTHEON_PROMPT_TIMEOUT" "$bin" run --auto --format json "$text") \
|
|
429
|
-
> "$out_file" 2> "$err_file"
|
|
430
|
-
rc=$?
|
|
431
|
-
set -e
|
|
432
|
-
|
|
433
|
-
local extracted
|
|
434
|
-
extracted="$(extract_text "$out_file" || true)"
|
|
435
|
-
local raw
|
|
436
|
-
raw="$( { tr '\n' ' ' < "$out_file"; tr '\n' ' ' < "$err_file"; } | cut -c1-400)"
|
|
437
|
-
rm -f "$out_file" "$err_file"
|
|
438
|
-
|
|
439
|
-
if [ "$rc" -eq 124 ]; then
|
|
440
|
-
ATTEMPT_RESULT="FAIL"
|
|
441
|
-
ATTEMPT_DETAIL="timeout after ${PANTHEON_PROMPT_TIMEOUT}s"
|
|
442
|
-
elif [ "$rc" -ne 0 ]; then
|
|
443
|
-
ATTEMPT_RESULT="FAIL"
|
|
444
|
-
ATTEMPT_DETAIL="marker $marker absent; exit $rc; snippet: $(printf '%s' "$raw" | cut -c1-160)"
|
|
445
|
-
elif printf '%s' "$extracted" | grep -q "$marker"; then
|
|
446
|
-
ATTEMPT_RESULT="PASS"
|
|
447
|
-
ATTEMPT_DETAIL="marker $marker found"
|
|
448
|
-
elif [ -n "$verify" ] \
|
|
449
|
-
&& [ "$(cat "${verify%%::*}" 2>/dev/null)" = "${verify##*::}" ]; then
|
|
450
|
-
ATTEMPT_RESULT="PASS"
|
|
451
|
-
ATTEMPT_DETAIL="probe file verified: ${verify%%::*} contains ${verify##*::}"
|
|
452
|
-
else
|
|
453
|
-
ATTEMPT_RESULT="FAIL"
|
|
454
|
-
ATTEMPT_DETAIL="marker $marker absent; rc=$rc; snippet: $(printf '%s' "$raw" | cut -c1-160)"
|
|
455
|
-
fi
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
run_prompt() { # v idx current_bin — exactly one attempt
|
|
459
|
-
local v="$1" idx="$2" bin="$3" id
|
|
460
|
-
id="${PROMPT_IDS[$idx]}"
|
|
461
|
-
run_prompt_attempt "$v" "$idx" "$bin"
|
|
462
|
-
record "$v" "$id" "$ATTEMPT_RESULT" "$ATTEMPT_DETAIL"
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
check_mcp_list() { # v
|
|
466
|
-
local v="$1" bin out connected_count rc=0
|
|
467
|
-
if ! bin="$(sandbox_bin_v "$v")"; then
|
|
468
|
-
record "$v" "mcp-list" "FAIL" "binary not installed in sandbox prefix"
|
|
469
|
-
return 0
|
|
470
|
-
fi
|
|
471
|
-
set +e
|
|
472
|
-
out="$(cd "$(project_for "$v")" && "$bin" mcp list 2>&1)"
|
|
473
|
-
rc=$?
|
|
474
|
-
set -e
|
|
475
|
-
out="$(printf '%s' "$out" | sed -E $'s/\x1B\\[[0-?]*[ -/]*[@-~]//g')"
|
|
476
|
-
if [ "$rc" -ne 0 ]; then
|
|
477
|
-
record "$v" "mcp-list" "FAIL" "opencode mcp list exited $rc"
|
|
478
|
-
return 0
|
|
479
|
-
fi
|
|
480
|
-
connected_count="$(printf '%s' "$out" | grep -Eic '(^|[^[:alnum:]_])connected([^[:alnum:]_]|$)')" || connected_count=0
|
|
481
|
-
if [ "$connected_count" -eq 5 ] \
|
|
482
|
-
&& ! printf '%s' "$out" | grep -Eiq "failed|✘"; then
|
|
483
|
-
record "$v" "mcp-list" "PASS" "$connected_count connected (expected exactly 5)"
|
|
484
|
-
else
|
|
485
|
-
record "$v" "mcp-list" "FAIL" "expected exactly 5 connected MCPs; found $connected_count or failures reported"
|
|
486
|
-
fi
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
check_doctor() { # v
|
|
490
|
-
local v="$1" pan rc=0
|
|
491
|
-
if ! pan="$(sandbox_bin_for "pantheon-opencode")"; then
|
|
492
|
-
record "$v" "doctor" "FAIL" "pantheon-opencode not installed in sandbox prefix"
|
|
493
|
-
return 0
|
|
494
|
-
fi
|
|
495
|
-
set +e
|
|
496
|
-
(cd "$(project_for "$v")" && "$pan" doctor --target "$(project_for "$v")") > /dev/null 2>&1
|
|
497
|
-
rc=$?
|
|
498
|
-
set -e
|
|
499
|
-
if [ "$rc" -eq 0 ]; then
|
|
500
|
-
record "$v" "doctor" "PASS" "exit 0"
|
|
501
|
-
else
|
|
502
|
-
record "$v" "doctor" "FAIL" "doctor exited $rc (blocking errors)"
|
|
503
|
-
fi
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
installed_package_root() {
|
|
507
|
-
local pan resolved
|
|
508
|
-
if ! pan="$(sandbox_bin_for "pantheon-opencode")"; then
|
|
509
|
-
return 1
|
|
510
|
-
fi
|
|
511
|
-
resolved="$(readlink -f "$pan" 2>/dev/null || true)"
|
|
512
|
-
[ -n "$resolved" ] || return 1
|
|
513
|
-
dirname "$(dirname "$resolved")"
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
run_cost_probe() { # v
|
|
517
|
-
local v="$1" package_root probe raw status detail
|
|
518
|
-
if ! package_root="$(installed_package_root)"; then
|
|
519
|
-
record "$v" "pantheon-cost" "FAIL" "sandbox package is not installed"
|
|
520
|
-
return 0
|
|
521
|
-
fi
|
|
522
|
-
probe="$package_root/scripts/probe-pantheon-cost.mjs"
|
|
523
|
-
if [ ! -f "$probe" ]; then
|
|
524
|
-
record "$v" "pantheon-cost" "FAIL" "installed package has no offline cost probe"
|
|
525
|
-
return 0
|
|
526
|
-
fi
|
|
527
|
-
if ! command -v node >/dev/null 2>&1; then
|
|
528
|
-
record "$v" "pantheon-cost" "FAIL" "node runtime is not available"
|
|
529
|
-
return 0
|
|
530
|
-
fi
|
|
531
|
-
set +e
|
|
532
|
-
raw="$(node "$probe" --version "$v" --fixture --json 2>&1)"
|
|
533
|
-
local rc=$?
|
|
534
|
-
set -e
|
|
535
|
-
case "$raw" in
|
|
536
|
-
*'"status":"PASS"'*) status="PASS" ;;
|
|
537
|
-
*'"status":"NOT_TESTED"'*) status="FAIL" ;;
|
|
538
|
-
*'"status":"AMBIENTAL"'*) status="FAIL" ;;
|
|
539
|
-
*) status="FAIL" ;;
|
|
540
|
-
esac
|
|
541
|
-
detail="$(printf '%s' "$raw" | tr '\n' ' ' | cut -c1-400)"
|
|
542
|
-
if [ "$rc" -ne 0 ] && [ "$status" = "PASS" ]; then
|
|
543
|
-
status="FAIL"
|
|
544
|
-
detail="$detail (probe exit $rc)"
|
|
545
|
-
elif [ "$rc" -ne 0 ] && [ "$status" != "FAIL" ]; then
|
|
546
|
-
detail="$detail (probe exit $rc)"
|
|
547
|
-
fi
|
|
548
|
-
record "$v" "pantheon-cost" "$status" "$detail"
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
run_rehydrate_probe() { # v
|
|
552
|
-
local v="$1" package_root probe raw status detail
|
|
553
|
-
if ! package_root="$(installed_package_root)"; then
|
|
554
|
-
record "$v" "context-rehydrate" "FAIL" "sandbox package is not installed"
|
|
555
|
-
return 0
|
|
556
|
-
fi
|
|
557
|
-
probe="$package_root/scripts/probe-context-rehydrate.mjs"
|
|
558
|
-
if [ ! -f "$probe" ]; then
|
|
559
|
-
record "$v" "context-rehydrate" "FAIL" "installed package has no offline context probe"
|
|
560
|
-
return 0
|
|
561
|
-
fi
|
|
562
|
-
if ! command -v node >/dev/null 2>&1; then
|
|
563
|
-
record "$v" "context-rehydrate" "FAIL" "node runtime is not available"
|
|
564
|
-
return 0
|
|
565
|
-
fi
|
|
566
|
-
set +e
|
|
567
|
-
raw="$(timeout 30 node "$probe" --version "$v" --json 2>&1)"
|
|
568
|
-
local rc=$?
|
|
569
|
-
set -e
|
|
570
|
-
case "$raw" in
|
|
571
|
-
*'"status":"PASS"'*) status="PASS" ;;
|
|
572
|
-
*'"status":"NOT_TESTED"'*) status="FAIL" ;;
|
|
573
|
-
*'"status":"AMBIENTAL"'*) status="FAIL" ;;
|
|
574
|
-
*) status="FAIL" ;;
|
|
575
|
-
esac
|
|
576
|
-
detail="$(printf '%s' "$raw" | tr '\n' ' ' | cut -c1-400)"
|
|
577
|
-
if [ "$rc" -eq 124 ]; then
|
|
578
|
-
status="FAIL"
|
|
579
|
-
detail="context probe timed out after 30s"
|
|
580
|
-
elif [ "$rc" -ne 0 ] && [ "$status" = "PASS" ]; then
|
|
581
|
-
# A non-zero probe exit must never be reported as PASS.
|
|
582
|
-
status="FAIL"
|
|
583
|
-
detail="$detail (probe exit $rc)"
|
|
584
|
-
elif [ "$rc" -ne 0 ] && [ "$status" != "FAIL" ]; then
|
|
585
|
-
detail="$detail (probe exit $rc)"
|
|
586
|
-
fi
|
|
587
|
-
record "$v" "context-rehydrate" "$status" "$detail"
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
run_version_prompts() { # v
|
|
591
|
-
local v="$1" bin
|
|
592
|
-
if ! bin="$(sandbox_bin_v "$v")"; then
|
|
593
|
-
log "Binary '$(bin_for "$v")' not in sandbox prefix — failing all $v checks."
|
|
594
|
-
build_battery "$v"
|
|
595
|
-
local i
|
|
596
|
-
for i in "${!PROMPT_IDS[@]}"; do
|
|
597
|
-
record "$v" "${PROMPT_IDS[$i]}" "FAIL" "binary $(bin_for "$v") not installed in sandbox"
|
|
598
|
-
done
|
|
599
|
-
record "$v" "mcp-list" "FAIL" "binary $(bin_for "$v") not installed in sandbox"
|
|
600
|
-
record "$v" "doctor" "FAIL" "binary $(bin_for "$v") not installed in sandbox"
|
|
601
|
-
return 0
|
|
602
|
-
fi
|
|
603
|
-
prepare_project "$v"
|
|
604
|
-
build_battery "$v"
|
|
605
|
-
local i
|
|
606
|
-
for i in "${!PROMPT_IDS[@]}"; do
|
|
607
|
-
log "[$v] prompt ${PROMPT_IDS[$i]} ..."
|
|
608
|
-
run_prompt "$v" "$i" "$bin"
|
|
609
|
-
log "[$v] prompt ${PROMPT_IDS[$i]} → ${RESULTS["$v:${PROMPT_IDS[$i]}"]}"
|
|
610
|
-
done
|
|
611
|
-
check_mcp_list "$v"
|
|
612
|
-
check_doctor "$v"
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
probe_verdict() { # pass total → PASS when every required check was explicitly PASS
|
|
616
|
-
local pass="$1" total="$2"
|
|
617
|
-
if [ "$pass" -eq "$total" ] && [ "$pass" -gt 0 ]; then
|
|
618
|
-
printf 'PASS'
|
|
619
|
-
else
|
|
620
|
-
printf 'FAIL'
|
|
621
|
-
fi
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
write_report() { # versions...
|
|
625
|
-
local versions=("$@") total_fail=0 total_pass=0
|
|
626
|
-
{
|
|
627
|
-
printf '# Pantheon Sandbox Prompts Report\n\n'
|
|
628
|
-
printf -- '- **Date:** %s\n' "$(date -u '+%Y-%m-%d %H:%M UTC')"
|
|
629
|
-
printf -- '- **Model:** %s\n' "$PANTHEON_SANDBOX_MODEL"
|
|
630
|
-
printf -- '- **Sandbox:** %s\n' "$SANDBOX_ROOT"
|
|
631
|
-
printf -- '- **V1 binary:** %s (%s) | **V2 binary:** %s (%s)\n\n' \
|
|
632
|
-
"$V1_BIN" "$OPENCODE_V1_SPEC" "$V2_BIN" "$OPENCODE_V2_SPEC"
|
|
633
|
-
local v id res
|
|
634
|
-
for v in "${versions[@]}"; do
|
|
635
|
-
printf '## Version %s (`%s`)\n\n' "$v" "$(bin_for "$v")"
|
|
636
|
-
printf '| Prompt | Result | Detail |\n|---|---|---|\n'
|
|
637
|
-
for id in "${PROMPT_IDS[@]}" mcp-list doctor; do
|
|
638
|
-
res="${RESULTS["$v:$id"]:-SKIP}"
|
|
639
|
-
case "$res" in
|
|
640
|
-
PASS) total_pass=$((total_pass + 1)) ;;
|
|
641
|
-
*) total_fail=$((total_fail + 1)) ;;
|
|
642
|
-
esac
|
|
643
|
-
printf '| %s | %s | %s |\n' "$id" "$res" "${DETAILS["$v:$id"]:-—}"
|
|
644
|
-
done
|
|
645
|
-
printf '\n'
|
|
646
|
-
done
|
|
647
|
-
printf '## Summary\n\n'
|
|
648
|
-
printf -- '- PASS: %d\n- Not PASS (blocking): %d\n\n' \
|
|
649
|
-
"$total_pass" "$total_fail"
|
|
650
|
-
printf '**Verdict: %s**\n' "$(probe_verdict "$total_pass" "$((total_pass + total_fail))")"
|
|
651
|
-
} > "$REPORT_FILE"
|
|
652
|
-
log "Report written to $REPORT_FILE"
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
write_cost_report() { # versions...
|
|
656
|
-
local versions=("$@") total_fail=0 total_pass=0
|
|
657
|
-
{
|
|
658
|
-
printf '# Pantheon Cost Probe Report\n\n'
|
|
659
|
-
printf -- '- **Date:** %s\n' "$(date -u '+%Y-%m-%d %H:%M UTC')"
|
|
660
|
-
printf -- '- **Sandbox:** %s\n\n' "$SANDBOX_ROOT"
|
|
661
|
-
printf '| Version | Result | Detail |\n|---|---|---|\n'
|
|
662
|
-
local v result
|
|
663
|
-
for v in "${versions[@]}"; do
|
|
664
|
-
result="${RESULTS["$v:pantheon-cost"]:-NOT_TESTED}"
|
|
665
|
-
case "$result" in
|
|
666
|
-
PASS) total_pass=$((total_pass + 1)) ;;
|
|
667
|
-
*) total_fail=$((total_fail + 1)) ;;
|
|
668
|
-
esac
|
|
669
|
-
printf '| %s | %s | %s |\n' "$v" "$result" "${DETAILS["$v:pantheon-cost"]:-—}"
|
|
670
|
-
done
|
|
671
|
-
printf '\n## Summary\n\n'
|
|
672
|
-
printf -- '- PASS: %d\n- Not PASS (blocking): %d\n\n' \
|
|
673
|
-
"$total_pass" "$total_fail"
|
|
674
|
-
printf '**Verdict: %s**\n' "$(probe_verdict "$total_pass" "$((total_pass + total_fail))")"
|
|
675
|
-
} > "$COST_REPORT_FILE"
|
|
676
|
-
log "Cost probe report written to $COST_REPORT_FILE"
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
write_rehydrate_report() { # versions...
|
|
680
|
-
local versions=("$@") total_fail=0 total_pass=0
|
|
681
|
-
{
|
|
682
|
-
printf '# Pantheon Context Rehydration Probe Report\n\n'
|
|
683
|
-
printf -- '- **Date:** %s\n' "$(date -u '+%Y-%m-%d %H:%M UTC')"
|
|
684
|
-
printf -- '- **Sandbox:** %s\n\n' "$SANDBOX_ROOT"
|
|
685
|
-
printf '| Version | Probe | Result | Detail |\n|---|---|---|---|\n'
|
|
686
|
-
local v result
|
|
687
|
-
for v in "${versions[@]}"; do
|
|
688
|
-
result="${RESULTS["$v:context-rehydrate"]:-NOT_TESTED}"
|
|
689
|
-
case "$result" in
|
|
690
|
-
PASS) total_pass=$((total_pass + 1)) ;;
|
|
691
|
-
*) total_fail=$((total_fail + 1)) ;;
|
|
692
|
-
esac
|
|
693
|
-
printf '| %s | context_rehydrate + context_session_summary | %s | %s |\n' \
|
|
694
|
-
"$v" "$result" "${DETAILS["$v:context-rehydrate"]:-—}"
|
|
695
|
-
done
|
|
696
|
-
printf '\n## Summary\n\n'
|
|
697
|
-
printf -- '- PASS: %d\n- Not PASS (blocking): %d\n\n' \
|
|
698
|
-
"$total_pass" "$total_fail"
|
|
699
|
-
printf '**Verdict: %s**\n' "$(probe_verdict "$total_pass" "$((total_pass + total_fail))")"
|
|
700
|
-
} > "$REHYDRATE_REPORT_FILE"
|
|
701
|
-
log "Context probe report written to $REHYDRATE_REPORT_FILE"
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
run_cost() { # versions...
|
|
705
|
-
local versions=("$@") v fail=0
|
|
706
|
-
mkdir -p "$SANDBOX_ROOT"
|
|
707
|
-
for v in "${versions[@]}"; do
|
|
708
|
-
log "[$v] pantheon_cost offline probe ..."
|
|
709
|
-
run_cost_probe "$v"
|
|
710
|
-
log "[$v] pantheon-cost → ${RESULTS["$v:pantheon-cost"]}"
|
|
711
|
-
done
|
|
712
|
-
write_cost_report "${versions[@]}"
|
|
713
|
-
for v in "${versions[@]}"; do
|
|
714
|
-
[ "${RESULTS["$v:pantheon-cost"]:-}" = "PASS" ] || fail=$((fail + 1))
|
|
715
|
-
done
|
|
716
|
-
if [ "$fail" -gt 0 ]; then
|
|
717
|
-
log "Cost probe: $fail check(s) without explicit PASS."
|
|
718
|
-
exit 1
|
|
719
|
-
fi
|
|
720
|
-
log "Cost probe: PASS."
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
run_rehydrate() { # versions...
|
|
724
|
-
local versions=("$@") v fail=0
|
|
725
|
-
mkdir -p "$SANDBOX_ROOT"
|
|
726
|
-
for v in "${versions[@]}"; do
|
|
727
|
-
log "[$v] context_rehydrate/session_summary offline probe ..."
|
|
728
|
-
run_rehydrate_probe "$v"
|
|
729
|
-
log "[$v] context probe → ${RESULTS["$v:context-rehydrate"]}"
|
|
730
|
-
done
|
|
731
|
-
write_rehydrate_report "${versions[@]}"
|
|
732
|
-
for v in "${versions[@]}"; do
|
|
733
|
-
[ "${RESULTS["$v:context-rehydrate"]:-}" = "PASS" ] || fail=$((fail + 1))
|
|
734
|
-
done
|
|
735
|
-
if [ "$fail" -gt 0 ]; then
|
|
736
|
-
log "Context probe: $fail check(s) without explicit PASS."
|
|
737
|
-
exit 1
|
|
738
|
-
fi
|
|
739
|
-
log "Context probe: PASS."
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
run_battery() { # versions...
|
|
743
|
-
local versions=("$@")
|
|
744
|
-
mkdir -p "$SANDBOX_ROOT"
|
|
745
|
-
write_extract_py
|
|
746
|
-
local v
|
|
747
|
-
for v in "${versions[@]}"; do
|
|
748
|
-
run_version_prompts "$v"
|
|
749
|
-
done
|
|
750
|
-
write_report "${versions[@]}"
|
|
751
|
-
local v fail=0 id
|
|
752
|
-
for v in "${versions[@]}"; do
|
|
753
|
-
for id in "${PROMPT_IDS[@]}" mcp-list doctor; do
|
|
754
|
-
[ "${RESULTS["$v:$id"]:-}" = "PASS" ] || fail=$((fail + 1))
|
|
755
|
-
done
|
|
756
|
-
done
|
|
757
|
-
if [ "$fail" -gt 0 ]; then
|
|
758
|
-
log "Prompts battery: $fail check(s) without explicit PASS."
|
|
759
|
-
exit 1
|
|
760
|
-
fi
|
|
761
|
-
log "Prompts battery: PASS."
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
# ── Base validation (no prompts) ──────────────────────────────────────────────
|
|
765
|
-
|
|
766
|
-
run_base() { # v
|
|
767
|
-
local v="$1" bin
|
|
768
|
-
if ! bin="$(sandbox_bin_v "$v")"; then
|
|
769
|
-
printf 'ERROR: sandbox not prepared for %s (binary '\''%s'\'' missing from sandbox prefix) — run %s --prepare first\n' \
|
|
770
|
-
"$v" "$(bin_for "$v")" "$0" >&2
|
|
771
|
-
exit 3
|
|
772
|
-
fi
|
|
773
|
-
prepare_project "$v"
|
|
774
|
-
build_battery "$v"
|
|
775
|
-
log "[$v] opencode mcp list"
|
|
776
|
-
check_mcp_list "$v"
|
|
777
|
-
log "[$v] mcp-list → ${RESULTS["$v:mcp-list"]}"
|
|
778
|
-
log "[$v] doctor"
|
|
779
|
-
check_doctor "$v"
|
|
780
|
-
log "[$v] doctor → ${RESULTS["$v:doctor"]}"
|
|
781
|
-
local fail=0
|
|
782
|
-
[ "${RESULTS["$v:mcp-list"]}" = "PASS" ] || fail=$((fail + 1))
|
|
783
|
-
[ "${RESULTS["$v:doctor"]}" = "PASS" ] || fail=$((fail + 1))
|
|
784
|
-
if [ "$fail" -gt 0 ]; then
|
|
785
|
-
log "Base validation $v: $fail check(s) without explicit PASS."
|
|
786
|
-
exit 1
|
|
787
|
-
fi
|
|
788
|
-
log "Base validation $v: PASS."
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
# ── CLI ───────────────────────────────────────────────────────────────────────
|
|
792
|
-
|
|
793
|
-
while [ $# -gt 0 ]; do
|
|
794
|
-
case "$1" in
|
|
795
|
-
--prepare) MODE_PREPARE=1 ;;
|
|
796
|
-
--reset) MODE_RESET=1 ;;
|
|
797
|
-
--prompts) MODE_PROMPTS=1 ;;
|
|
798
|
-
--cost) MODE_COST=1 ;;
|
|
799
|
-
--rehydrate) MODE_REHYDRATE=1 ;;
|
|
800
|
-
--run)
|
|
801
|
-
shift
|
|
802
|
-
case "${1:-}" in
|
|
803
|
-
v1 | v2) RUN_VERSION="$1" ;;
|
|
804
|
-
*) usage ;;
|
|
805
|
-
esac
|
|
806
|
-
;;
|
|
807
|
-
--help | -h) usage ;;
|
|
808
|
-
*) usage ;;
|
|
809
|
-
esac
|
|
810
|
-
shift
|
|
811
|
-
done
|
|
812
|
-
|
|
813
|
-
if [ "$MODE_RESET" -eq 1 ]; then
|
|
814
|
-
cmd_reset
|
|
815
|
-
exit 0
|
|
816
|
-
fi
|
|
817
|
-
|
|
818
|
-
if [ "$MODE_PREPARE" -eq 0 ] && [ -z "$RUN_VERSION" ] \
|
|
819
|
-
&& [ "$MODE_PROMPTS" -eq 0 ] && [ "$MODE_COST" -eq 0 ] \
|
|
820
|
-
&& [ "$MODE_REHYDRATE" -eq 0 ]; then
|
|
821
|
-
usage
|
|
822
|
-
fi
|
|
823
|
-
|
|
824
|
-
if [ "$MODE_PREPARE" -eq 1 ]; then
|
|
825
|
-
cmd_prepare
|
|
826
|
-
fi
|
|
827
|
-
|
|
828
|
-
if [ -n "$RUN_VERSION" ]; then
|
|
829
|
-
if [ "$MODE_PROMPTS" -eq 1 ]; then
|
|
830
|
-
sandbox_env
|
|
831
|
-
run_battery "$RUN_VERSION"
|
|
832
|
-
else
|
|
833
|
-
sandbox_env
|
|
834
|
-
run_base "$RUN_VERSION"
|
|
835
|
-
fi
|
|
836
|
-
if [ "$MODE_COST" -eq 1 ]; then
|
|
837
|
-
sandbox_env
|
|
838
|
-
run_cost "$RUN_VERSION"
|
|
839
|
-
fi
|
|
840
|
-
if [ "$MODE_REHYDRATE" -eq 1 ]; then
|
|
841
|
-
sandbox_env
|
|
842
|
-
run_rehydrate "$RUN_VERSION"
|
|
843
|
-
fi
|
|
844
|
-
elif [ "$MODE_PROMPTS" -eq 1 ]; then
|
|
845
|
-
sandbox_env
|
|
846
|
-
run_battery v1 v2
|
|
847
|
-
if [ "$MODE_COST" -eq 1 ]; then
|
|
848
|
-
run_cost v1 v2
|
|
849
|
-
fi
|
|
850
|
-
if [ "$MODE_REHYDRATE" -eq 1 ]; then
|
|
851
|
-
run_rehydrate v1 v2
|
|
852
|
-
fi
|
|
853
|
-
elif [ "$MODE_COST" -eq 1 ]; then
|
|
854
|
-
sandbox_env
|
|
855
|
-
run_cost v1 v2
|
|
856
|
-
if [ "$MODE_REHYDRATE" -eq 1 ]; then
|
|
857
|
-
run_rehydrate v1 v2
|
|
858
|
-
fi
|
|
859
|
-
elif [ "$MODE_REHYDRATE" -eq 1 ]; then
|
|
860
|
-
sandbox_env
|
|
861
|
-
run_rehydrate v1 v2
|
|
862
|
-
fi
|