pantheon-opencode 1.5.0-beta.1 → 1.5.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +3 -0
  2. package/package.json +1 -1
  3. package/plugin.json +1 -1
  4. package/scripts/cost.mjs +30 -14
  5. package/scripts/mcp_persistence_server.py +44 -7
  6. package/scripts/probe-context-rehydrate.mjs +240 -0
  7. package/scripts/probe-pantheon-cost.mjs +304 -0
  8. package/scripts/test-opencode-v1-v2-sandbox.sh +394 -41
  9. package/src/mcp/mcp_persistence_server.py +44 -7
  10. package/src/pantheon/cost-command.ts +49 -32
  11. package/src/pantheon/delegate-manager.ts +285 -0
  12. package/src/pantheon/token-opt.ts +237 -11
  13. package/src/plugin-v2.ts +2 -14
  14. package/src/plugin.ts +3 -8
  15. package/src/plugins/tui/package.json +1 -1
  16. package/.pantheon/code-mode/.gitkeep +0 -0
  17. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-1.json +0 -8
  18. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-10.json +0 -8
  19. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-11.json +0 -8
  20. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-2.json +0 -8
  21. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-3.json +0 -8
  22. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-4.json +0 -8
  23. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-5.json +0 -8
  24. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-6.json +0 -8
  25. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-7.json +0 -8
  26. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-8.json +0 -8
  27. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-9.json +0 -8
  28. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/heartbeat.json +0 -8
  29. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/session.json +0 -11
  30. package/src/pantheon/delegate-task-adapter.ts +0 -444
@@ -13,6 +13,11 @@
13
13
  # (config is regenerated for that version first).
14
14
  # --prompts Prompt battery via `opencode run --format json` for every
15
15
  # requested version (with --run vX) or both versions alone.
16
+ # --cost Offline/ambiental 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/ambiental context_rehydrate + context_session_summary
20
+ # probe for the requested version (with --run vX) or both.
16
21
  # --reset Wipe the sandbox root.
17
22
  # --help Usage.
18
23
  #
@@ -49,11 +54,15 @@ V1_BIN="${OPENCODE_V1_BIN:-opencode}"
49
54
  V2_BIN="${OPENCODE_V2_BIN:-opencode2}"
50
55
 
51
56
  REPORT_FILE="$SANDBOX_ROOT/prompts-report.md"
57
+ COST_REPORT_FILE="$SANDBOX_ROOT/pantheon-cost-report.md"
58
+ REHYDRATE_REPORT_FILE="$SANDBOX_ROOT/context-rehydrate-report.md"
52
59
  EXTRACT_PY="$SANDBOX_ROOT/.prompt-extract-json.py"
53
60
 
54
61
  MODE_PREPARE=0
55
62
  MODE_RESET=0
56
63
  MODE_PROMPTS=0
64
+ MODE_COST=0
65
+ MODE_REHYDRATE=0
57
66
  RUN_VERSION=""
58
67
 
