pi-oracle 0.7.17 → 0.7.19

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/CHANGELOG.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.7.19 - 2026-07-16
4
+
5
+ ### Changed
6
+ - refreshed the local Pi/pi-ai development lock, sanity contracts, safe-loader version check, and compatibility guidance for Pi 0.80.9; Oracle does not use the removed SDK auth/session options
7
+
8
+ ## 0.7.18 - 2026-07-14
9
+
10
+ ### Changed
11
+ - refreshed the local Pi/pi-ai development lock, sanity contracts, safe-loader version check, and compatibility guidance for Pi 0.80.7
12
+ - updated the local Pi and pi-ai development baseline to 0.80.6
13
+ - switched provider and mode tool schemas to Pi's Google-compatible `StringEnum` helper
14
+
15
+ ### Fixed
16
+ - made the sanity harness independent of the caller's browser profile layout and installed `agent-browser` sessions
4
17
 
5
18
  ## 0.7.17 - 2026-07-01
6
19
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `pi-oracle` lets a `pi` agent send hard, long-running work to ChatGPT.com or Grok through the web app, with repo archives, background execution, saved results, and a best-effort wake-up back into `pi` when the answer is ready.
4
4
 
5
- > Status: experimental public beta. Current local validation baseline is pi `0.80.2`; the platform-smoke harness covers macOS, Linux, and Windows native with Chromium-family browsers. Pi `0.80.2+` is the suggested tested floor for project-trust-aware package/runtime validation, but pi-bundled runtime packages remain optional wildcard peers so npm peer ranges do not block users from trying newer pi releases. Normal oracle jobs run in an isolated browser profile, not your active browser window.
5
+ > Status: experimental public beta. Current local validation baseline is pi `0.80.9`; the platform-smoke harness covers macOS, Linux, and Windows native with Chromium-family browsers. Pi `0.80.9+` is the suggested tested floor for project-trust-aware package/runtime validation, but pi-bundled runtime packages remain optional wildcard peers so npm peer ranges do not block users from trying newer pi releases. Normal oracle jobs run in an isolated browser profile, not your active browser window.
6
6
 
7
7
  ## What a successful run looks like
8
8
 
@@ -79,7 +79,7 @@ You need:
79
79
 
80
80
  - macOS, Linux, or Windows native
81
81
  - Node.js 22.19.0 or newer for package install/use; platform smoke/release validation currently expects Node 24+ per `platform-smoke.config.mjs`
82
- - Suggested tested floor: `pi` 0.80.2 or newer; older pi versions are not blocked by package metadata but are outside the current validation baseline
82
+ - Suggested tested floor: `pi` 0.80.9 or newer; older pi versions are not blocked by package metadata but are outside the current validation baseline
83
83
  - Google Chrome/Chromium or another Chromium-family browser
84
84
  - ChatGPT or Grok already signed in to the configured local browser profile for the provider you plan to use
85
85
  - `agent-browser` and `tar` available on the machine; `zstd` is also required when submitting ChatGPT `.tar.zst` archives
@@ -7,7 +7,7 @@ Companion doc:
7
7
  - `docs/ORACLE_RECOVERY_DRILL.md` — safe expired-auth recovery validation drill
8
8
 
9
9
  Compatibility target:
10
- - `pi` 0.80.2+ is the suggested tested floor for current project-trust-aware package/runtime validation
10
+ - `pi` 0.80.9+ is the suggested tested floor for current project-trust-aware package/runtime validation
11
11
  - package metadata keeps pi runtime packages as optional wildcard peers, so this suggested floor is not enforced as a hard npm install requirement
12
12
  - current extension lifecycle only; no backward-compatibility shims for removed `session_switch` / `session_fork` events
13
13
 
@@ -639,6 +639,10 @@ Remaining non-blocking hardening work:
639
639
  - keep hardening model-selection verification against future ChatGPT UI variation
640
640
 
