pi-oracle 0.7.11 → 0.7.13

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.
@@ -22,6 +22,9 @@ import { getProjectId } from "./runtime.js";
22
22
  export const ORACLE_PROVIDERS = ["chatgpt", "grok"] as const;
23
23
  export type OracleProvider = (typeof ORACLE_PROVIDERS)[number];
24
24
 
25
+ export { resolveOracleArchiveFormat, resolveOracleProviderArchivePlan } from "./provider-capabilities.js";
26
+ export type { OracleArchiveFormat, OracleProviderArchivePlan } from "./provider-capabilities.js";
27
+
25
28
  export const MODEL_FAMILIES = ["instant", "thinking", "pro", "grok"] as const;
26
29
  export type OracleModelFamily = (typeof MODEL_FAMILIES)[number];
27
30
 
@@ -209,13 +212,19 @@ export function getProviderAuthSeedProfileDir(config: OracleConfig, provider: Or
209
212
  }
210
213
 
211
214
  export function resolveOracleConfigForProvider(config: OracleConfig, provider: OracleProvider): OracleConfig {
212
- if (provider === "chatgpt") return config;
215
+ const defaults = {
216
+ ...config.defaults,
217
+ provider,
218
+ };
219
+ if (provider === "chatgpt") {
220
+ return {
221
+ ...config,
222
+ defaults,
223
+ };
224
+ }
213
225
  return {
214
226
  ...config,
215
- defaults: {
216
- ...config.defaults,
217
- provider,
218
- },
227
+ defaults,
219
228
  browser: {
220
229
  ...config.browser,
221
230
  authSeedProfileDir: getProviderAuthSeedProfileDir(config, provider),
@@ -4,9 +4,11 @@
4
4
  // Usage: Imported by oracle commands, tools, queue logic, poller flows, and runtime cleanup/reconciliation paths.
5
5
  // Invariants/Assumptions: Job mutations happen under per-job locks, worker identity checks defend against PID reuse, and persisted jobs remain the source of truth.
6
6
  import { createHash, randomUUID } from "node:crypto";
7
+ import { execFileSync } from "node:child_process";
7
8
  import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
8
9
  import { chmod, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
9
10
  import { isAbsolute, join, relative as relativePath, resolve, sep } from "node:path";
11
+ import { fileURLToPath } from "node:url";
10
12
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
11
13
  import {
12
14
  ACTIVE_ORACLE_JOB_STATUSES,
@@ -28,6 +30,7 @@ import type { OracleJobLifecycleEvent as SharedOracleJobLifecycleEvent, OracleJo
28
30
  import { hasDurableWorkerHandoff as sharedHasDurableWorkerHandoff } from "../shared/job-coordination-helpers.mjs";
29
31
  import { isTrackedProcessAlive, readProcessStartedAt, spawnDetachedNodeProcess, terminateTrackedProcess } from "../shared/process-helpers.mjs";
30
32
  import type { OracleConfig, OracleResolvedSelection } from "./config.js";
33
+ import { resolveOracleProviderArchivePlan } from "./provider-capabilities.js";
31
34
  import { withJobLock, withLock } from "./locks.js";
32
35
  import { cleanupRuntimeArtifacts, getProjectId, getSessionId, parseConversationId, requirePersistedSessionFile, type OracleCleanupReport } from "./runtime.js";
33
36
 
@@ -116,6 +119,14 @@ export interface OracleArtifactRecord {
116
119
  matchesUploadedArchive?: boolean;
117
120
  }
118
121
 
122
+ export interface OracleExtensionProvenance {
123
+ schemaVersion: 1;
124
+ packageName: string;
125
+ packageVersion: string;
126
+ sourcePath: string;
127
+ gitHead?: string;
128
+ }
129
+
119
130
  export interface OracleJob {
120
131
  id: string;
121
132
  status: OracleJobStatus;
@@ -134,6 +145,7 @@ export interface OracleJob {
134
145
  originSessionFile?: string;
135
146
  requestSource: "command" | "tool";
136
147
  selection: OracleResolvedSelection;
148
+ extensionProvenance?: OracleExtensionProvenance;
137
149
  followUpToJobId?: string;
138
150
  chatUrl?: string;
139
151
  conversationId?: string;
@@ -451,8 +463,8 @@ export async function cleanupJobResources(
451
463
 
452
464
  function getCleanupRetentionMs(job: OracleJob): { complete: number; failed: number } {
453
465
  return {
454
- complete: job.config.cleanup?.completeJobRetentionMs ?? ORACLE_COMPLETE_JOB_RETENTION_MS,
455
- failed: job.config.cleanup?.failedJobRetentionMs ?? ORACLE_FAILED_JOB_RETENTION_MS,
466
+ complete: job.config?.cleanup?.completeJobRetentionMs ?? ORACLE_COMPLETE_JOB_RETENTION_MS,
467
+ failed: job.config?.cleanup?.failedJobRetentionMs ?? ORACLE_FAILED_JOB_RETENTION_MS,
456
468
  };
457
469
  }
458
470
 
@@ -898,6 +910,39 @@ export async function cancelOracleJob(id: string, reason = "Cancelled by user"):
898
910
  });
899
911
  }
900
912
 
913
+ function readExtensionProvenance(cwd: string): OracleExtensionProvenance {
914
+ const sourcePath = resolve(fileURLToPath(new URL("../../../", import.meta.url)));
915
+ let packageName = "pi-oracle";
916
+ let packageVersion = "unknown";
917
+ try {
918
+ const packageJson = JSON.parse(readFileSync(join(sourcePath, "package.json"), "utf8")) as { name?: string; version?: string };
919
+ packageName = packageJson.name || packageName;
920
+ packageVersion = packageJson.version || packageVersion;
921
+ } catch {
922
+ // Keep provenance present even when package metadata is unavailable in an
923
+ // unusual loader; release proof rejects unknown versions.
924
+ }
925
+
926
+ let gitHead: string | undefined;
927
+ try {
928
+ gitHead = execFileSync("git", ["rev-parse", "HEAD"], { cwd: sourcePath, encoding: "utf8" }).trim();
929
+ } catch {
930
+ try {
931
+ gitHead = execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" }).trim();
932
+ } catch {
933
+ gitHead = undefined;
934
+ }
935
+ }
936
+
937
+ return {
938
+ schemaVersion: 1,
939
+ packageName,
940
+ packageVersion,
941
+ sourcePath,
942
+ gitHead,
943
+ };
944
+ }
945
+
901
946
  export async function createJob(
902
947
  id: string,
903
948
  input: OracleSubmitInput,
@@ -911,7 +956,7 @@ export async function createJob(
911
956
  const logsDir = join(jobDir, "logs");
912
957
  const workerLogPath = join(logsDir, "worker.log");
913
958
  const promptPath = join(jobDir, "prompt.md");
914
- const archivePath = join(jobDir, `context-${id}.tar.zst`);
959
+ const archivePath = join(jobDir, `context-${id}.${resolveOracleProviderArchivePlan(input.selection.provider).archiveExtension}`);
915
960
  const responsePath = join(jobDir, "response.md");
916
961
  const reasoningPath = join(jobDir, "reasoning.md");
917
962
  const artifactsManifestPath = join(jobDir, "artifacts.json");
@@ -945,6 +990,7 @@ export async function createJob(
945
990
  originSessionFile: sessionFile,
946
991
  requestSource: input.requestSource,
947
992
  selection: input.selection,
993
+ extensionProvenance: readExtensionProvenance(cwd),
948
994
  followUpToJobId: input.followUpToJobId,
949
995
  chatUrl: input.chatUrl,
950
996
  conversationId,
@@ -0,0 +1,41 @@
1
+ // Purpose: Centralize provider-specific archive capabilities for oracle submissions.
2
+ // Responsibilities: Map each supported provider to archive format, file extension, upload ceiling, and local compression dependencies.
3
+ // Scope: Static provider capability data only; config loading, archive writing, and runtime preflight live in sibling modules.
4
+ // Usage: Imported by config, jobs, tools, runtime, and archive code so provider policy stays in one canonical place.
5
+ // Invariants/Assumptions: Provider ids are defined by config.ts, while this module must not import config values to avoid runtime/config cycles.
6
+ import type { OracleProvider } from "./config.js";
7
+
8
+ export type OracleArchiveFormat = "tar.zst" | "tar.gz";
9
+
10
+ export interface OracleProviderArchivePlan {
11
+ provider: OracleProvider;
12
+ archiveFormat: OracleArchiveFormat;
13
+ archiveExtension: OracleArchiveFormat;
14
+ maxArchiveBytes: number;
15
+ requiresZstd: boolean;
16
+ }
17
+
18
+ const ORACLE_PROVIDER_ARCHIVE_PLANS: Record<OracleProvider, OracleProviderArchivePlan> = {
19
+ chatgpt: {
20
+ provider: "chatgpt",
21
+ archiveFormat: "tar.zst",
22
+ archiveExtension: "tar.zst",
23
+ maxArchiveBytes: 250 * 1024 * 1024,
24
+ requiresZstd: true,
25
+ },
26
+ grok: {
27
+ provider: "grok",
28
+ archiveFormat: "tar.gz",
29
+ archiveExtension: "tar.gz",
30
+ maxArchiveBytes: 200 * 1024 * 1024,
31
+ requiresZstd: false,
32
+ },
33
+ };
34
+
35
+ export function resolveOracleProviderArchivePlan(provider: OracleProvider): OracleProviderArchivePlan {
36
+ return ORACLE_PROVIDER_ARCHIVE_PLANS[provider];
37
+ }
38
+
39
+ export function resolveOracleArchiveFormat(provider: OracleProvider): OracleArchiveFormat {
40
+ return resolveOracleProviderArchivePlan(provider).archiveFormat;
41
+ }
@@ -11,7 +11,8 @@ import { delimiter, dirname, join } from "node:path";
11
11
  import { assertNotKnownBrowserUserDataPath, sweetCookieSafeStoragePasswordScrubbedEnv } from "../shared/browser-profile-helpers.mjs";
12
12
  import { jobBlocksAdmission } from "../shared/job-coordination-helpers.mjs";
13
13
  import { isTrackedProcessAlive } from "../shared/process-helpers.mjs";
14
- import type { OracleConfig } from "./config.js";
14
+ import type { OracleConfig, OracleProvider } from "./config.js";
15
+ import { resolveOracleProviderArchivePlan } from "./provider-capabilities.js";
15
16
  import { createLease, listLeaseMetadata, readLeaseMetadata, releaseLease, withAuthLock } from "./locks.js";
16
17
 
17
18
  const SEED_GENERATION_FILE = ".oracle-seed-generation";
@@ -49,12 +50,15 @@ function cpCommand(): string {
49
50
  return process.env.PI_ORACLE_CP_PATH?.trim() || "cp";
50
51
  }
51
52
 
52
- function requiredOracleDependencies(config: OracleConfig): Array<{ name: string; command: string }> {
53
+ function requiredOracleDependencies(config: OracleConfig, provider = config.defaults.provider): Array<{ name: string; command: string }> {
54
+ const archivePlan = resolveOracleProviderArchivePlan(provider);
53
55
  const dependencies = [
54
56
  { name: "agent-browser", command: AGENT_BROWSER_BIN },
55
57
  { name: "tar", command: "tar" },
56
- { name: "zstd", command: "zstd" },
57
58
  ];
59
+ if (archivePlan.requiresZstd) {
60
+ dependencies.push({ name: "zstd", command: "zstd" });
61
+ }
58
62
  if (config.browser.cloneStrategy === "apfs-clone" && process.platform === "darwin") {
59
63
  dependencies.push({ name: "cp", command: cpCommand() });
60
64
  }
@@ -325,11 +329,11 @@ export async function assertOracleAuthSeedProfileReady(config: OracleConfig): Pr
325
329
  }
326
330
  }
327
331
 
328
- export async function assertOracleSubmitPrerequisites(config: OracleConfig): Promise<void> {
332
+ export async function assertOracleSubmitPrerequisites(config: OracleConfig, provider: OracleProvider = config.defaults.provider): Promise<void> {
329
333
  assertSafeOracleProfilePath(config.browser.runtimeProfilesDir, "runtime profiles", config);
330
334
  await assertOracleAuthSeedProfileReady(config);
331
335
  await assertConfiguredBrowserExecutableReady(config.browser.executablePath);
332
- for (const dependency of requiredOracleDependencies(config)) {
336
+ for (const dependency of requiredOracleDependencies(config, provider)) {
333
337
  await assertRequiredLocalDependencyReady(dependency.name, dependency.command);
334
338
  }
335
339
  await assertWritableDirectory(config.browser.runtimeProfilesDir, "runtime profiles");