59
68
  usage() {
@@ -105,8 +114,8 @@ write_run_test_sh() {
105
114
  cat > "$SANDBOX_ROOT/run-test.sh" <<'RUNTEST'
106
115
  #!/usr/bin/env bash
107
116
  # 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).
117
+ # Validates the global install: binaries, exactly 5 connected MCPs per version,
118
+ # doctor 0 errors, AND the CONTENT of pantheon://agents (zeus/hermes present).
110
119
  set -euo pipefail
111
120
 
112
121
  SANDBOX_DIR="$(cd "$(dirname "$0")" && pwd)"
@@ -124,9 +133,81 @@ export HOME="$SANDBOX_HOME"
124
133
  export PATH="$NPM_PREFIX/bin:$SANDBOX_VENV/bin:$PATH"
125
134
  export npm_config_prefix="$NPM_PREFIX"
126
135
 
136
+ STATUS_FAIL=0
137
+ STATUS_AMBIENTAL=0
138
+ STATUS_NOT_TESTED=0
139
+ STATUS_PASS=0
140
+ MCP_AMBIENTAL_RE='needs authentication|unauthorized|authentication failure|api key|credentials|401|docker|econnrefused|enotfound|etimedout|econnreset|connection refused|rate.?limit|429|quota|overloaded|edgesout|npm err'
141
+
142
+ record_status() { # label status detail
143
+ local label="$1" status="$2" detail="$3"
144
+ case "$status" in
145
+ PASS) STATUS_PASS=$((STATUS_PASS + 1)) ;;
146
+ FAIL) STATUS_FAIL=$((STATUS_FAIL + 1)) ;;
147
+ AMBIENTAL) STATUS_AMBIENTAL=$((STATUS_AMBIENTAL + 1)) ;;
148
+ NOT_TESTED) STATUS_NOT_TESTED=$((STATUS_NOT_TESTED + 1)) ;;
149
+ esac
150
+ printf '%s: %s — %s\n' "$status" "$label" "$detail"
151
+ }
152
+
153
+ check_binary() { # label binary
154
+ local label="$1" bin="$2" out rc=0
155
+ if ! command -v "$bin" >/dev/null 2>&1; then
156
+ record_status "$label" "NOT_TESTED" "binary '$bin' is not installed in the sandbox prefix"
157
+ return 0
158
+ fi
159
+ set +e
160
+ out="$("$bin" --version 2>&1)"
161
+ rc=$?
162
+ set -e
163
+ if [ "$rc" -eq 0 ]; then
164
+ record_status "$label" "PASS" "${out:-version command exited 0}"
165
+ elif printf '%s' "$out" | grep -Eiq "$MCP_AMBIENTAL_RE"; then
166
+ record_status "$label" "AMBIENTAL" "version check unavailable (exit $rc)"
167
+ else
168
+ record_status "$label" "FAIL" "version check exited $rc"
169
+ fi
170
+ }
171
+
172
+ check_mcp() { # label binary project
173
+ local label="$1" bin="$2" project="$3" out connected_count rc=0
174
+ if ! command -v "$bin" >/dev/null 2>&1; then
175
+ record_status "$label MCP" "NOT_TESTED" "binary '$bin' is not installed in the sandbox prefix"
176
+ return 0
177
+ fi
178
+ if [ ! -d "$project" ]; then
179
+ record_status "$label MCP" "NOT_TESTED" "project directory is missing: $project"
180
+ return 0
181
+ fi
182
+ set +e
183
+ out="$(cd "$project" && "$bin" mcp list 2>&1)"
184
+ rc=$?
185
+ set -e
186
+ out="$(printf '%s' "$out" | sed -E $'s/\x1B\\[[0-?]*[ -/]*[@-~]//g')"
187
+ printf '%s\n' "$out"
188
+ if [ "$rc" -ne 0 ]; then
189
+ if printf '%s' "$out" | grep -Eiq "$MCP_AMBIENTAL_RE"; then
190
+ record_status "$label MCP" "AMBIENTAL" "mcp list environmental failure (exit $rc)"
191
+ else
192
+ record_status "$label MCP" "FAIL" "mcp list exited $rc"
193
+ fi
194
+ return 0
195
+ fi
196
+ connected_count="$(printf '%s' "$out" | grep -Eic '(^|[^[:alnum:]_])connected([^[:alnum:]_]|$)')" || connected_count=0
197
+ if [ "$connected_count" -eq 5 ] \
198
+ && ! printf '%s' "$out" | grep -Eiq 'failed|✘' \
199
+ && ! printf '%s' "$out" | grep -Eiq "$MCP_AMBIENTAL_RE"; then
200
+ record_status "$label MCP" "PASS" "exactly 5 connected MCPs"
201
+ elif printf '%s' "$out" | grep -Eiq "$MCP_AMBIENTAL_RE"; then
202
+ record_status "$label MCP" "AMBIENTAL" "environmental MCP status; found $connected_count connected"
203
+ else
204
+ record_status "$label MCP" "FAIL" "expected exactly 5 connected MCPs; found $connected_count or failures reported"
205
+ fi
206
+ }
207
+
127
208
  echo "--- Binaries ---"
128
- opencode --version
129
- opencode2 --version
209
+ check_binary "V1 binary" opencode
210
+ check_binary "V2 binary" opencode2
130
211
 
131
212
  echo "--- Building tarball ---"
132
213
  cd "$REPO_DIR"
@@ -134,42 +215,80 @@ npm pack 2>/dev/null || { echo "ERROR: npm pack failed"; exit 1; }
134
215
  TGZ=$(ls -t pantheon-opencode-*.tgz | head -1)
135
216
  npm rm -g pantheon-opencode 2>/dev/null || true
136
217
  npm install -g "$TGZ"
218
+ if ! git -C "$REPO_DIR" ls-files --error-unmatch -- "$TGZ" >/dev/null 2>&1; then rm -f -- "$TGZ"; fi
137
219
 
138
220
  echo "--- Init (headless) ---"
139
221
  pantheon-opencode init --headless -y
140
222
 
141
- echo "--- MCP List (V1) ---"
142
- opencode mcp list
143
-
144
- echo "--- MCP List (V2) ---"
145
- opencode2 mcp list
223
+ echo "--- MCP Validation ---"
224
+ check_mcp "V1" opencode "$SANDBOX_DIR/project-v1"
225
+ check_mcp "V2" opencode2 "$SANDBOX_DIR/project-v2"
146
226
 
147
227
  echo "--- Doctor ---"
148
- pantheon-opencode doctor --target "$SANDBOX_DIR"
228
+ if command -v pantheon-opencode >/dev/null 2>&1; then
229
+ set +e
230
+ DOCTOR_OUTPUT="$(pantheon-opencode doctor --target "$SANDBOX_DIR" 2>&1)"
231
+ DOCTOR_RC=$?
232
+ set -e
233
+ if [ "$DOCTOR_RC" -eq 0 ]; then
234
+ record_status "doctor" "PASS" "exit 0"
235
+ elif printf '%s' "$DOCTOR_OUTPUT" | grep -Eiq "$MCP_AMBIENTAL_RE"; then
236
+ record_status "doctor" "AMBIENTAL" "environmental doctor failure (exit $DOCTOR_RC)"
237
+ else
238
+ record_status "doctor" "FAIL" "doctor exited $DOCTOR_RC"
239
+ fi
240
+ else
241
+ record_status "doctor" "NOT_TESTED" "pantheon-opencode is not installed in the sandbox prefix"
242
+ fi
149
243
 