641
641
  Recent proof points:
642
+ - Pi 0.80.7 local gate: `npm run verify:oracle` passed on 2026-07-14, including syntax/bundle checks, both typechecks, the isolated sanity harness, and `npm pack --dry-run`
643
+ - Pi 0.80.7 safe loader smokes: `.artifacts/real-smoke/run-1784068526377-67yb2l` passed source loading, and `.artifacts/real-smoke/run-1784068527234-t2a5xm` passed packed-install loading through the real Pi CLI; both executed `/oracle-status` without creating an external oracle job or requiring provider credentials
644
+ - Pi 0.80.6 local gate: `npm run verify:oracle` passed three consecutive runs on 2026-07-11; each run completed syntax/bundle checks, both typechecks, the isolated sanity harness, and `npm pack --dry-run` without an `ENOTEMPTY` cleanup failure
645
+ - Pi 0.80.6 safe loader smokes: `.artifacts/real-smoke/run-1783810473367-cn72at` passed source loading, and `.artifacts/real-smoke/run-1783810475290-hj0pfs` passed packed-install loading through the real Pi CLI; both recorded `pi --version` as 0.80.6 and executed `/oracle-status` without creating an external oracle job or requiring provider credentials
642
646
  - Pi 0.80.2 local gate: `npm run verify:oracle` passed on 2026-06-24 after the JSON command output, prompt-manifest, schema, and lazy Chrome-probe audit fixes
643
647
  - Pi 0.80.2 isolated extension smokes: `.artifacts/real-smoke/run-1782321054924-jnq0x3` passed source proof, and `.artifacts/real-smoke/run-1782321056224-yuq5a2` passed packed-install proof
644
648
  - Pi 0.80.2 JSON command smoke: `pi --no-extensions -e ./extensions/oracle/index.ts --mode json --no-session --no-approve "/oracle-status"` emitted displayed `oracle-command-output` JSON events
@@ -658,6 +662,6 @@ Recent proof points:
658
662
  - expired-auth drill post-repair success: `fa26a2a7-0057-4a21-b3e0-71c1d020facf`
659
663
  - successful multi-artifact completion: `b6b3599c-6b91-4315-adfa-8a83aa5eda9b`
660
664
  - repo-owned sanity harness: `npm run sanity:oracle`
661
- - real installed-extension smoke source of truth: `scripts/oracle-real-smoke.mjs`; required release proof runs packed-install mode (`npm run smoke:real:packed`) and executes installed-package `oracle_submit` deterministically, with optional slower model-agent debugging via `PI_ORACLE_REAL_TEST_MODEL_AGENT=1`; source mode (`npm run smoke:real:source`) is inner-loop/debug only
665
+ - real installed-extension smoke source of truth: `scripts/oracle-real-smoke.mjs`; required release proof runs packed-install mode (`npm run smoke:real:packed`), asserts Pi 0.80.9, and executes `/oracle-status` through Pi's installed-package loader without provider credentials or an external oracle job; optional slower model-agent submission debugging remains behind `PI_ORACLE_REAL_TEST_MODEL_AGENT=1`; source mode (`npm run smoke:real:source`) is inner-loop/debug only
662
666
  - macOS, Ubuntu, and Windows native package/build/runtime smoke source of truth: `docs/platform-smoke.md`; use `npm run verify:oracle` for everyday local iteration, `npm run smoke:platform:doctor` plus a focused target/suite run for platform-sensitive changes, `npm run smoke:platform:all` for doctor-first platform matrix evidence, and `npm run release:check` for the full local-plus-platform release gate
663
667
  - release gate: `npm run release:check`, also used by `prepublishOnly`, combines static verification, fresh loaded-extension ChatGPT preset proof via `npm run release:proof:chatgpt-presets`, and all required Crabbox platform smokes
@@ -9,6 +9,7 @@ import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
  import { createArchive, type ArchiveCreationResult, type ArchiveSizeBreakdownRow } from "./archive.js";
