pi-oracle 0.6.17 → 0.7.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/CHANGELOG.md +32 -0
- package/README.md +52 -39
- package/docs/ORACLE_DESIGN.md +29 -26
- package/extensions/oracle/lib/auth.ts +4 -3
- package/extensions/oracle/lib/commands.ts +15 -4
- package/extensions/oracle/lib/config.ts +50 -2
- package/extensions/oracle/lib/jobs.ts +1 -1
- package/extensions/oracle/lib/runtime.ts +10 -0
- package/extensions/oracle/lib/tools.ts +123 -36
- package/extensions/oracle/shared/job-observability-helpers.d.mts +2 -0
- package/extensions/oracle/shared/job-observability-helpers.mjs +6 -2
- package/extensions/oracle/worker/auth-bootstrap.mjs +63 -15
- package/extensions/oracle/worker/auth-cookie-policy.mjs +18 -4
- package/extensions/oracle/worker/auth-flow-helpers.mjs +3 -0
- package/extensions/oracle/worker/chatgpt-flow-helpers.mjs +2 -2
- package/extensions/oracle/worker/chromium-cookie-source.mjs +2 -2
- package/extensions/oracle/worker/run-job.mjs +234 -34
- package/package.json +6 -5
- package/prompts/oracle-followup.md +21 -19
- package/prompts/oracle.md +11 -9
|
@@ -943,7 +943,7 @@ export async function createJob(
|
|
|
943
943
|
requestSource: input.requestSource,
|
|
944
944
|
selection: input.selection,
|
|
945
945
|
followUpToJobId: input.followUpToJobId,
|
|
946
|
-
chatUrl: input.
|
|
946
|
+
chatUrl: input.chatUrl,
|
|
947
947
|
conversationId,
|
|
948
948
|
responseFormat: "text/plain",
|
|
949
949
|
artifactPaths: [],
|
|
@@ -429,6 +429,15 @@ async function spawnCp(args: string[], options?: { timeoutMs?: number }): Promis
|
|
|
429
429
|
});
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
async function removeChromiumProcessSingletonArtifacts(profileDir: string): Promise<void> {
|
|
433
|
+
await Promise.all([
|
|
434
|
+
rm(join(profileDir, "SingletonLock"), { force: true }),
|
|
435
|
+
rm(join(profileDir, "SingletonSocket"), { force: true }),
|
|
436
|
+
rm(join(profileDir, "SingletonCookie"), { force: true }),
|
|
437
|
+
rm(join(profileDir, "DevToolsActivePort"), { force: true }),
|
|
438
|
+
]);
|
|
439
|
+
}
|
|
440
|
+
|
|
432
441
|
export async function cloneSeedProfileToRuntime(
|
|
433
442
|
config: OracleConfig,
|
|
434
443
|
runtimeProfileDir: string,
|
|
@@ -441,6 +450,7 @@ export async function cloneSeedProfileToRuntime(
|
|
|
441
450
|
await rm(runtimeProfileDir, { recursive: true, force: true }).catch(() => undefined);
|
|
442
451
|
await mkdir(dirname(runtimeProfileDir), { recursive: true, mode: 0o700 }).catch(() => undefined);
|
|
443
452
|
await spawnCp(profileCloneArgs(config, seedDir, runtimeProfileDir), { timeoutMs: options?.cpTimeoutMs ?? PROFILE_CLONE_TIMEOUT_MS });
|
|
453
|
+
await removeChromiumProcessSingletonArtifacts(runtimeProfileDir);
|
|
444
454
|
});
|
|
445
455
|
|
|
446
456
|
return getSeedGeneration(config);
|
|
@@ -16,8 +16,12 @@ import { isLockTimeoutError, withGlobalReconcileLock, withLock } from "./locks.j
|
|
|
16
16
|
import {
|
|
17
17
|
coerceOracleSubmitPresetId,
|
|
18
18
|
loadOracleConfig,
|
|
19
|
+
ORACLE_PROVIDERS,
|
|
19
20
|
ORACLE_SUBMIT_PRESET_IDS,
|
|
21
|
+
resolveOracleConfigForProvider,
|
|
22
|
+
resolveOracleGrokMode,
|
|
20
23
|
resolveOracleSubmitPreset,
|
|
24
|
+
type OracleProvider,
|
|
21
25
|
} from "./config.js";
|
|
22
26
|
import {
|
|
23
27
|
appendCleanupWarnings,
|
|
@@ -59,7 +63,7 @@ import {
|
|
|
59
63
|
} from "./runtime.js";
|
|
60
64
|
|
|
61
65
|
const ORACLE_SUBMIT_PARAMS = Type.Object({
|
|
62
|
-
prompt: Type.String({ description: "Prompt text to send to ChatGPT web." }),
|
|
66
|
+
prompt: Type.String({ description: "Prompt text to send to ChatGPT or Grok web." }),
|
|
63
67
|
files: Type.Array(Type.String({
|
|
64
68
|
description: "Project-relative file or directory path to include in the archive.",
|
|
65
69
|
minLength: 1,
|
|
@@ -68,16 +72,35 @@ const ORACLE_SUBMIT_PARAMS = Type.Object({
|
|
|
68
72
|
description: "Exact project-relative files/directories to include in the oracle archive.",
|
|
69
73
|
minItems: 1,
|
|
70
74
|
}),
|
|
75
|
+
provider: Type.Optional(
|
|
76
|
+
Type.String({
|
|
77
|
+
description: `Oracle web provider. Omit to use the configured default provider. Supported providers: ${ORACLE_PROVIDERS.join(", ")}. Use grok when the user asks to oracle to Grok. Grok archives are capped at 200 MiB.`,
|
|
78
|
+
}),
|
|
79
|
+
),
|
|
71
80
|
preset: Type.Optional(
|
|
72
81
|
Type.String({
|
|
73
82
|
description:
|
|
74
83
|
`ChatGPT model preset. Omit to use the configured default preset. Canonical ids: ${ORACLE_SUBMIT_PRESET_IDS.join(", ")}. ` +
|
|
75
|
-
"Matching human-readable preset labels and common hyphen/space variants are normalized automatically.",
|
|
84
|
+
"Matching human-readable preset labels and common hyphen/space variants are normalized automatically. Do not pass preset when provider is grok.",
|
|
85
|
+
}),
|
|
86
|
+
),
|
|
87
|
+
mode: Type.Optional(
|
|
88
|
+
Type.String({
|
|
89
|
+
description: "Provider mode. For Grok, only heavy is currently supported. Omit to use the configured default mode.",
|
|
76
90
|
}),
|
|
77
91
|
),
|
|
78
92
|
followUpJobId: Type.Optional(Type.String({ description: "Earlier oracle job id whose chat thread should be continued." })),
|
|
79
93
|
});
|
|
80
94
|
|
|
95
|
+
const ORACLE_PREFLIGHT_PARAMS = Type.Object({
|
|
96
|
+
provider: Type.Optional(Type.String({ description: `Provider readiness to check. Omit to use the configured default provider. Supported providers: ${ORACLE_PROVIDERS.join(", ")}.` })),
|
|
97
|
+
followUpJobId: Type.Optional(Type.String({ description: "Earlier oracle job id whose provider/thread readiness should be checked." })),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const ORACLE_AUTH_PARAMS = Type.Object({
|
|
101
|
+
provider: Type.Optional(Type.String({ description: `Provider auth seed to refresh. Omit to use the configured default provider. Supported providers: ${ORACLE_PROVIDERS.join(", ")}.` })),
|
|
102
|
+
});
|
|
103
|
+
|
|
81
104
|
const ORACLE_READ_PARAMS = Type.Object({
|
|
82
105
|
jobId: Type.String({ description: "Oracle job id." }),
|
|
83
106
|
});
|
|
@@ -86,7 +109,9 @@ const ORACLE_CANCEL_PARAMS = Type.Object({
|
|
|
86
109
|
jobId: Type.String({ description: "Oracle job id." }),
|
|
87
110
|
});
|
|
88
111
|
|
|
89
|
-
const
|
|
112
|
+
const CHATGPT_MAX_ARCHIVE_BYTES = 250 * 1024 * 1024;
|
|
113
|
+
const GROK_MAX_ARCHIVE_BYTES = 200 * 1024 * 1024;
|
|
114
|
+
const MAX_ARCHIVE_BYTES = CHATGPT_MAX_ARCHIVE_BYTES;
|
|
90
115
|
const MAX_QUEUED_JOBS_PER_ACTIVE_RUNTIME = 1;
|
|
91
116
|
const MAX_QUEUED_ARCHIVE_BYTES_PER_ACTIVE_RUNTIME = MAX_ARCHIVE_BYTES;
|
|
92
117
|
const ARCHIVE_COMMAND_TIMEOUT_MS = 120_000;
|
|
@@ -347,7 +372,7 @@ function formatArchiveOversizeError(args: {
|
|
|
347
372
|
const topLevel = summarizeTopLevelIncludedPaths(args.entrySizes);
|
|
348
373
|
const adaptiveCandidates = summarizeAdaptivePruneCandidates(args.entrySizes, args.adaptivePruneMinBytes).slice(0, 7);
|
|
349
374
|
return [
|
|
350
|
-
`Oracle archive exceeds
|
|
375
|
+
`Oracle archive exceeds provider upload limit (${formatBytes(args.maxBytes)}) after default exclusions${args.autoPrunedPrefixes.length > 0 ? " and automatic generic generated-output-dir pruning" : ""}.`,
|
|
351
376
|
`The local archive measured ${formatBytes(args.archiveBytes)} (${args.archiveBytes} bytes), so submission stopped before dispatch.`,
|
|
352
377
|
args.autoPrunedPrefixes.length > 0 ? "Automatically pruned generic generated-output paths before failing:" : undefined,
|
|
353
378
|
...args.autoPrunedPrefixes.map((entry) => `- ${formatDirectoryLabel(entry.relativePath)} — ${formatBytes(entry.bytes)}`),
|
|
@@ -497,7 +522,7 @@ export async function createArchiveForTesting(
|
|
|
497
522
|
}
|
|
498
523
|
|
|
499
524
|
const archiveBytes = await writeArchiveFile(cwd, expandedEntries, archivePath, listPath, { commandTimeoutMs: options?.commandTimeoutMs });
|
|
500
|
-
if (archiveBytes
|
|
525
|
+
if (archiveBytes <= maxBytes) {
|
|
501
526
|
return {
|
|
502
527
|
sha256: await sha256File(archivePath),
|
|
503
528
|
archiveBytes,
|
|
@@ -528,8 +553,29 @@ export async function createArchiveForTesting(
|
|
|
528
553
|
}
|
|
529
554
|
}
|
|
530
555
|
|
|
531
|
-
async function createArchive(cwd: string, files: string[], archivePath: string): Promise<ArchiveCreationResult> {
|
|
532
|
-
return createArchiveForTesting(cwd, files, archivePath);
|
|
556
|
+
async function createArchive(cwd: string, files: string[], archivePath: string, maxBytes = MAX_ARCHIVE_BYTES): Promise<ArchiveCreationResult> {
|
|
557
|
+
return createArchiveForTesting(cwd, files, archivePath, { maxBytes });
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function normalizeOracleProvider(value: unknown, fallback: OracleProvider, toolName = "oracle_submit"): OracleProvider {
|
|
561
|
+
if (value === undefined) return fallback;
|
|
562
|
+
if (typeof value !== "string") throw new Error(`${toolName} provider must be a string`);
|
|
563
|
+
const normalized = value.trim().toLowerCase();
|
|
564
|
+
if (normalized === "chatgpt" || normalized === "chat-gpt" || normalized === "openai") return "chatgpt";
|
|
565
|
+
if (normalized === "grok" || normalized === "xai" || normalized === "x.ai") return "grok";
|
|
566
|
+
throw new Error(`Unknown ${toolName} provider: ${value}. Use chatgpt or grok.`);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function normalizeGrokMode(value: unknown, fallback: "heavy"): "heavy" {
|
|
570
|
+
if (value === undefined) return fallback;
|
|
571
|
+
if (typeof value !== "string") throw new Error("oracle_submit mode must be a string");
|
|
572
|
+
const normalized = value.trim().toLowerCase();
|
|
573
|
+
if (normalized === "heavy" || normalized === "grok heavy" || normalized === "grok-heavy") return "heavy";
|
|
574
|
+
throw new Error(`Unknown Grok oracle mode: ${value}. Only heavy is currently supported.`);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function getProviderMaxArchiveBytes(provider: "chatgpt" | "grok"): number {
|
|
578
|
+
return provider === "grok" ? GROK_MAX_ARCHIVE_BYTES : CHATGPT_MAX_ARCHIVE_BYTES;
|
|
533
579
|
}
|
|
534
580
|
|
|
535
581
|
export interface QueuedArchivePressure {
|
|
@@ -590,6 +636,7 @@ function resolveFollowUp(previousJobId: string | undefined, cwd: string): {
|
|
|
590
636
|
followUpToJobId?: string;
|
|
591
637
|
chatUrl?: string;
|
|
592
638
|
conversationId?: string;
|
|
639
|
+
provider?: "chatgpt" | "grok";
|
|
593
640
|
} {
|
|
594
641
|
if (!previousJobId) return {};
|
|
595
642
|
const previous = readJob(previousJobId);
|
|
@@ -609,6 +656,7 @@ function resolveFollowUp(previousJobId: string | undefined, cwd: string): {
|
|
|
609
656
|
followUpToJobId: previous.id,
|
|
610
657
|
chatUrl: previous.chatUrl,
|
|
611
658
|
conversationId: previous.conversationId || parseConversationId(previous.chatUrl),
|
|
659
|
+
provider: previous.selection?.provider === "grok" ? "grok" : "chatgpt",
|
|
612
660
|
};
|
|
613
661
|
}
|
|
614
662
|
|
|
@@ -898,7 +946,7 @@ function buildOracleToolErrorDetails(toolName: OracleToolErrorSource, error: unk
|
|
|
898
946
|
};
|
|
899
947
|
}
|
|
900
948
|
|
|
901
|
-
if (toolName === "oracle_submit" && message.startsWith("Oracle archive exceeds ChatGPT upload limit")) {
|
|
949
|
+
if (toolName === "oracle_submit" && (message.startsWith("Oracle archive exceeds provider upload limit") || message.startsWith("Oracle archive exceeds ChatGPT upload limit"))) {
|
|
902
950
|
return {
|
|
903
951
|
code: "archive_too_large",
|
|
904
952
|
message,
|
|
@@ -937,6 +985,7 @@ function buildOracleToolErrorResult(
|
|
|
937
985
|
|
|
938
986
|
type OraclePreflightDetails = {
|
|
939
987
|
ready: boolean;
|
|
988
|
+
provider?: OracleProvider;
|
|
940
989
|
session: {
|
|
941
990
|
persisted: boolean;
|
|
942
991
|
sessionFile?: string;
|
|
@@ -951,25 +1000,32 @@ type OraclePreflightDetails = {
|
|
|
951
1000
|
error?: OracleToolErrorDetails;
|
|
952
1001
|
};
|
|
953
1002
|
|
|
1003
|
+
function formatOracleProviderLabel(provider: OracleProvider | undefined): string {
|
|
1004
|
+
if (provider === "grok") return "Grok";
|
|
1005
|
+
if (provider === "chatgpt") return "ChatGPT";
|
|
1006
|
+
return "configured provider";
|
|
1007
|
+
}
|
|
1008
|
+
|
|
954
1009
|
function formatOraclePreflightResponse(details: OraclePreflightDetails): string {
|
|
1010
|
+
const providerLabel = formatOracleProviderLabel(details.provider);
|
|
955
1011
|
if (details.ready) {
|
|
956
1012
|
return [
|
|
957
|
-
|
|
1013
|
+
`Oracle preflight ready for ${providerLabel}.`,
|
|
958
1014
|
details.session.sessionFile ? `Persisted session: ${details.session.sessionFile}` : undefined,
|
|
959
1015
|
details.auth.seedProfileDir ? `Auth seed profile: ${details.auth.seedProfileDir}` : undefined,
|
|
960
|
-
|
|
1016
|
+
`Preflight validates the persisted pi session, local oracle config, and ${providerLabel} auth seed created by oracle_auth.`,
|
|
961
1017
|
"You can continue with oracle context gathering and submission.",
|
|
962
1018
|
].filter(Boolean).join("\n");
|
|
963
1019
|
}
|
|
964
1020
|
|
|
965
1021
|
return [
|
|
966
1022
|
`Oracle preflight blocked: ${details.error?.message ?? "unknown blocker"}`,
|
|
967
|
-
|
|
1023
|
+
`Preflight checks the persisted pi session, local oracle config, and ${providerLabel} auth seed before any archive work starts.`,
|
|
968
1024
|
details.error?.suggestedNextStep ? `Suggested next step: ${details.error.suggestedNextStep}` : undefined,
|
|
969
1025
|
].filter(Boolean).join("\n");
|
|
970
1026
|
}
|
|
971
1027
|
|
|
972
|
-
async function runOraclePreflight(ctx: ExtensionContext): Promise<OraclePreflightDetails> {
|
|
1028
|
+
async function runOraclePreflight(ctx: ExtensionContext, params: { provider?: unknown; followUpJobId?: unknown } = {}): Promise<OraclePreflightDetails> {
|
|
973
1029
|
const sessionFile = getSessionFile(ctx);
|
|
974
1030
|
if (!hasPersistedSessionFile(sessionFile)) {
|
|
975
1031
|
return {
|
|
@@ -986,11 +1042,23 @@ async function runOraclePreflight(ctx: ExtensionContext): Promise<OraclePrefligh
|
|
|
986
1042
|
}
|
|
987
1043
|
|
|
988
1044
|
let config;
|
|
1045
|
+
let provider: OracleProvider | undefined;
|
|
989
1046
|
try {
|
|
990
|
-
|
|
1047
|
+
const followUpJobId = params.followUpJobId;
|
|
1048
|
+
if (followUpJobId !== undefined && typeof followUpJobId !== "string") {
|
|
1049
|
+
throw new Error("oracle_preflight followUpJobId must be a string");
|
|
1050
|
+
}
|
|
1051
|
+
const baseConfig = loadOracleConfig(ctx.cwd);
|
|
1052
|
+
const followUp = resolveFollowUp(followUpJobId, ctx.cwd);
|
|
1053
|
+
provider = normalizeOracleProvider(params.provider, followUp.provider ?? baseConfig.defaults.provider, "oracle_preflight");
|
|
1054
|
+
if (followUp.provider && provider !== followUp.provider) {
|
|
1055
|
+
throw new Error(`Follow-up job ${followUpJobId} uses provider ${followUp.provider}; cannot check it with ${provider}.`);
|
|
1056
|
+
}
|
|
1057
|
+
config = resolveOracleConfigForProvider(baseConfig, provider);
|
|
991
1058
|
} catch (error) {
|
|
992
1059
|
return {
|
|
993
1060
|
ready: false,
|
|
1061
|
+
provider,
|
|
994
1062
|
session: { persisted: true, sessionFile },
|
|
995
1063
|
config: { ready: false },
|
|
996
1064
|
auth: { ready: false },
|
|
@@ -1004,6 +1072,7 @@ async function runOraclePreflight(ctx: ExtensionContext): Promise<OraclePrefligh
|
|
|
1004
1072
|
const errorDetails = buildOracleToolErrorDetails("oracle_preflight", error, {});
|
|
1005
1073
|
return {
|
|
1006
1074
|
ready: false,
|
|
1075
|
+
provider,
|
|
1007
1076
|
session: { persisted: true, sessionFile },
|
|
1008
1077
|
config: { ready: true },
|
|
1009
1078
|
auth: {
|
|
@@ -1016,6 +1085,7 @@ async function runOraclePreflight(ctx: ExtensionContext): Promise<OraclePrefligh
|
|
|
1016
1085
|
|
|
1017
1086
|
return {
|
|
1018
1087
|
ready: true,
|
|
1088
|
+
provider,
|
|
1019
1089
|
session: { persisted: true, sessionFile },
|
|
1020
1090
|
config: { ready: true },
|
|
1021
1091
|
auth: {
|
|
@@ -1042,11 +1112,11 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1042
1112
|
description: "Check whether oracle is ready in this session before spending time gathering context or preparing a submission.",
|
|
1043
1113
|
promptSnippet: "Check oracle readiness before expensive /oracle preparation.",
|
|
1044
1114
|
promptGuidelines: [
|
|
1045
|
-
"Call oracle_preflight before doing expensive /oracle preparation. If ready is false, stop immediately and report the suggested next step instead of reading files or crafting archive inputs.",
|
|
1115
|
+
"Call oracle_preflight before doing expensive /oracle preparation. Pass provider='grok' when the user explicitly asks for Grok, or followUpJobId for same-thread follow-ups. If ready is false, stop immediately and report the suggested next step instead of reading files or crafting archive inputs.",
|
|
1046
1116
|
],
|
|
1047
|
-
parameters:
|
|
1048
|
-
async execute(_toolCallId,
|
|
1049
|
-
const details = await runOraclePreflight(ctx);
|
|
1117
|
+
parameters: ORACLE_PREFLIGHT_PARAMS,
|
|
1118
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1119
|
+
const details = await runOraclePreflight(ctx, params);
|
|
1050
1120
|
return {
|
|
1051
1121
|
content: [{ type: "text" as const, text: formatOraclePreflightResponse(details) }],
|
|
1052
1122
|
details,
|
|
@@ -1057,23 +1127,26 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1057
1127
|
pi.registerTool({
|
|
1058
1128
|
name: "oracle_auth",
|
|
1059
1129
|
label: "Oracle Auth",
|
|
1060
|
-
description: "Refresh the shared oracle auth seed profile by importing ChatGPT cookies from your configured local browser profile.",
|
|
1130
|
+
description: "Refresh the shared oracle auth seed profile by importing ChatGPT or Grok cookies from your configured local browser profile, based on the configured default provider.",
|
|
1061
1131
|
promptSnippet: "Refresh oracle auth before retrying a login-required oracle run.",
|
|
1062
1132
|
promptGuidelines: [
|
|
1063
|
-
"Call oracle_auth when an oracle run failed because ChatGPT login is required, the worker said to rerun /oracle-auth, or stale auth appears to be blocking submission execution.",
|
|
1133
|
+
"Call oracle_auth when an oracle run failed because ChatGPT or Grok login is required, the worker said to rerun /oracle-auth, or stale auth appears to be blocking submission execution. Pass provider='grok' when refreshing Grok auth.",
|
|
1064
1134
|
"At most once per user request, refresh auth and then retry the blocked oracle submission.",
|
|
1065
1135
|
"If oracle_auth itself fails, stop and report the failure instead of looping.",
|
|
1066
1136
|
],
|
|
1067
|
-
parameters:
|
|
1068
|
-
async execute(_toolCallId,
|
|
1137
|
+
parameters: ORACLE_AUTH_PARAMS,
|
|
1138
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1069
1139
|
try {
|
|
1070
1140
|
const projectCwd = getProjectId(ctx.cwd);
|
|
1071
|
-
const
|
|
1141
|
+
const baseConfig = loadOracleConfig(projectCwd);
|
|
1142
|
+
const provider = normalizeOracleProvider(params.provider, baseConfig.defaults.provider, "oracle_auth");
|
|
1143
|
+
const message = await runOracleAuthBootstrap(authWorkerPath, projectCwd, provider);
|
|
1072
1144
|
return {
|
|
1073
1145
|
content: [{ type: "text" as const, text: message }],
|
|
1074
1146
|
details: {
|
|
1075
1147
|
refreshed: true,
|
|
1076
|
-
|
|
1148
|
+
provider,
|
|
1149
|
+
authSeedProfileDir: resolveOracleConfigForProvider(baseConfig, provider).browser.authSeedProfileDir,
|
|
1077
1150
|
},
|
|
1078
1151
|
};
|
|
1079
1152
|
} catch (error) {
|
|
@@ -1086,13 +1159,13 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1086
1159
|
name: "oracle_submit",
|
|
1087
1160
|
label: "Oracle Submit",
|
|
1088
1161
|
description:
|
|
1089
|
-
"Dispatch a background ChatGPT web oracle job after gathering context. Always pass a prompt and exact project-relative archive inputs. " +
|
|
1090
|
-
"Optional ChatGPT model: set parameter `preset`, or omit it for configured defaults; canonical preset ids are listed in the README and ORACLE_SUBMIT_PRESETS registry, and matching labels are normalized at submit time.",
|
|
1091
|
-
promptSnippet: "Dispatch a background ChatGPT web oracle job after gathering repo context.",
|
|
1162
|
+
"Dispatch a background ChatGPT or Grok web oracle job after gathering context. Always pass a prompt and exact project-relative archive inputs. " +
|
|
1163
|
+
"Optional provider: set `provider` to `grok` when the user asks for Grok; Grok currently supports only Heavy. Optional ChatGPT model: set parameter `preset`, or omit it for configured defaults; canonical preset ids are listed in the README and ORACLE_SUBMIT_PRESETS registry, and matching labels are normalized at submit time.",
|
|
1164
|
+
promptSnippet: "Dispatch a background ChatGPT or Grok web oracle job after gathering repo context.",
|
|
1092
1165
|
promptGuidelines: [
|
|
1093
1166
|
"Gather context before calling oracle_submit.",
|
|
1094
|
-
"If the immediately preceding oracle run failed because ChatGPT login is required or the worker explicitly said to rerun /oracle-auth, call oracle_auth once before retrying the submission. Do not loop auth refreshes.",
|
|
1095
|
-
"Prefer context-rich archives up to the
|
|
1167
|
+
"If the immediately preceding oracle run failed because ChatGPT or Grok login is required or the worker explicitly said to rerun /oracle-auth, call oracle_auth once before retrying the submission; pass provider='grok' for Grok retries. Do not loop auth refreshes.",
|
|
1168
|
+
"Prefer context-rich archives up to the provider ceiling because more relevant surrounding context is usually better than less: 250 MB for ChatGPT and 200 MiB for Grok.",
|
|
1096
1169
|
"By default, archive the whole repo by passing '.' for broad or unclear requests; default archive exclusions apply automatically, including common bulky outputs and obvious credentials/private data like .env files, key material, credential dotfiles, local database files, and nested secrets directories anywhere in the repo.",
|
|
1097
1170
|
"For narrower asks, still include nearby tests, docs, configs, and adjacent modules when they may improve answer quality. Only narrow aggressively when the user explicitly asks, privacy/sensitivity requires it, or size pressure forces it.",
|
|
1098
1171
|
"Do not default to a one-file archive for a single function, file, or stack trace if the relevant surrounding context still fits comfortably within the limit.",
|
|
@@ -1104,21 +1177,31 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1104
1177
|
"For any other submit-time error, stop and report the error instead of retrying automatically.",
|
|
1105
1178
|
"If oracle_submit returns a queued job instead of an immediately dispatched one, treat that as success and stop exactly the same way.",
|
|
1106
1179
|
"After a successful or queued oracle_submit, stop; do not continue the task while the oracle job is running. If oracle_submit failed with retryable archive_too_large, narrow the archive and retry first.",
|
|
1107
|
-
"
|
|
1180
|
+
"For ChatGPT, use `preset` as the only model-selection parameter on oracle_submit. " +
|
|
1108
1181
|
`Canonical ids: ${ORACLE_SUBMIT_PRESET_IDS.join(", ")}. ` +
|
|
1109
|
-
"matching human-readable preset labels are normalized automatically. Omit preset to use the configured default.",
|
|
1182
|
+
"matching human-readable preset labels are normalized automatically. Omit preset to use the configured default. For Grok, pass provider='grok' and omit preset; only Heavy is supported today.",
|
|
1110
1183
|
],
|
|
1111
1184
|
parameters: ORACLE_SUBMIT_PARAMS,
|
|
1112
1185
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1113
1186
|
try {
|
|
1114
1187
|
const projectCwd = getProjectId(ctx.cwd);
|
|
1115
|
-
const
|
|
1188
|
+
const baseConfig = loadOracleConfig(projectCwd);
|
|
1116
1189
|
const originSessionFile = requirePersistedSessionFile(getSessionFile(ctx), "submit oracle jobs");
|
|
1117
1190
|
const projectId = getProjectId(projectCwd);
|
|
1118
1191
|
const sessionId = getSessionId(originSessionFile, projectId);
|
|
1119
|
-
const presetId = typeof params.preset === "string" ? coerceOracleSubmitPresetId(params.preset) : config.defaults.preset;
|
|
1120
|
-
const selection = resolveOracleSubmitPreset(presetId);
|
|
1121
1192
|
const followUp = resolveFollowUp(params.followUpJobId, projectCwd);
|
|
1193
|
+
const provider = normalizeOracleProvider(params.provider, followUp.provider ?? baseConfig.defaults.provider, "oracle_submit");
|
|
1194
|
+
if (followUp.provider && provider !== followUp.provider) {
|
|
1195
|
+
throw new Error(`Follow-up job ${params.followUpJobId} uses provider ${followUp.provider}; cannot continue it with ${provider}.`);
|
|
1196
|
+
}
|
|
1197
|
+
if (provider === "grok" && typeof params.preset === "string") {
|
|
1198
|
+
throw new Error("oracle_submit preset is only valid for ChatGPT. For Grok, use provider='grok' and mode='heavy'.");
|
|
1199
|
+
}
|
|
1200
|
+
const selection = provider === "grok"
|
|
1201
|
+
? resolveOracleGrokMode(normalizeGrokMode(params.mode, baseConfig.defaults.grokMode))
|
|
1202
|
+
: resolveOracleSubmitPreset(typeof params.preset === "string" ? coerceOracleSubmitPresetId(params.preset) : baseConfig.defaults.preset);
|
|
1203
|
+
const config = resolveOracleConfigForProvider(baseConfig, provider);
|
|
1204
|
+
const targetChatUrl = followUp.chatUrl;
|
|
1122
1205
|
// Validate caller-specified archive paths before surfacing unrelated local setup failures such as a missing auth seed profile.
|
|
1123
1206
|
resolveArchiveInputs(projectCwd, params.files);
|
|
1124
1207
|
await assertOracleSubmitPrerequisites(config);
|
|
@@ -1144,7 +1227,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1144
1227
|
let spawnedWorker: Awaited<ReturnType<typeof spawnWorker>> | undefined;
|
|
1145
1228
|
|
|
1146
1229
|
try {
|
|
1147
|
-
archive = await createArchive(projectCwd, params.files, tempArchivePath);
|
|
1230
|
+
archive = await createArchive(projectCwd, params.files, tempArchivePath, getProviderMaxArchiveBytes(selection.provider));
|
|
1148
1231
|
const currentArchive = archive;
|
|
1149
1232
|
await withLock("admission", "global", { jobId, processPid: process.pid }, async () => {
|
|
1150
1233
|
await promoteQueuedJobsWithinAdmissionLock({ workerPath, source: "oracle_submit" });
|
|
@@ -1184,7 +1267,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1184
1267
|
files: params.files,
|
|
1185
1268
|
selection,
|
|
1186
1269
|
followUpToJobId: followUp.followUpToJobId,
|
|
1187
|
-
chatUrl:
|
|
1270
|
+
chatUrl: targetChatUrl,
|
|
1188
1271
|
requestSource: "tool",
|
|
1189
1272
|
},
|
|
1190
1273
|
projectCwd,
|
|
@@ -1227,7 +1310,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1227
1310
|
files: params.files,
|
|
1228
1311
|
selection,
|
|
1229
1312
|
followUpToJobId: followUp.followUpToJobId,
|
|
1230
|
-
chatUrl:
|
|
1313
|
+
chatUrl: targetChatUrl,
|
|
1231
1314
|
requestSource: "tool",
|
|
1232
1315
|
},
|
|
1233
1316
|
projectCwd,
|
|
@@ -1369,6 +1452,8 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1369
1452
|
name: "oracle_read",
|
|
1370
1453
|
label: "Oracle Read",
|
|
1371
1454
|
description: "Read the status and outputs of a previously dispatched oracle job.",
|
|
1455
|
+
promptSnippet: "Read oracle job status, queue position, artifacts, and response preview by job id.",
|
|
1456
|
+
promptGuidelines: ["Use oracle_read when the user asks for the status, output, or artifacts of a previously submitted oracle job."],
|
|
1372
1457
|
parameters: ORACLE_READ_PARAMS,
|
|
1373
1458
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1374
1459
|
try {
|
|
@@ -1427,6 +1512,8 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1427
1512
|
name: "oracle_cancel",
|
|
1428
1513
|
label: "Oracle Cancel",
|
|
1429
1514
|
description: "Cancel a queued or active oracle job.",
|
|
1515
|
+
promptSnippet: "Cancel a queued or active oracle background job by job id.",
|
|
1516
|
+
promptGuidelines: ["Use oracle_cancel only when the user explicitly asks to stop a queued or active oracle job."],
|
|
1430
1517
|
parameters: ORACLE_CANCEL_PARAMS,
|
|
1431
1518
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1432
1519
|
try {
|
|
@@ -51,13 +51,17 @@ function formatAutoPrunedArchiveMessage(autoPrunedPrefixes) {
|
|
|
51
51
|
* @returns {string | undefined}
|
|
52
52
|
*/
|
|
53
53
|
function formatOracleSelection(selection) {
|
|
54
|
-
if (!selection?.
|
|
54
|
+
if (!selection?.modelFamily) return undefined;
|
|
55
55
|
const details = [
|
|
56
|
+
selection.provider ? `provider=${selection.provider}` : undefined,
|
|
56
57
|
`family=${selection.modelFamily}`,
|
|
58
|
+
selection.mode ? `mode=${selection.mode}` : undefined,
|
|
57
59
|
selection.effort ? `effort=${selection.effort}` : undefined,
|
|
58
60
|
selection.autoSwitchToThinking === true ? "auto-switch-to-thinking=true" : undefined,
|
|
59
61
|
].filter(Boolean).join(", ");
|
|
60
|
-
return `Model preset: ${selection.preset}${details ? ` (${details})` : ""}`;
|
|
62
|
+
if (selection.preset) return `Model preset: ${selection.preset}${details ? ` (${details})` : ""}`;
|
|
63
|
+
if (selection.provider === "grok") return `Provider model: Grok${selection.mode ? ` ${selection.mode}` : ""}${details ? ` (${details})` : ""}`;
|
|
64
|
+
return `Model selection: ${details}`;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
const ACTIVE_SUMMARY_STATUSES = new Set(["preparing", "submitted", "waiting"]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Purpose: Bootstrap isolated oracle browser auth by importing real Chromium-family cookies and validating
|
|
1
|
+
// Purpose: Bootstrap isolated oracle browser auth by importing real Chromium-family cookies and validating provider session readiness.
|
|
2
2
|
// Responsibilities: Copy/import cookies, classify auth pages, drive lightweight account-selection flows, and persist diagnostics for auth failures.
|
|
3
3
|
// Scope: Auth bootstrap worker only; long-running oracle job execution stays in run-job.mjs and shared lifecycle/state helpers stay elsewhere.
|
|
4
4
|
// Usage: Spawned by /oracle-auth to prepare the shared auth seed profile used by future oracle jobs.
|
|
@@ -51,6 +51,7 @@ const CHATGPT_COOKIE_ORIGINS = [
|
|
|
51
51
|
"https://sentinel.openai.com",
|
|
52
52
|
"https://ws.chatgpt.com",
|
|
53
53
|
];
|
|
54
|
+
const GROK_COOKIE_ORIGINS = ["https://grok.com", "https://x.ai", "https://x.com"];
|
|
54
55
|
let DIAGNOSTICS_DIR;
|
|
55
56
|
let LOG_PATH = "(oracle-auth log path unavailable)";
|
|
56
57
|
let URL_PATH = "(oracle-auth url path unavailable)";
|
|
@@ -75,6 +76,7 @@ function readPositiveIntEnv(name, fallback) {
|
|
|
75
76
|
const AGENT_BROWSER_COMMAND_TIMEOUT_MS = readPositiveIntEnv("PI_ORACLE_AUTH_AGENT_BROWSER_TIMEOUT_MS", 30_000);
|
|
76
77
|
const AGENT_BROWSER_CLOSE_TIMEOUT_MS = readPositiveIntEnv("PI_ORACLE_AUTH_CLOSE_TIMEOUT_MS", 10_000);
|
|
77
78
|
const AGENT_BROWSER_KILL_GRACE_MS = readPositiveIntEnv("PI_ORACLE_AUTH_KILL_GRACE_MS", 2_000);
|
|
79
|
+
const COOKIE_READ_TIMEOUT_MS = readPositiveIntEnv("PI_ORACLE_AUTH_COOKIE_READ_TIMEOUT_MS", 30_000);
|
|
78
80
|
|
|
79
81
|
let runtimeProfileDir = config.browser.authSeedProfileDir;
|
|
80
82
|
|
|
@@ -455,8 +457,21 @@ function stripQuery(url) {
|
|
|
455
457
|
}
|
|
456
458
|
}
|
|
457
459
|
|
|
460
|
+
function preferredProvider() {
|
|
461
|
+
return config?.defaults?.provider === "grok" ? "grok" : "chatgpt";
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function providerChatUrl() {
|
|
465
|
+
return preferredProvider() === "grok" ? "https://grok.com/" : config.browser.chatUrl;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function providerName() {
|
|
469
|
+
return preferredProvider() === "grok" ? "Grok" : "ChatGPT";
|
|
470
|
+
}
|
|
471
|
+
|
|
458
472
|
function cookieOrigins() {
|
|
459
|
-
|
|
473
|
+
const providerOrigins = preferredProvider() === "grok" ? GROK_COOKIE_ORIGINS : CHATGPT_COOKIE_ORIGINS;
|
|
474
|
+
return Array.from(new Set([stripQuery(providerChatUrl()), ...providerOrigins]));
|
|
460
475
|
}
|
|
461
476
|
|
|
462
477
|
function cookieSource() {
|
|
@@ -513,13 +528,13 @@ function formatAuthFailureGuidance(error) {
|
|
|
513
528
|
if (usesConfiguredChromiumCookieSource()) {
|
|
514
529
|
lines.push(
|
|
515
530
|
"- the configured cookie DB is stale or from the wrong browser profile",
|
|
516
|
-
|
|
531
|
+
`- ${providerName()} is logged out in that browser profile`,
|
|
517
532
|
"- auth.chromiumKeychain does not match the browser safe-storage item for that cookie DB",
|
|
518
533
|
"- the target browser was still running while /oracle-auth read its Cookies DB",
|
|
519
534
|
"",
|
|
520
535
|
"Next:",
|
|
521
536
|
"1. Open the configured browser profile.",
|
|
522
|
-
|
|
537
|
+
`2. Confirm ${providerName()} works there.`,
|
|
523
538
|
"3. Quit the browser fully.",
|
|
524
539
|
"4. Confirm auth.chromeCookiePath points at that profile's Cookies DB.",
|
|
525
540
|
"5. Confirm auth.chromiumKeychain.services names the browser's safe-storage Keychain service.",
|
|
@@ -527,13 +542,13 @@ function formatAuthFailureGuidance(error) {
|
|
|
527
542
|
);
|
|
528
543
|
} else {
|
|
529
544
|
lines.push(
|
|
530
|
-
|
|
545
|
+
`- ${providerName()} is logged out in the configured local browser profile`,
|
|
531
546
|
"- auth.chromeProfile or auth.chromeCookiePath points at the wrong profile",
|
|
532
547
|
"- the profile cookie store is stale or unreadable",
|
|
533
548
|
"",
|
|
534
549
|
"Next:",
|
|
535
550
|
"1. Open the configured local browser profile.",
|
|
536
|
-
|
|
551
|
+
`2. Confirm ${providerName()} works there.`,
|
|
537
552
|
"3. Quit the browser fully.",
|
|
538
553
|
"4. Re-run /oracle-auth.",
|
|
539
554
|
);
|
|
@@ -561,29 +576,29 @@ async function readRawSourceCookies() {
|
|
|
561
576
|
keychain: config.auth.chromiumKeychain,
|
|
562
577
|
origins: cookieOrigins(),
|
|
563
578
|
profile: config.auth.chromeProfile,
|
|
564
|
-
timeoutMs:
|
|
579
|
+
timeoutMs: COOKIE_READ_TIMEOUT_MS,
|
|
565
580
|
});
|
|
566
581
|
}
|
|
567
582
|
|
|
568
583
|
return await getCookies({
|
|
569
|
-
url:
|
|
584
|
+
url: providerChatUrl(),
|
|
570
585
|
origins: cookieOrigins(),
|
|
571
586
|
browsers: ["chrome"],
|
|
572
587
|
mode: "merge",
|
|
573
588
|
chromeProfile: cookieSource(),
|
|
574
|
-
timeoutMs:
|
|
589
|
+
timeoutMs: COOKIE_READ_TIMEOUT_MS,
|
|
575
590
|
});
|
|
576
591
|
}
|
|
577
592
|
|
|
578
593
|
async function readSourceCookies() {
|
|
579
|
-
await log(`Reading
|
|
594
|
+
await log(`Reading ${providerName()} cookies from ${cookieSourceLabel()}`);
|
|
580
595
|
const { cookies, warnings } = await readRawSourceCookies();
|
|
581
596
|
|
|
582
597
|
if (warnings.length) {
|
|
583
598
|
await log(`Cookie source warnings: ${warnings.join(" | ")}`);
|
|
584
599
|
}
|
|
585
600
|
|
|
586
|
-
const filtered = filterImportableAuthCookies(cookies,
|
|
601
|
+
const filtered = filterImportableAuthCookies(cookies, providerChatUrl());
|
|
587
602
|
let normalizedCookies = filtered.cookies;
|
|
588
603
|
await log(
|
|
589
604
|
`Read ${normalizedCookies.length} filtered auth cookies: ${normalizedCookies.map((cookie) => `${cookie.name}@${cookie.domain}`).join(", ")}`,
|
|
@@ -599,6 +614,13 @@ async function readSourceCookies() {
|
|
|
599
614
|
const hasAccountCookie = normalizedCookies.some((cookie) => cookie.name === "_account");
|
|
600
615
|
await log(`Cookie presence: sessionToken=${hasSessionToken} account=${hasAccountCookie}`);
|
|
601
616
|
|
|
617
|
+
if (preferredProvider() === "grok") {
|
|
618
|
+
if (normalizedCookies.length === 0) {
|
|
619
|
+
throw new Error(`No Grok cookies were found in ${cookieSourceLabel()}. Make sure Grok is logged into that browser profile. ${authConfigRemediation()}`);
|
|
620
|
+
}
|
|
621
|
+
return normalizedCookies;
|
|
622
|
+
}
|
|
623
|
+
|
|
602
624
|
if (!hasSessionToken) {
|
|
603
625
|
throw new Error(
|
|
604
626
|
`No ChatGPT session-token cookies were found in ${cookieSourceLabel()}. Make sure ChatGPT is logged into that browser profile. ${authConfigRemediation()}`,
|
|
@@ -626,7 +648,7 @@ function cookieSetArgs(cookie) {
|
|
|
626
648
|
}
|
|
627
649
|
|
|
628
650
|
async function seedCookiesIntoTarget(cookies) {
|
|
629
|
-
await log(
|
|
651
|
+
await log(`Clearing isolated browser cookies before seeding fresh ${providerName()} cookies`);
|
|
630
652
|
await targetCommand("cookies", "clear", { logLabel: "cookies clear" });
|
|
631
653
|
|
|
632
654
|
let applied = 0;
|
|
@@ -764,6 +786,7 @@ async function captureDiagnostics(reason) {
|
|
|
764
786
|
}
|
|
765
787
|
|
|
766
788
|
function classifyChatPage({ url, snapshot, body, probe }) {
|
|
789
|
+
if (preferredProvider() === "grok") return classifyGrokAuthPage({ url, snapshot, body });
|
|
767
790
|
return classifyChatAuthPage({
|
|
768
791
|
url,
|
|
769
792
|
snapshot,
|
|
@@ -778,6 +801,31 @@ function classifyChatPage({ url, snapshot, body, probe }) {
|
|
|
778
801
|
});
|
|
779
802
|
}
|
|
780
803
|
|
|
804
|
+
function hasGrokLoginCta(text) {
|
|
805
|
+
const lines = String(text || "").split("\n").map((line) => line.trim()).filter(Boolean);
|
|
806
|
+
return lines.some((line) => {
|
|
807
|
+
const accessibleControl = line.match(/^-\s*(?:button|link|menuitem)\s+"([^"]+)"/i)?.[1]?.trim();
|
|
808
|
+
const label = accessibleControl || line;
|
|
809
|
+
return /^(?:sign in|log in|continue with x|continue with google|create account)$/i.test(label);
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function classifyGrokAuthPage({ url, snapshot, body }) {
|
|
814
|
+
const text = `${snapshot}\n${body}`;
|
|
815
|
+
if (/captcha|cloudflare|verify you are human|unusual activity|suspicious activity/i.test(text)) {
|
|
816
|
+
return { state: "challenge_blocking", message: "Grok is showing a challenge/verification page" };
|
|
817
|
+
}
|
|
818
|
+
if (/something went wrong|network error|try again later|rate limit/i.test(text)) {
|
|
819
|
+
return { state: "transient_outage_error", message: "Grok is showing a transient outage/error page" };
|
|
820
|
+
}
|
|
821
|
+
const onGrokOrigin = typeof url === "string" && url.startsWith("https://grok.com");
|
|
822
|
+
const hasComposer = snapshot.includes('button "Attach"') && (snapshot.includes('textbox "Ask Grok anything"') || snapshot.includes("contenteditable"));
|
|
823
|
+
if (onGrokOrigin && hasGrokLoginCta(text)) return { state: "login_required", message: `Grok login is required. Sign in to Grok in ${cookieSourceLabel()}.` };
|
|
824
|
+
if (onGrokOrigin && hasComposer) return { state: "authenticated_and_ready", message: "Grok is authenticated and ready." };
|
|
825
|
+
if (url && !onGrokOrigin) return { state: "login_required", message: "Grok redirected away from grok.com." };
|
|
826
|
+
return { state: "unknown", message: "Grok page is not ready yet." };
|
|
827
|
+
}
|
|
828
|
+
|
|
781
829
|
async function maybeSelectAccountIdentity(snapshot, probe) {
|
|
782
830
|
const candidates = buildAccountChooserCandidateLabels(probe?.name);
|
|
783
831
|
|
|
@@ -827,7 +875,7 @@ async function waitForImportedAuthReady() {
|
|
|
827
875
|
await writeFile(BODY_PATH, `${body}\n`, { mode: 0o600 }).catch(() => undefined);
|
|
828
876
|
const classification = classifyChatPage({ url, snapshot, body, probe });
|
|
829
877
|
await log(
|
|
830
|
-
`poll ${iteration}: url=${JSON.stringify(url)} probe=${JSON.stringify(probe)} classification=${classification.state} hasComposer=${snapshot.includes(`textbox \"${CHATGPT_LABELS.composer}\"`)} hasAddFiles=${snapshot.includes(`button \"${CHATGPT_LABELS.addFiles}\"`)}`,
|
|
878
|
+
`poll ${iteration}: url=${JSON.stringify(url)} probe=${JSON.stringify(probe)} classification=${classification.state} hasComposer=${preferredProvider() === "grok" ? snapshot.includes('Ask Grok anything') || snapshot.includes('contenteditable') : snapshot.includes(`textbox \"${CHATGPT_LABELS.composer}\"`)} hasAddFiles=${preferredProvider() === "grok" ? snapshot.includes('button \"Attach\"') : snapshot.includes(`button \"${CHATGPT_LABELS.addFiles}\"`)}`,
|
|
831
879
|
);
|
|
832
880
|
if (classification.state === "authenticated_and_ready") return classification;
|
|
833
881
|
if (classification.state === "auth_transitioning") {
|
|
@@ -880,7 +928,7 @@ async function waitForImportedAuthReady() {
|
|
|
880
928
|
await sleep(config.auth.pollMs);
|
|
881
929
|
}
|
|
882
930
|
await captureDiagnostics("timeout");
|
|
883
|
-
throw new Error(`Timed out verifying synced
|
|
931
|
+
throw new Error(`Timed out verifying synced ${providerName()} cookies in the isolated oracle profile. Logs: ${LOG_PATH}`);
|
|
884
932
|
}
|
|
885
933
|
|
|
886
934
|
async function run() {
|
|
@@ -901,7 +949,7 @@ async function run() {
|
|
|
901
949
|
await launchTargetBrowser();
|
|
902
950
|
const appliedCount = await seedCookiesIntoTarget(cookies);
|
|
903
951
|
await log(`Cookie seeding complete: applied=${appliedCount}`);
|
|
904
|
-
await openUrl(
|
|
952
|
+
await openUrl(providerChatUrl(), providerChatUrl());
|
|
905
953
|
const classification = await waitForImportedAuthReady();
|
|
906
954
|
await log(`Auth bootstrap success: ${classification.message}`);
|
|
907
955
|
await closeTargetBrowser();
|