150
244
  echo "--- Gate (b): pantheon://agents CONTENT check ---"
151
245
  RESOURCES_SRV="$PANTHEON_GLOBAL/scripts/mcp_resources_server.py"
152
246
  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'
247
+ record_status "pantheon://agents resource" "NOT_TESTED" "resources server not found at $RESOURCES_SRV"
248
+ else
249
+ set +e
250
+ AGENTS_CONTENT="$("$SANDBOX_VENV/bin/python" - "$RESOURCES_SRV" <<'PYEOF'
156
251
  import asyncio, importlib.util, sys
252
+ from pathlib import Path
253
+ resource_path = Path(sys.argv[1]).resolve()
254
+ sys.path.insert(0, str(resource_path.parent))
157
255
  spec = importlib.util.spec_from_file_location("mrs", sys.argv[1])
158
256
  mod = importlib.util.module_from_spec(spec)
159
257
  spec.loader.exec_module(mod)
160
258
  print(asyncio.run(mod.list_agents()))
161
259
  PYEOF
162
260
  )"
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
261
+ AGENTS_RC=$?
262
+ set -e
263
+ printf '%s\n' "$AGENTS_CONTENT" | head -8
264
+ if [ "$AGENTS_RC" -ne 0 ]; then
265
+ if printf '%s' "$AGENTS_CONTENT" | grep -Eiq "$MCP_AMBIENTAL_RE"; then
266
+ record_status "pantheon://agents resource" "AMBIENTAL" "resource probe environmental failure (exit $AGENTS_RC)"
267
+ else
268
+ record_status "pantheon://agents resource" "FAIL" "resource probe exited $AGENTS_RC"
269
+ fi
270
+ elif printf '%s\n' "$AGENTS_CONTENT" | grep -qi 'zeus' \
271
+ && printf '%s\n' "$AGENTS_CONTENT" | grep -qi 'hermes'; then
272
+ record_status "pantheon://agents resource" "PASS" "pantheon://agents content includes zeus and hermes"
273
+ else
274
+ record_status "pantheon://agents resource" "FAIL" "content missing zeus/hermes (connectivity alone is not enough)"
275
+ fi
169
276
  fi
170
-
171
277
  echo ""
172
278
  echo "=== VALIDATION COMPLETE ==="
279
+ if [ "$STATUS_FAIL" -gt 0 ]; then
280
+ FINAL_VERDICT="FAIL"
281
+ elif [ "$STATUS_NOT_TESTED" -gt 0 ]; then
282
+ FINAL_VERDICT="NOT_TESTED"
283
+ elif [ "$STATUS_AMBIENTAL" -gt 0 ]; then
284
+ FINAL_VERDICT="AMBIENTAL"
285
+ else
286
+ FINAL_VERDICT="PASS"
287
+ fi
288
+ printf 'Final verdict: %s\n' "$FINAL_VERDICT"
289
+ if [ "$FINAL_VERDICT" = "FAIL" ]; then
290
+ exit 1
291
+ fi
173
292
  echo "Next: open the isolated TUI with: $SANDBOX_DIR/start-pantheon.sh"
174
293
  RUNTEST
175
294
  chmod +x "$SANDBOX_ROOT/run-test.sh"