11
11
  import { runOracleAuthBootstrap } from "./auth.js";
12
+ import { StringEnum } from "@earendil-works/pi-ai";
12
13
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
13
14
  import { Type, type Static } from "typebox";
14
15
  import { formatOracleCancelOutcome, formatOracleJobSummary, formatOracleSubmitResponse } from "../shared/job-observability-helpers.mjs";
@@ -68,13 +69,11 @@ import {
68
69
  } from "./runtime.js";
69
70
 
70
71
  const ORACLE_PROVIDER_PARAM_DESCRIPTION = `Oracle web provider. Omit to use the configured default provider. Supported providers: ${ORACLE_PROVIDERS.join(", ")}.`;
71
- const ORACLE_PROVIDER_PARAM = Type.Optional(Type.String({
72
+ const ORACLE_PROVIDER_PARAM = Type.Optional(StringEnum(ORACLE_PROVIDERS, {
72
73
  description: ORACLE_PROVIDER_PARAM_DESCRIPTION,
73
- enum: [...ORACLE_PROVIDERS],
74
74
  }));
75
- const ORACLE_GROK_MODE_PARAM = Type.Optional(Type.String({
75
+ const ORACLE_GROK_MODE_PARAM = Type.Optional(StringEnum(GROK_MODES, {
76
76
  description: "Provider mode. For Grok, only heavy is currently supported. Omit to use the configured default mode.",
77
- enum: [...GROK_MODES],
78
77
  }));
79
78
 
80
79
  const ORACLE_SUBMIT_PARAMS = Type.Object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-oracle",
3
- "version": "0.7.17",
3
+ "version": "0.7.19",
4
4
  "description": "ChatGPT and Grok web-oracle extension for pi with isolated browser auth, async jobs, and project-context archives.",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -48,7 +48,7 @@
48
48
  ]
49
49
  },
50
50
  "scripts": {
51
- "check:oracle-extension": "node --check extensions/oracle/shared/browser-profile-helpers.mjs && node --check extensions/oracle/shared/process-helpers.mjs && node --check extensions/oracle/shared/state-path-helpers.mjs && node --check extensions/oracle/shared/time-helpers.mjs && node --check extensions/oracle/shared/state-coordination-helpers.mjs && node --check extensions/oracle/shared/job-coordination-helpers.mjs && node --check extensions/oracle/shared/job-lifecycle-helpers.mjs && node --check extensions/oracle/shared/job-observability-helpers.mjs && node --check extensions/oracle/worker/run-job.mjs && node --check extensions/oracle/worker/state-locks.mjs && node --check extensions/oracle/worker/artifact-heuristics.mjs && node --check extensions/oracle/worker/chatgpt-ui-helpers.mjs && node --check extensions/oracle/worker/chatgpt-flow-helpers.mjs && node --check extensions/oracle/worker/auth-flow-helpers.mjs && node --check extensions/oracle/worker/auth-cookie-policy.mjs && node --check extensions/oracle/worker/chromium-cookie-source.mjs && node --check extensions/oracle/worker/auth-bootstrap.mjs && esbuild extensions/oracle/index.ts --bundle --platform=node --format=esm --external:@earendil-works/pi-coding-agent --external:typebox --outfile=/tmp/pi-oracle-extension-check.js",
51
+ "check:oracle-extension": "node --check extensions/oracle/shared/browser-profile-helpers.mjs && node --check extensions/oracle/shared/process-helpers.mjs && node --check extensions/oracle/shared/state-path-helpers.mjs && node --check extensions/oracle/shared/time-helpers.mjs && node --check extensions/oracle/shared/state-coordination-helpers.mjs && node --check extensions/oracle/shared/job-coordination-helpers.mjs && node --check extensions/oracle/shared/job-lifecycle-helpers.mjs && node --check extensions/oracle/shared/job-observability-helpers.mjs && node --check extensions/oracle/worker/run-job.mjs && node --check extensions/oracle/worker/state-locks.mjs && node --check extensions/oracle/worker/artifact-heuristics.mjs && node --check extensions/oracle/worker/chatgpt-ui-helpers.mjs && node --check extensions/oracle/worker/chatgpt-flow-helpers.mjs && node --check extensions/oracle/worker/auth-flow-helpers.mjs && node --check extensions/oracle/worker/auth-cookie-policy.mjs && node --check extensions/oracle/worker/chromium-cookie-source.mjs && node --check extensions/oracle/worker/auth-bootstrap.mjs && esbuild extensions/oracle/index.ts --bundle --platform=node --format=esm --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:typebox --outfile=/tmp/pi-oracle-extension-check.js",
52
52
  "typecheck": "tsc --noEmit -p tsconfig.json",
53
53
  "typecheck:worker-helpers": "tsc --noEmit -p tsconfig.worker-helpers.json",
54
54
  "sanity:oracle": "node scripts/oracle-sanity-runner.mjs",
@@ -78,6 +78,7 @@
78
78
  "@steipete/sweet-cookie": "^0.3.0"
79
79
  },
80
80
  "peerDependencies": {
81
+ "@earendil-works/pi-ai": "*",
81
82
  "@earendil-works/pi-coding-agent": "*",
82
83
  "typebox": "*"
83
84
  },
@@ -86,8 +87,8 @@
86
87
  "protobufjs": "7.6.1"
87
88
  },
88
89
  "devDependencies": {
89
- "@earendil-works/pi-ai": "^0.80.2",
90
- "@earendil-works/pi-coding-agent": "^0.80.2",
90
+ "@earendil-works/pi-ai": "^0.80.9",
91
+ "@earendil-works/pi-coding-agent": "^0.80.9",
91
92
  "@types/node": "^22.19.19",
92
93
  "esbuild": "^0.28.0",
93
94
  "tsx": "^4.22.3",
@@ -104,6 +105,9 @@
104
105
  ],
105
106
  "packageManager": "npm@11.16.0",
106
107
  "peerDependenciesMeta": {
108
+ "@earendil-works/pi-ai": {
109
+ "optional": true
110
+ },
107
111
  "@earendil-works/pi-coding-agent": {
108
112
  "optional": true
109
113
  },
@@ -5,17 +5,13 @@
5
5
  import { spawn } from "node:child_process";
6
6
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
7
7
  import { mkdtempSync } from "node:fs";
8
- import { createRequire } from "node:module";
9
8
  import { tmpdir } from "node:os";
10
9
  import { basename, dirname, join, resolve } from "node:path";
11
- import { pathToFileURL } from "node:url";
12
-
13
- const require = createRequire(import.meta.url);
14
- const tsxCli = require.resolve("tsx/cli");
15
10
 
16
11
  const DEFAULT_PROVIDER = "zai";
17
12
  const DEFAULT_MODEL = "glm-5.2";
18
13
  const DEFAULT_TIMEOUT_MS = 180_000;
14
+ const EXPECTED_PI_VERSION = "0.80.9";
19
15
  const PACKAGE_NAME = "pi-oracle";
20
16
 
21
17
  function usage() {
@@ -31,7 +27,7 @@ Environment:
31
27
  PI_ORACLE_REAL_TEST_TIMEOUT_MS per-agent timeout in ms (default: ${DEFAULT_TIMEOUT_MS})
32
28
  PI_ORACLE_REAL_TEST_ARTIFACT_ROOT artifact root (default: .artifacts/real-smoke)
33
29
  PI_ORACLE_REAL_TEST_KEEP_TMP keep temporary fixture directory when set to 1/true/yes
34
- PI_ORACLE_REAL_TEST_MODEL_AGENT run oracle_submit through a model-agent turn instead of direct installed-tool execution (off by default)
30
+ PI_ORACLE_REAL_TEST_MODEL_AGENT run oracle_submit through a credentialed model-agent turn instead of the safe loader command (off by default)
35
31
  PI_ORACLE_REAL_TEST_NEGATIVE_SYMLINK run optional second-agent symlink rejection check (off by default; covered by sanity:oracle)
36
32
  `);
37
33
  }
@@ -299,39 +295,14 @@ function hasCreatedJobArchive(jobsDir) {
299
295
  return Boolean(jobDir && archivePath && existsSync(archivePath));
300
296
  }
301
297
 
302
- async function runDirectOracleSubmit({ prepared, agentDir, sessionDir, jobsDir, outDir, timeoutMs }) {
298
+ async function runPiLoaderStatus({ prepared, agentDir, sessionDir, jobsDir, outDir, timeoutMs }) {
303
299
  mkdirSync(outDir, { recursive: true });
304
- const sessionFile = join(sessionDir, "real-smoke-session.jsonl");
305
- const fakeWorkerPath = join(outDir, "fake-worker.mjs");
306
- const scriptPath = join(outDir, "direct-submit.mjs");
307
- const packageRoot = prepared.mode === "packed" ? join(prepared.cwd, "node_modules", PACKAGE_NAME) : process.cwd();
308
- const toolsUrl = pathToFileURL(join(packageRoot, "extensions", "oracle", "lib", "tools.ts")).href;
309
300
  mkdirSync(sessionDir, { recursive: true });
310
- writeFileSync(sessionFile, "");
311
- writeFileSync(fakeWorkerPath, "process.exit(0);\n");
312
- writeFileSync(scriptPath, `
313
- import { registerOracleTools } from ${JSON.stringify(toolsUrl)};
314
- const tools = new Map();
315
- const pi = {
316
- on() { return undefined; },
317
- registerTool(tool) { tools.set(tool.name, tool); },
318
- };
319
- registerOracleTools(pi, ${JSON.stringify(fakeWorkerPath)});
320
- const submit = tools.get("oracle_submit");
321
- if (!submit) throw new Error("oracle_submit was not registered by the installed package");
322
- const ctx = {
323
- cwd: process.cwd(),
324
- sessionManager: { getSessionFile() { return ${JSON.stringify(sessionFile)}; } },
325
- };
326
- const result = await submit.execute("real-smoke", {
327
- prompt: "Real smoke archive test. Reply OK if this reaches the provider.",
328
- files: ["."],
329
- preset: "instant",
330
- }, new AbortController().signal, () => undefined, ctx);
331
- console.log(JSON.stringify(result, null, 2));
332
- `);
333
- writeFileSync(join(outDir, "command.json"), `${JSON.stringify({ command: process.execPath, args: [tsxCli, scriptPath], cwd: prepared.cwd, mode: prepared.mode, extensionPath: prepared.extensionPath }, null, 2)}\n`);
334
- const result = await runCommand(process.execPath, [tsxCli, scriptPath], {
301
+ const args = ["--mode", "json", "--no-session", prepared.mode === "packed" ? "--approve" : "--no-approve", "--session-dir", sessionDir];
302
+ if (prepared.mode === "source") args.push("--no-extensions", "-e", prepared.extensionPath);
303
+ args.push("/oracle-status");
304
+ writeFileSync(join(outDir, "command.json"), `${JSON.stringify({ command: piCommand(), args, cwd: prepared.cwd, mode: prepared.mode, extensionPath: prepared.extensionPath }, null, 2)}\n`);
305
+ const result = await runCommand(piCommand(), args, {
335
306
  cwd: prepared.cwd,
336
307
  env: {
337
308
  ...process.env,
@@ -341,9 +312,9 @@ console.log(JSON.stringify(result, null, 2));
341
312
  },
342
313
  timeoutMs,
343
314
  });
344
- writeRunResult(outDir, "direct-submit", result);
345
- if (result.timedOut) throw new Error(`direct oracle_submit timed out after ${timeoutMs}ms`);
346
- if (result.code !== 0) throw new Error(`direct oracle_submit exited ${result.code}; see ${join(outDir, "direct-submit.stderr.txt")}`);
315
+ writeRunResult(outDir, "pi-loader", result);
316
+ if (result.timedOut) throw new Error(`Pi loader smoke timed out after ${timeoutMs}ms`);
317
+ if (result.code !== 0) throw new Error(`Pi loader smoke exited ${result.code}; see ${join(outDir, "pi-loader.stderr.txt")}`);
347
318
  return result;
348
319
  }
349
320
 
@@ -385,10 +356,12 @@ async function run(mode = "packed") {
385
356
  const artifactRoot = resolve(env("PI_ORACLE_REAL_TEST_ARTIFACT_ROOT") ?? ".artifacts/real-smoke");
386
357
  const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
387
358
  const runDir = join(artifactRoot, runId);
388
- const tmpRoot = mkdtempSync(join(tmpdir(), "pi-oracle-real-smoke-"));
389
359
  const assertions = [];
390
360
 
391
361
  mkdirSync(runDir, { recursive: true });
362
+ const piVersion = await mustRun(runDir, "pi-version", piCommand(), ["--version"], { cwd: process.cwd(), env: process.env, timeoutMs: 30_000 });
363
+ if (piVersion.stdout.trim() !== EXPECTED_PI_VERSION) throw new Error(`real smoke requires Pi ${EXPECTED_PI_VERSION}; found ${piVersion.stdout.trim() || "unknown"}`);
364
+ const tmpRoot = mkdtempSync(join(tmpdir(), "pi-oracle-real-smoke-"));
392
365
  const prepared = mode === "packed"
393
366
  ? await preparePackedProject({ runDir, provider, model, timeoutMs })
394
367
  : prepareSourceProject({ provider, model });
@@ -411,7 +384,8 @@ async function run(mode = "packed") {
411
384
  mkdirSync(sessions1, { recursive: true });
412
385
  mkdirSync(jobs1, { recursive: true });
413
386
 
414
- const submitResult = truthy(env("PI_ORACLE_REAL_TEST_MODEL_AGENT"))
387
+ const modelAgent = truthy(env("PI_ORACLE_REAL_TEST_MODEL_AGENT"));
388
+ const submitResult = modelAgent
415
389
  ? await runPiAgent({
416
390
  prepared,
417
391
  agentDir: agent1,
@@ -423,7 +397,7 @@ async function run(mode = "packed") {
423
397
  stopAfterJobArchive: true,
424
398
  prompt: 'Call oracle_submit directly with prompt "Real smoke archive test. Reply OK if this reaches the provider." files ["."] and preset "instant". Do not use bash, read, edit, write, or any tool except oracle_submit.',
425
399
  })
426
- : await runDirectOracleSubmit({
400
+ : await runPiLoaderStatus({
427
401
  prepared,
428
402
  agentDir: agent1,
429
403
  sessionDir: sessions1,
@@ -433,16 +407,21 @@ async function run(mode = "packed") {
433
407
  });
434
408
 
435
409
  const jobDir1 = latestJobDir(jobs1);
436
- const archivePath = parseJobArchivePath(jobDir1);
437
- writeFileSync(join(test1, "job-dir.txt"), `${jobDir1 ?? ""}\n`);
438
- writeFileSync(join(test1, "archive-path.txt"), `${archivePath ?? ""}\n`);
439
- assert("whole-repo-job-created", Boolean(jobDir1), `whole-repo submit did not create an oracle job; stdout=${submitResult.stdout.slice(-1000)} stderr=${submitResult.stderr.slice(-1000)}`);
440
- assert("whole-repo-archive-created", Boolean(archivePath && existsSync(archivePath)), `whole-repo submit did not create a readable archive; jobDir=${jobDir1 ?? "<none>"} stdout=${submitResult.stdout.slice(-1000)} stderr=${submitResult.stderr.slice(-1000)}`);
441
- const entries = await tarList(archivePath);
442
- writeFileSync(join(test1, "archive-list.txt"), `${entries.join("\n")}\n`);
443
- assert("archive-includes-readme", entryExists(entries, "README.md"), "archive should include README.md");
444
- for (const excluded of [".pi", ".oracle-context", ".scratchpad.md", ".artifacts", ".crabbox", ".debug"]) {
445
- assert(`archive-excludes-${excluded.replace(/[^a-z0-9]+/gi, "-")}`, !entryExists(entries, excluded), `archive should exclude ${excluded}`);
410
+ if (modelAgent) {
411
+ const archivePath = parseJobArchivePath(jobDir1);
412
+ writeFileSync(join(test1, "job-dir.txt"), `${jobDir1 ?? ""}\n`);
413
+ writeFileSync(join(test1, "archive-path.txt"), `${archivePath ?? ""}\n`);
414
+ assert("whole-repo-job-created", Boolean(jobDir1), `whole-repo submit did not create an oracle job; stdout=${submitResult.stdout.slice(-1000)} stderr=${submitResult.stderr.slice(-1000)}`);
415
+ assert("whole-repo-archive-created", Boolean(archivePath && existsSync(archivePath)), `whole-repo submit did not create a readable archive; jobDir=${jobDir1 ?? "<none>"} stdout=${submitResult.stdout.slice(-1000)} stderr=${submitResult.stderr.slice(-1000)}`);
416
+ const entries = await tarList(archivePath);
417
+ writeFileSync(join(test1, "archive-list.txt"), `${entries.join("\n")}\n`);
418
+ assert("archive-includes-readme", entryExists(entries, "README.md"), "archive should include README.md");
419
+ for (const excluded of [".pi", ".oracle-context", ".scratchpad.md", ".artifacts", ".crabbox", ".debug"]) {
420
+ assert(`archive-excludes-${excluded.replace(/[^a-z0-9]+/gi, "-")}`, !entryExists(entries, excluded), `archive should exclude ${excluded}`);
421
+ }
422
+ } else {
423
+ assert("pi-loader-command-output", submitResult.stdout.includes('"customType":"oracle-command-output"'), "Pi loader smoke should execute the oracle command through the loaded extension");
424
+ assert("pi-loader-no-external-job", !jobDir1, "safe Pi loader smoke should not create an oracle job");
446
425
  }
447
426
  if (prepared.mode === "packed") {
448
427
  assert("packed-mode-no-source-extension", !readFileSync(join(test1, "command.json"), "utf8").includes("extensions/oracle/index.ts"), "packed real smoke must not use source extension path");
@@ -101,7 +101,8 @@ function testCanonicalWorkflowConfig() {
101
101
 
102
102
  function testRealSmokeExpensiveAgentPathsAreOptIn() {
103
103
  const source = readFileSync(new URL("../oracle-real-smoke.mjs", import.meta.url), "utf8");
104
- assert.match(source, /runDirectOracleSubmit/, "default real smoke should use deterministic installed-tool execution");
104
+ assert.match(source, /runPiLoaderStatus/, "default real smoke should execute a deterministic command through Pi's extension loader");
105
+ assert.doesNotMatch(source, /tsx\/cli|runDirectOracleSubmit/, "default real smoke must not bypass Pi's loader through checkout tsx");
105
106
  assert.match(source, /PI_ORACLE_REAL_TEST_MODEL_AGENT/, "real smoke should expose the optional model-agent toggle");
106
107
  const targetsSource = readFileSync(new URL("./targets.mjs", import.meta.url), "utf8");
107
108
  assert.match(targetsSource, /PI_ORACLE_REAL_TEST_MODEL_AGENT/, "platform real-extension command should forward the optional model-agent toggle");