pi-oracle 0.6.16 → 0.7.0
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 +30 -0
- package/README.md +197 -127
- 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 -34
- package/extensions/oracle/shared/job-observability-helpers.d.mts +8 -0
- package/extensions/oracle/shared/job-observability-helpers.mjs +19 -0
- 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 +20 -5
- package/extensions/oracle/worker/chatgpt-flow-helpers.mjs +2 -2
- package/extensions/oracle/worker/chatgpt-ui-helpers.mjs +17 -1
- package/extensions/oracle/worker/chromium-cookie-source.mjs +2 -2
- package/extensions/oracle/worker/run-job.mjs +260 -40
- package/package.json +3 -2
- package/prompts/oracle-followup.md +21 -19
- package/prompts/oracle.md +11 -9
|
@@ -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,23 +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,
|
|
1016
|
+
`Preflight validates the persisted pi session, local oracle config, and ${providerLabel} auth seed created by oracle_auth.`,
|
|
960
1017
|
"You can continue with oracle context gathering and submission.",
|
|
961
1018
|
].filter(Boolean).join("\n");
|
|
962
1019
|
}
|
|
963
1020
|
|
|
964
1021
|
return [
|
|
965
1022
|
`Oracle preflight blocked: ${details.error?.message ?? "unknown blocker"}`,
|
|
1023
|
+
`Preflight checks the persisted pi session, local oracle config, and ${providerLabel} auth seed before any archive work starts.`,
|
|
966
1024
|
details.error?.suggestedNextStep ? `Suggested next step: ${details.error.suggestedNextStep}` : undefined,
|
|
967
1025
|
].filter(Boolean).join("\n");
|
|
968
1026
|
}
|
|
969
1027
|
|
|
970
|
-
async function runOraclePreflight(ctx: ExtensionContext): Promise<OraclePreflightDetails> {
|
|
1028
|
+
async function runOraclePreflight(ctx: ExtensionContext, params: { provider?: unknown; followUpJobId?: unknown } = {}): Promise<OraclePreflightDetails> {
|
|
971
1029
|
const sessionFile = getSessionFile(ctx);
|
|
972
1030
|
if (!hasPersistedSessionFile(sessionFile)) {
|
|
973
1031
|
return {
|
|
@@ -984,11 +1042,23 @@ async function runOraclePreflight(ctx: ExtensionContext): Promise<OraclePrefligh
|
|
|
984
1042
|
}
|
|
985
1043
|
|
|
986
1044
|
let config;
|
|
1045
|
+
let provider: OracleProvider | undefined;
|
|
987
1046
|
try {
|
|
988
|
-
|
|
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);
|
|
989
1058
|
} catch (error) {
|
|
990
1059
|
return {
|
|
991
1060
|
ready: false,
|
|
1061
|
+
provider,
|
|
992
1062
|
session: { persisted: true, sessionFile },
|
|
993
1063
|
config: { ready: false },
|
|
994
1064
|
auth: { ready: false },
|
|
@@ -1002,6 +1072,7 @@ async function runOraclePreflight(ctx: ExtensionContext): Promise<OraclePrefligh
|
|
|
1002
1072
|
const errorDetails = buildOracleToolErrorDetails("oracle_preflight", error, {});
|
|
1003
1073
|
return {
|
|
1004
1074
|
ready: false,
|
|
1075
|
+
provider,
|
|
1005
1076
|
session: { persisted: true, sessionFile },
|
|
1006
1077
|
config: { ready: true },
|
|
1007
1078
|
auth: {
|
|
@@ -1014,6 +1085,7 @@ async function runOraclePreflight(ctx: ExtensionContext): Promise<OraclePrefligh
|
|
|
1014
1085
|
|
|
1015
1086
|
return {
|
|
1016
1087
|
ready: true,
|
|
1088
|
+
provider,
|
|
1017
1089
|
session: { persisted: true, sessionFile },
|
|
1018
1090
|
config: { ready: true },
|
|
1019
1091
|
auth: {
|
|
@@ -1040,11 +1112,11 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1040
1112
|
description: "Check whether oracle is ready in this session before spending time gathering context or preparing a submission.",
|
|
1041
1113
|
promptSnippet: "Check oracle readiness before expensive /oracle preparation.",
|
|
1042
1114
|
promptGuidelines: [
|
|
1043
|
-
"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.",
|
|
1044
1116
|
],
|
|
1045
|
-
parameters:
|
|
1046
|
-
async execute(_toolCallId,
|
|
1047
|
-
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);
|
|
1048
1120
|
return {
|
|
1049
1121
|
content: [{ type: "text" as const, text: formatOraclePreflightResponse(details) }],
|
|
1050
1122
|
details,
|
|
@@ -1055,23 +1127,26 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1055
1127
|
pi.registerTool({
|
|
1056
1128
|
name: "oracle_auth",
|
|
1057
1129
|
label: "Oracle Auth",
|
|
1058
|
-
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.",
|
|
1059
1131
|
promptSnippet: "Refresh oracle auth before retrying a login-required oracle run.",
|
|
1060
1132
|
promptGuidelines: [
|
|
1061
|
-
"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.",
|
|
1062
1134
|
"At most once per user request, refresh auth and then retry the blocked oracle submission.",
|
|
1063
1135
|
"If oracle_auth itself fails, stop and report the failure instead of looping.",
|
|
1064
1136
|
],
|
|
1065
|
-
parameters:
|
|
1066
|
-
async execute(_toolCallId,
|
|
1137
|
+
parameters: ORACLE_AUTH_PARAMS,
|
|
1138
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1067
1139
|
try {
|
|
1068
1140
|
const projectCwd = getProjectId(ctx.cwd);
|
|
1069
|
-
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);
|
|
1070
1144
|
return {
|
|
1071
1145
|
content: [{ type: "text" as const, text: message }],
|
|
1072
1146
|
details: {
|
|
1073
1147
|
refreshed: true,
|
|
1074
|
-
|
|
1148
|
+
provider,
|
|
1149
|
+
authSeedProfileDir: resolveOracleConfigForProvider(baseConfig, provider).browser.authSeedProfileDir,
|
|
1075
1150
|
},
|
|
1076
1151
|
};
|
|
1077
1152
|
} catch (error) {
|
|
@@ -1084,13 +1159,13 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1084
1159
|
name: "oracle_submit",
|
|
1085
1160
|
label: "Oracle Submit",
|
|
1086
1161
|
description:
|
|
1087
|
-
"Dispatch a background ChatGPT web oracle job after gathering context. Always pass a prompt and exact project-relative archive inputs. " +
|
|
1088
|
-
"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.",
|
|
1089
|
-
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.",
|
|
1090
1165
|
promptGuidelines: [
|
|
1091
1166
|
"Gather context before calling oracle_submit.",
|
|
1092
|
-
"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.",
|
|
1093
|
-
"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.",
|
|
1094
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.",
|
|
1095
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.",
|
|
1096
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.",
|
|
@@ -1102,21 +1177,31 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1102
1177
|
"For any other submit-time error, stop and report the error instead of retrying automatically.",
|
|
1103
1178
|
"If oracle_submit returns a queued job instead of an immediately dispatched one, treat that as success and stop exactly the same way.",
|
|
1104
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.",
|
|
1105
|
-
"
|
|
1180
|
+
"For ChatGPT, use `preset` as the only model-selection parameter on oracle_submit. " +
|
|
1106
1181
|
`Canonical ids: ${ORACLE_SUBMIT_PRESET_IDS.join(", ")}. ` +
|
|
1107
|
-
"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.",
|
|
1108
1183
|
],
|
|
1109
1184
|
parameters: ORACLE_SUBMIT_PARAMS,
|
|
1110
1185
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1111
1186
|
try {
|
|
1112
1187
|
const projectCwd = getProjectId(ctx.cwd);
|
|
1113
|
-
const
|
|
1188
|
+
const baseConfig = loadOracleConfig(projectCwd);
|
|
1114
1189
|
const originSessionFile = requirePersistedSessionFile(getSessionFile(ctx), "submit oracle jobs");
|
|
1115
1190
|
const projectId = getProjectId(projectCwd);
|
|
1116
1191
|
const sessionId = getSessionId(originSessionFile, projectId);
|
|
1117
|
-
const presetId = typeof params.preset === "string" ? coerceOracleSubmitPresetId(params.preset) : config.defaults.preset;
|
|
1118
|
-
const selection = resolveOracleSubmitPreset(presetId);
|
|
1119
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;
|
|
1120
1205
|
// Validate caller-specified archive paths before surfacing unrelated local setup failures such as a missing auth seed profile.
|
|
1121
1206
|
resolveArchiveInputs(projectCwd, params.files);
|
|
1122
1207
|
await assertOracleSubmitPrerequisites(config);
|
|
@@ -1142,7 +1227,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1142
1227
|
let spawnedWorker: Awaited<ReturnType<typeof spawnWorker>> | undefined;
|
|
1143
1228
|
|
|
1144
1229
|
try {
|
|
1145
|
-
archive = await createArchive(projectCwd, params.files, tempArchivePath);
|
|
1230
|
+
archive = await createArchive(projectCwd, params.files, tempArchivePath, getProviderMaxArchiveBytes(selection.provider));
|
|
1146
1231
|
const currentArchive = archive;
|
|
1147
1232
|
await withLock("admission", "global", { jobId, processPid: process.pid }, async () => {
|
|
1148
1233
|
await promoteQueuedJobsWithinAdmissionLock({ workerPath, source: "oracle_submit" });
|
|
@@ -1182,7 +1267,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1182
1267
|
files: params.files,
|
|
1183
1268
|
selection,
|
|
1184
1269
|
followUpToJobId: followUp.followUpToJobId,
|
|
1185
|
-
chatUrl:
|
|
1270
|
+
chatUrl: targetChatUrl,
|
|
1186
1271
|
requestSource: "tool",
|
|
1187
1272
|
},
|
|
1188
1273
|
projectCwd,
|
|
@@ -1225,7 +1310,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1225
1310
|
files: params.files,
|
|
1226
1311
|
selection,
|
|
1227
1312
|
followUpToJobId: followUp.followUpToJobId,
|
|
1228
|
-
chatUrl:
|
|
1313
|
+
chatUrl: targetChatUrl,
|
|
1229
1314
|
requestSource: "tool",
|
|
1230
1315
|
},
|
|
1231
1316
|
projectCwd,
|
|
@@ -1367,6 +1452,8 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1367
1452
|
name: "oracle_read",
|
|
1368
1453
|
label: "Oracle Read",
|
|
1369
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."],
|
|
1370
1457
|
parameters: ORACLE_READ_PARAMS,
|
|
1371
1458
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1372
1459
|
try {
|
|
@@ -1425,6 +1512,8 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1425
1512
|
name: "oracle_cancel",
|
|
1426
1513
|
label: "Oracle Cancel",
|
|
1427
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."],
|
|
1428
1517
|
parameters: ORACLE_CANCEL_PARAMS,
|
|
1429
1518
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
1430
1519
|
try {
|
|
@@ -12,6 +12,14 @@ export interface OracleJobSummaryLike {
|
|
|
12
12
|
heartbeatAt?: string;
|
|
13
13
|
projectId: string;
|
|
14
14
|
sessionId: string;
|
|
15
|
+
selection?: {
|
|
16
|
+
provider?: string;
|
|
17
|
+
preset?: string;
|
|
18
|
+
mode?: string;
|
|
19
|
+
modelFamily?: string;
|
|
20
|
+
effort?: string;
|
|
21
|
+
autoSwitchToThinking?: boolean;
|
|
22
|
+
};
|
|
15
23
|
followUpToJobId?: string;
|
|
16
24
|
chatUrl?: string;
|
|
17
25
|
conversationId?: string;
|
|
@@ -46,6 +46,24 @@ function formatAutoPrunedArchiveMessage(autoPrunedPrefixes) {
|
|
|
46
46
|
return `Archive auto-pruned generic generated-output-name dirs to fit size limit: ${autoPrunedPrefixes.map((entry) => `${entry.relativePath}/ (${formatBytes(entry.bytes)})`).join(", ")}`;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* @param {OracleJobSummaryLike["selection"]} selection
|
|
51
|
+
* @returns {string | undefined}
|
|
52
|
+
*/
|
|
53
|
+
function formatOracleSelection(selection) {
|
|
54
|
+
if (!selection?.modelFamily) return undefined;
|
|
55
|
+
const details = [
|
|
56
|
+
selection.provider ? `provider=${selection.provider}` : undefined,
|
|
57
|
+
`family=${selection.modelFamily}`,
|
|
58
|
+
selection.mode ? `mode=${selection.mode}` : undefined,
|
|
59
|
+
selection.effort ? `effort=${selection.effort}` : undefined,
|
|
60
|
+
selection.autoSwitchToThinking === true ? "auto-switch-to-thinking=true" : undefined,
|
|
61
|
+
].filter(Boolean).join(", ");
|
|
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}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
49
67
|
const ACTIVE_SUMMARY_STATUSES = new Set(["preparing", "submitted", "waiting"]);
|
|
50
68
|
const DEFAULT_ORACLE_HEARTBEAT_STALE_MS = 3 * 60 * 1000;
|
|
51
69
|
const DEFAULT_ACTIVE_JOB_POLL_HINT_SECONDS = 15;
|
|
@@ -221,6 +239,7 @@ export function formatOracleSubmitResponse(job, options) {
|
|
|
221
239
|
`${options.queued ? "Oracle job queued" : "Oracle job dispatched"}: ${job.id}`,
|
|
222
240
|
options.queued && options.queuePosition && options.queueDepth ? `Queue position: ${options.queuePosition} of ${options.queueDepth}` : undefined,
|
|
223
241
|
job.followUpToJobId ? `Follow-up to: ${job.followUpToJobId}` : undefined,
|
|
242
|
+
formatOracleSelection(job.selection),
|
|
224
243
|
`Prompt: ${job.promptPath}`,
|
|
225
244
|
`Archive: ${job.archivePath}`,
|
|
226
245
|
formatAutoPrunedArchiveMessage(options.autoPrunedPrefixes),
|
|
@@ -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();
|