@@ -195,6 +314,7 @@ install_binaries() {
195
314
  tgz=$(ls -t pantheon-opencode-*.tgz | head -1)
196
315
  npm rm -g pantheon-opencode 2>/dev/null || true
197
316
  npm install -g "$REPO_DIR/$tgz" || die "install of $tgz failed"
317
+ if ! git -C "$REPO_DIR" ls-files --error-unmatch -- "$tgz" >/dev/null 2>&1; then rm -f -- "$tgz"; fi
198
318
  log "--- Installing OpenCode V1 ($OPENCODE_V1_SPEC) ---"
199
319
  npm install -g "$OPENCODE_V1_SPEC" \
200
320
  || warn "V1 binary install failed ($OPENCODE_V1_SPEC) — v1 prompts will be AMBIENTAL"
@@ -273,6 +393,7 @@ PYEOF
273
393
 
274
394
  # Known environmental failure signatures (never fail the run).
275
395
  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'
396
+ MCP_AMBIENTAL_RE='needs authentication|unauthorized|authentication failure|api key|credentials|401|docker|econnrefused|enotfound|etimedout|econnreset|connection refused|rate.?limit|429|quota|overloaded|edgesout|npm err'
276
397
 
277
398
  PROMPT_IDS=()
278
399
  PROMPT_TEXTS=()
@@ -354,12 +475,20 @@ run_prompt_attempt() { # v idx current_bin — sets ATTEMPT_RESULT / ATTEMPT_DET
354
475
  if [ "$rc" -eq 124 ]; then
355
476
  ATTEMPT_RESULT="AMBIENTAL"
356
477
  ATTEMPT_DETAIL="timeout after ${PANTHEON_PROMPT_TIMEOUT}s (provider latency)"
478
+ elif [ "$rc" -ne 0 ]; then
479
+ if [ -n "$amb_marker" ] && printf '%s' "$extracted" | grep -q "$amb_marker"; then
480
+ ATTEMPT_RESULT="AMBIENTAL"
481
+ ATTEMPT_DETAIL="explicit $amb_marker (exit $rc)"
482
+ elif printf '%s' "$raw" | grep -Eiq "$AMBIENTAL_RE"; then
483
+ ATTEMPT_RESULT="AMBIENTAL"
484
+ ATTEMPT_DETAIL="environmental signature with exit $rc"
485
+ else
486
+ ATTEMPT_RESULT="FAIL"
487
+ ATTEMPT_DETAIL="marker $marker absent; exit $rc; snippet: $(printf '%s' "$raw" | cut -c1-160)"
488
+ fi
357
489
  elif printf '%s' "$extracted" | grep -q "$marker"; then
358
490
  ATTEMPT_RESULT="PASS"
359
491
  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
492
  elif [ -n "$verify" ] \
364
493
  && [ "$(cat "${verify%%::*}" 2>/dev/null)" = "${verify##*::}" ]; then
365
494
  ATTEMPT_RESULT="PASS"
@@ -398,7 +527,7 @@ run_prompt() { # v idx current_bin — ONE retry after cooldown for real FAIL on
398
527
  }
399
528
 
400
529
  check_mcp_list() { # v
401
- local v="$1" bin out rc=0
530
+ local v="$1" bin out connected_count rc=0
402
531
  if ! bin="$(sandbox_bin_v "$v")"; then
403
532
  record "$v" "mcp-list" "AMBIENTAL" "binary not installed in sandbox prefix"
404
533
  return 0
@@ -407,13 +536,24 @@ check_mcp_list() { # v
407
536
  out="$(cd "$(project_for "$v")" && "$bin" mcp list 2>&1)"
408
537
  rc=$?
409
538
  set -e
539
+ out="$(printf '%s' "$out" | sed -E $'s/\x1B\\[[0-?]*[ -/]*[@-~]//g')"
410
540
  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"
541
+ if printf '%s' "$out" | grep -Eiq "$MCP_AMBIENTAL_RE"; then
542
+ record "$v" "mcp-list" "AMBIENTAL" "mcp list environmental failure (exit $rc)"
543
+ else
544
+ record "$v" "mcp-list" "FAIL" "opencode mcp list exited $rc"
545
+ fi
546
+ return 0
547
+ fi
548
+ connected_count="$(printf '%s' "$out" | grep -Eic '(^|[^[:alnum:]_])connected([^[:alnum:]_]|$)')" || connected_count=0
549
+ if [ "$connected_count" -eq 5 ] \
550
+ && ! printf '%s' "$out" | grep -Eiq "failed|✘" \
551
+ && ! printf '%s' "$out" | grep -Eiq "$MCP_AMBIENTAL_RE"; then
552
+ record "$v" "mcp-list" "PASS" "$connected_count connected (expected exactly 5)"
553
+ elif printf '%s' "$out" | grep -Eiq "$MCP_AMBIENTAL_RE"; then
554
+ record "$v" "mcp-list" "AMBIENTAL" "environmental MCP status; found $connected_count connected"
415
555
  else
416
- record "$v" "mcp-list" "FAIL" "no connected MCPs or failures reported"
556
+ record "$v" "mcp-list" "FAIL" "expected exactly 5 connected MCPs; found $connected_count or failures reported"
417
557
  fi
418
558
  }
419
559
 
@@ -434,6 +574,90 @@ check_doctor() { # v
434
574
  fi
435
575
  }
436
576
 
577
+ installed_package_root() {
578
+ local pan resolved
579
+ if ! pan="$(sandbox_bin_for "pantheon-opencode")"; then
580
+ return 1
581
+ fi
582
+ resolved="$(readlink -f "$pan" 2>/dev/null || true)"
583
+ [ -n "$resolved" ] || return 1
584
+ dirname "$(dirname "$resolved")"
585
+ }
586
+
587
+ run_cost_probe() { # v
588
+ local v="$1" package_root probe raw status detail
589
+ if ! package_root="$(installed_package_root)"; then
590
+ record "$v" "pantheon-cost" "NOT_TESTED" "sandbox package is not installed"
591
+ return 0
592
+ fi
593
+ probe="$package_root/scripts/probe-pantheon-cost.mjs"
594
+ if [ ! -f "$probe" ]; then
595
+ record "$v" "pantheon-cost" "NOT_TESTED" "installed package has no offline cost probe"
596
+ return 0
597
+ fi
598
+ if ! command -v node >/dev/null 2>&1; then
599
+ record "$v" "pantheon-cost" "AMBIENTAL" "node runtime is not available"
600
+ return 0
601
+ fi
602
+ set +e
603
+ raw="$(node "$probe" --version "$v" --fixture --json 2>&1)"
604
+ local rc=$?
605
+ set -e
606
+ case "$raw" in
607
+ *'"status":"PASS"'*) status="PASS" ;;
608
+ *'"status":"NOT_TESTED"'*) status="NOT_TESTED" ;;
609
+ *'"status":"AMBIENTAL"'*) status="AMBIENTAL" ;;
610
+ *) status="FAIL" ;;
611
+ esac
612
+ detail="$(printf '%s' "$raw" | tr '\n' ' ' | cut -c1-400)"
613
+ if [ "$rc" -ne 0 ] && [ "$status" = "PASS" ]; then
614
+ status="FAIL"
615
+ detail="$detail (probe exit $rc)"
616
+ elif [ "$rc" -ne 0 ] && [ "$status" != "FAIL" ]; then
617
+ detail="$detail (probe exit $rc)"
618
+ fi
619
+ record "$v" "pantheon-cost" "$status" "$detail"
620
+ }
621
+
622
+ run_rehydrate_probe() { # v
623
+ local v="$1" package_root probe raw status detail
624
+ if ! package_root="$(installed_package_root)"; then
625
+ record "$v" "context-rehydrate" "NOT_TESTED" "sandbox package is not installed"
626
+ return 0
627
+ fi
628
+ probe="$package_root/scripts/probe-context-rehydrate.mjs"
629
+ if [ ! -f "$probe" ]; then
630
+ record "$v" "context-rehydrate" "NOT_TESTED" "installed package has no offline context probe"
631
+ return 0
632
+ fi
633
+ if ! command -v node >/dev/null 2>&1; then
634
+ record "$v" "context-rehydrate" "AMBIENTAL" "node runtime is not available"
635
+ return 0
636
+ fi
637
+ set +e
638
+ raw="$(timeout 30 node "$probe" --version "$v" --json 2>&1)"
639
+ local rc=$?
640
+ set -e
641
+ case "$raw" in
642
+ *'"status":"PASS"'*) status="PASS" ;;
643
+ *'"status":"NOT_TESTED"'*) status="NOT_TESTED" ;;
644
+ *'"status":"AMBIENTAL"'*) status="AMBIENTAL" ;;
645
+ *) status="FAIL" ;;
646
+ esac
647
+ detail="$(printf '%s' "$raw" | tr '\n' ' ' | cut -c1-400)"
648
+ if [ "$rc" -eq 124 ]; then
649
+ status="AMBIENTAL"
650
+ detail="context probe timed out after 30s"
651
+ elif [ "$rc" -ne 0 ] && [ "$status" = "PASS" ]; then
652
+ # A non-zero probe exit must never be reported as PASS.
653
+ status="FAIL"
654
+ detail="$detail (probe exit $rc)"
655
+ elif [ "$rc" -ne 0 ] && [ "$status" != "FAIL" ]; then
656
+ detail="$detail (probe exit $rc)"
657
+ fi
658
+ record "$v" "context-rehydrate" "$status" "$detail"
659
+ }
660
+
437
661
  run_version_prompts() { # v
438
662
  local v="$1" bin
439
663
  if ! bin="$(sandbox_bin_v "$v")"; then
@@ -459,8 +683,22 @@ run_version_prompts() { # v
459
683
  check_doctor "$v"
460
684
  }
461
685
 
686
+ probe_verdict() { # fail pass ambiental not_tested → one final status
687
+ # Verdict: PASS; Verdict: FAIL; Verdict: AMBIENTAL; Verdict: NOT_TESTED remain distinct.
688
+ local fail="$1" pass="$2" ambiental="$3" not_tested="$4"
689
+ if [ "$fail" -gt 0 ]; then
690
+ printf 'FAIL'
691
+ elif [ "$not_tested" -gt 0 ]; then
692
+ printf 'NOT_TESTED'
693
+ elif [ "$ambiental" -gt 0 ]; then
694
+ printf 'AMBIENTAL'
695
+ else
696
+ printf 'PASS'
697
+ fi
698
+ }
699
+
462
700
  write_report() { # versions...
463
- local versions=("$@") total_fail=0 total_pass=0 total_amb=0
701
+ local versions=("$@") total_fail=0 total_pass=0 total_amb=0 total_not_tested=0
464
702
  {
465
703
  printf '# Pantheon Sandbox Prompts Report\n\n'
466
704
  printf -- '- **Date:** %s\n' "$(date -u '+%Y-%m-%d %H:%M UTC')"
@@ -475,26 +713,114 @@ write_report() { # versions...
475
713
  for id in "${PROMPT_IDS[@]}" mcp-list doctor; do
476
714
  res="${RESULTS["$v:$id"]:-SKIP}"
477
715
  case "$res" in
478
- PASS) total_pass=$((total_pass + 1)) ;;
479
- AMBIENTAL) total_amb=$((total_amb + 1)) ;;
480
- FAIL) total_fail=$((total_fail + 1)) ;;
716
+ PASS) total_pass=$((total_pass + 1)) ;;
717
+ AMBIENTAL) total_amb=$((total_amb + 1)) ;;
718
+ FAIL) total_fail=$((total_fail + 1)) ;;
719
+ NOT_TESTED) total_not_tested=$((total_not_tested + 1)) ;;
481
720
  esac
482
721
  printf '| %s | %s | %s |\n' "$id" "$res" "${DETAILS["$v:$id"]:-—}"
483
722
  done
484
723
  printf '\n'
485
724
  done
486
725
  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
726
+ printf -- '- PASS: %d\n- FAIL (real): %d\n- AMBIENTAL: %d\n- NOT_TESTED: %d\n- Retries used: %d\n\n' \
727
+ "$total_pass" "$total_fail" "$total_amb" "$total_not_tested" "$RETRIES_USED"
728
+ printf '**Verdict: %s**\n' "$(probe_verdict "$total_fail" "$total_pass" "$total_amb" "$total_not_tested")"
494
729
  } > "$REPORT_FILE"
495
730
  log "Report written to $REPORT_FILE"
496
731
  }
497
732
 
733
+ write_cost_report() { # versions...
734
+ local versions=("$@") total_fail=0 total_pass=0 total_ambient=0 total_not_tested=0
735
+ {
736
+ printf '# Pantheon Cost Probe Report\n\n'
737
+ printf -- '- **Date:** %s\n' "$(date -u '+%Y-%m-%d %H:%M UTC')"
738
+ printf -- '- **Sandbox:** %s\n\n' "$SANDBOX_ROOT"
739
+ printf '| Version | Result | Detail |\n|---|---|---|\n'
740
+ local v result
741
+ for v in "${versions[@]}"; do
742
+ result="${RESULTS["$v:pantheon-cost"]:-NOT_TESTED}"
743
+ case "$result" in
744
+ PASS) total_pass=$((total_pass + 1)) ;;
745
+ FAIL) total_fail=$((total_fail + 1)) ;;
746
+ AMBIENTAL) total_ambient=$((total_ambient + 1)) ;;
747
+ NOT_TESTED) total_not_tested=$((total_not_tested + 1)) ;;
748
+ esac
749
+ printf '| %s | %s | %s |\n' "$v" "$result" "${DETAILS["$v:pantheon-cost"]:-—}"
750
+ done
751
+ printf '\n## Summary\n\n'
752
+ printf -- '- PASS: %d\n- FAIL (real): %d\n- AMBIENTAL: %d\n- NOT_TESTED: %d\n\n' \
753
+ "$total_pass" "$total_fail" "$total_ambient" "$total_not_tested"
754
+ printf '**Verdict: %s**\n' "$(probe_verdict "$total_fail" "$total_pass" "$total_ambient" "$total_not_tested")"
755
+ } > "$COST_REPORT_FILE"
756
+ log "Cost probe report written to $COST_REPORT_FILE"
757
+ }
758
+
759
+ write_rehydrate_report() { # versions...
760
+ local versions=("$@") total_fail=0 total_pass=0 total_ambient=0 total_not_tested=0
761
+ {
762
+ printf '# Pantheon Context Rehydration Probe Report\n\n'
763
+ printf -- '- **Date:** %s\n' "$(date -u '+%Y-%m-%d %H:%M UTC')"
764
+ printf -- '- **Sandbox:** %s\n\n' "$SANDBOX_ROOT"
765
+ printf '| Version | Probe | Result | Detail |\n|---|---|---|---|\n'
766
+ local v result
767
+ for v in "${versions[@]}"; do
768
+ result="${RESULTS["$v:context-rehydrate"]:-NOT_TESTED}"
769
+ case "$result" in
770
+ PASS) total_pass=$((total_pass + 1)) ;;
771
+ FAIL) total_fail=$((total_fail + 1)) ;;
772
+ AMBIENTAL) total_ambient=$((total_ambient + 1)) ;;
773
+ NOT_TESTED) total_not_tested=$((total_not_tested + 1)) ;;
774
+ esac
775
+ printf '| %s | context_rehydrate + context_session_summary | %s | %s |\n' \
776
+ "$v" "$result" "${DETAILS["$v:context-rehydrate"]:-—}"
777
+ done
778
+ printf '\n## Summary\n\n'
779
+ printf -- '- PASS: %d\n- FAIL (real): %d\n- AMBIENTAL: %d\n- NOT_TESTED: %d\n\n' \
780
+ "$total_pass" "$total_fail" "$total_ambient" "$total_not_tested"
781
+ printf '**Verdict: %s**\n' "$(probe_verdict "$total_fail" "$total_pass" "$total_ambient" "$total_not_tested")"
782
+ } > "$REHYDRATE_REPORT_FILE"
783
+ log "Context probe report written to $REHYDRATE_REPORT_FILE"
784
+ }
785
+
786
+ run_cost() { # versions...
787
+ local versions=("$@") v fail=0
788
+ mkdir -p "$SANDBOX_ROOT"
789
+ for v in "${versions[@]}"; do
790
+ log "[$v] pantheon_cost offline probe ..."
791
+ run_cost_probe "$v"
792
+ log "[$v] pantheon-cost → ${RESULTS["$v:pantheon-cost"]}"
793
+ done
794
+ write_cost_report "${versions[@]}"
795
+ for v in "${versions[@]}"; do
796
+ [ "${RESULTS["$v:pantheon-cost"]:-}" = "FAIL" ] && fail=$((fail + 1)) || true
797
+ done
798
+ if [ "$fail" -gt 0 ]; then
799
+ log "Cost probe: $fail real FAIL(s)."
800
+ exit 1
801
+ fi
802
+ log "Cost probe: no real failures (NOT_TESTED/AMBIENTAL is non-blocking)."
803
+ }
804
+
805
+ run_rehydrate() { # versions...
806
+ local versions=("$@") v fail=0
807
+ mkdir -p "$SANDBOX_ROOT"
808
+ for v in "${versions[@]}"; do
809
+ log "[$v] context_rehydrate/session_summary offline probe ..."
810
+ run_rehydrate_probe "$v"
811
+ log "[$v] context probe → ${RESULTS["$v:context-rehydrate"]}"
812
+ done
813
+ write_rehydrate_report "${versions[@]}"
814
+ for v in "${versions[@]}"; do
815
+ [ "${RESULTS["$v:context-rehydrate"]:-}" = "FAIL" ] && fail=$((fail + 1)) || true
816
+ done
817
+ if [ "$fail" -gt 0 ]; then
818
+ log "Context probe: $fail real FAIL(s)."
819
+ exit 1
820
+ fi
821
+ log "Context probe: no real failures (NOT_TESTED/AMBIENTAL is non-blocking)."
822
+ }
823
+
498
824
  run_battery() { # versions...
499
825
  local versions=("$@")
500
826
  mkdir -p "$SANDBOX_ROOT"
@@ -551,6 +877,8 @@ while [ $# -gt 0 ]; do
551
877
  --prepare) MODE_PREPARE=1 ;;
552
878
  --reset) MODE_RESET=1 ;;
553
879
  --prompts) MODE_PROMPTS=1 ;;
880
+ --cost) MODE_COST=1 ;;
881
+ --rehydrate) MODE_REHYDRATE=1 ;;
554
882
  --run)
555
883
  shift
556
884
  case "${1:-}" in
@@ -569,7 +897,9 @@ if [ "$MODE_RESET" -eq 1 ]; then
569
897
  exit 0
570
898
  fi
571
899
 
572
- if [ "$MODE_PREPARE" -eq 0 ] && [ -z "$RUN_VERSION" ] && [ "$MODE_PROMPTS" -eq 0 ]; then
900
+ if [ "$MODE_PREPARE" -eq 0 ] && [ -z "$RUN_VERSION" ] \
901
+ && [ "$MODE_PROMPTS" -eq 0 ] && [ "$MODE_COST" -eq 0 ] \
902
+ && [ "$MODE_REHYDRATE" -eq 0 ]; then
573
903
  usage
574
904
  fi
575
905
 
@@ -585,7 +915,30 @@ if [ -n "$RUN_VERSION" ]; then
585
915
  sandbox_env
586
916
  run_base "$RUN_VERSION"
587
917
  fi
918
+ if [ "$MODE_COST" -eq 1 ]; then
919
+ sandbox_env
920
+ run_cost "$RUN_VERSION"
921
+ fi
922
+ if [ "$MODE_REHYDRATE" -eq 1 ]; then
923
+ sandbox_env
924
+ run_rehydrate "$RUN_VERSION"
925
+ fi
588
926
  elif [ "$MODE_PROMPTS" -eq 1 ]; then
589
927
  sandbox_env
590
928
  run_battery v1 v2
929
+ if [ "$MODE_COST" -eq 1 ]; then
930
+ run_cost v1 v2
931
+ fi
932
+ if [ "$MODE_REHYDRATE" -eq 1 ]; then
933
+ run_rehydrate v1 v2
934
+ fi
935
+ elif [ "$MODE_COST" -eq 1 ]; then
936
+ sandbox_env
937
+ run_cost v1 v2
938
+ if [ "$MODE_REHYDRATE" -eq 1 ]; then
939
+ run_rehydrate v1 v2
940
+ fi
941
+ elif [ "$MODE_REHYDRATE" -eq 1 ]; then
942
+ sandbox_env
943
+ run_rehydrate v1 v2
591
944
  fi
@@ -566,7 +566,7 @@ async def kv_delete_namespace(
566
566
  # ── Context Checkpoint Tools ────────────────────────────────────────────────────
567
567
  # Session-scoped context storage for deepwork checkpoints, phase state,
568
568
  # tool call history, and reasoning chains. All entries use namespace
569
- # "checkpoint:{slug}" with auto-TTL of 4 hours (session duration).
569
+ # "checkpoint:{slug}:{session}" with auto-TTL of 4 hours (session duration).
570
570
 
571
571
  DEFAULT_CONTEXT_TTL: int = 14400 # 4 hours
572
572
 
@@ -1023,6 +1023,31 @@ def _load_checkpoint_for_rehydrate(
1023
1023
  return None
1024
1024
 
1025
1025
 
1026
+ def _checkpoint_namespaces(
1027
+ conn: sqlite3.Connection, slug: str, session_id: str | None
1028
+ ) -> list[str]:
1029
+ """Resolve checkpoint namespaces while preserving session isolation.
1030
+
1031
+ ``context_save`` always creates a session-qualified namespace, including
1032
+ when the caller omits ``session_id`` (it generates one and returns it).
1033
+ Runtime callers may not retain that return value after compaction, so an
1034
+ unqualified rehydrate/summary must discover the newest matching session.
1035
+ The prefix comparison is parameterized and avoids LIKE wildcard leakage.
1036
+ """
1037
+ if session_id:
1038
+ return [f"checkpoint:{slug}:{session_id}"]
1039
+ prefix = f"checkpoint:{slug}:"
1040
+ rows = conn.execute(
1041
+ "SELECT namespace FROM kv_store "
1042
+ "WHERE substr(namespace, 1, ?) = ? "
1043
+ "AND (expires_at IS NULL OR datetime(expires_at) > datetime('now')) "
1044
+ "AND deleted_at IS NULL "
1045
+ "GROUP BY namespace ORDER BY MAX(updated_at) DESC LIMIT 20",
1046
+ (len(prefix), prefix),
1047
+ ).fetchall()
1048
+ return [str(row[0]) for row in rows if row and row[0]]
1049
+
1050
+
1026
1051
  def _refresh_heartbeat_ttl(conn: sqlite3.Connection, namespace: str) -> None:
1027
1052
  """Extend an unexpired heartbeat TTL; never shorten, never resurrect."""
1028
1053
  row = conn.execute(
@@ -1055,7 +1080,8 @@ def _refresh_heartbeat_ttl(conn: sqlite3.Connection, namespace: str) -> None:
1055
1080
  @mcp.tool(
1056
1081
  name="context_rehydrate",
1057
1082
  description="Rehydrate critical context after native compaction. "
1058
- "Reads 'latest' + serialized 'tail' from checkpoint:<slug> and rebuilds "
1083
+ "Reads 'latest' + serialized 'tail' from a session-qualified checkpoint "
1084
+ "namespace and rebuilds "
1059
1085
  "goal/phase/delegation blocks deterministically (no LLM). Refreshes the "
1060
1086
  "heartbeat TTL. Idempotent. Honors PANTHEON_COMPACTION=off.",
1061
1087
  )
@@ -1068,14 +1094,21 @@ async def context_rehydrate(
1068
1094
  if _compaction_disabled():
1069
1095
  return None
1070
1096
  conn = _db(scope)
1071
- ns = f"checkpoint:{slug}:{session_id}" if session_id else f"checkpoint:{slug}"
1072
- checkpoint = _load_checkpoint_for_rehydrate(conn, ns)
1097
+ namespaces = _checkpoint_namespaces(conn, slug, session_id)
1098
+ checkpoint = None
1099
+ selected_namespace = None
1100
+ for namespace in namespaces:
1101
+ checkpoint = _load_checkpoint_for_rehydrate(conn, namespace)
1102
+ if checkpoint is not None:
1103
+ selected_namespace = namespace
1104
+ break
1073
1105
  if checkpoint is None:
1074
1106
  return None
1075
1107
  blocks = build_rehydration_blocks(checkpoint)
1076
1108
  if blocks is None:
1077
1109
  return None
1078
- _refresh_heartbeat_ttl(conn, ns)
1110
+ if selected_namespace is not None:
1111
+ _refresh_heartbeat_ttl(conn, selected_namespace)
1079
1112
  return blocks
1080
1113
 
1081
1114
 
@@ -1095,8 +1128,12 @@ async def context_session_summary(
1095
1128
  if os.environ.get(SESSION_END_SUMMARY_ENV, "").lower() == "off":
1096
1129
  return None
1097
1130
  conn = _db(scope)
1098
- ns = f"checkpoint:{slug}:{session_id}" if session_id else f"checkpoint:{slug}"
1099
- checkpoint = _load_checkpoint_for_rehydrate(conn, ns)
1131
+ namespaces = _checkpoint_namespaces(conn, slug, session_id)
1132
+ checkpoint = None
1133
+ for namespace in namespaces:
1134
+ checkpoint = _load_checkpoint_for_rehydrate(conn, namespace)
1135
+ if checkpoint is not None:
1136
+ break
1100
1137
  if checkpoint is None:
1101
1138
  return None
1102
1139
  return build_session_end_summary(checkpoint)