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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Purpose: Define the allowlist/drop policy for importing
|
|
1
|
+
// Purpose: Define the allowlist/drop policy for importing provider auth cookies into the isolated oracle browser profile.
|
|
2
2
|
// Responsibilities: Recognize required auth cookies, drop noisy/irrelevant cookies, and normalize cookie import decisions.
|
|
3
3
|
// Scope: Pure cookie-policy logic only; reading cookies from Chrome and writing them into the isolated profile happen elsewhere.
|
|
4
4
|
// Usage: Imported by auth-bootstrap and sanity tests to keep cookie import behavior deterministic and reviewable.
|
|
@@ -14,6 +14,17 @@ const AUTH_COOKIE_NAME_PATTERNS = [
|
|
|
14
14
|
/^auth-session-minimized(?:-client-checksum)?$/,
|
|
15
15
|
/^(?:login_session|auth_provider|hydra_redirect|iss_context|rg_context)$/,
|
|
16
16
|
/^cf_clearance$/,
|
|
17
|
+
/^__(?:cf_bm|cflb)$/,
|
|
18
|
+
/^_cfuvid$/,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const GROK_AUTH_COOKIE_NAME_PATTERNS = [
|
|
22
|
+
/^sso(?:-rw)?$/,
|
|
23
|
+
/^auth_token$/,
|
|
24
|
+
/^ct0$/,
|
|
25
|
+
/^cf_clearance$/,
|
|
26
|
+
/^__(?:cf_bm|cflb)$/,
|
|
27
|
+
/^_cfuvid$/,
|
|
17
28
|
];
|
|
18
29
|
|
|
19
30
|
const DROPPED_COOKIE_NAME_PATTERNS = [
|
|
@@ -21,9 +32,6 @@ const DROPPED_COOKIE_NAME_PATTERNS = [
|
|
|
21
32
|
/^_uet/,
|
|
22
33
|
/^_rdt_uuid$/,
|
|
23
34
|
/^(?:marketing|analytics)_consent$/,
|
|
24
|
-
/^__cf_bm$/,
|
|
25
|
-
/^__cflb$/,
|
|
26
|
-
/^_cfuvid$/,
|
|
27
35
|
/^_dd_s$/,
|
|
28
36
|
/^g_state$/,
|
|
29
37
|
/^country$/,
|
|
@@ -41,6 +49,9 @@ const BASE_ALLOWED_COOKIE_HOSTS = new Set([
|
|
|
41
49
|
'sentinel.openai.com',
|
|
42
50
|
'atlas.openai.com',
|
|
43
51
|
'ws.chatgpt.com',
|
|
52
|
+
'grok.com',
|
|
53
|
+
'x.ai',
|
|
54
|
+
'x.com',
|
|
44
55
|
]);
|
|
45
56
|
|
|
46
57
|
function normalizeSameSite(value) {
|
|
@@ -101,6 +112,9 @@ export function normalizeImportedCookie(cookie, fallbackHost) {
|
|
|
101
112
|
export function classifyImportedCookie(cookie, chatUrl) {
|
|
102
113
|
if (matchesAny(DROPPED_COOKIE_NAME_PATTERNS, cookie.name)) return 'noise';
|
|
103
114
|
if (!isAllowedCookieDomain(cookie.domain, chatUrl)) return 'foreign-domain';
|
|
115
|
+
if (['grok.com', 'x.ai', 'x.com'].includes(cookie.domain)) {
|
|
116
|
+
return matchesAny(GROK_AUTH_COOKIE_NAME_PATTERNS, cookie.name) ? 'keep' : 'non-auth';
|
|
117
|
+
}
|
|
104
118
|
if (!matchesAny(AUTH_COOKIE_NAME_PATTERNS, cookie.name)) return 'non-auth';
|
|
105
119
|
return 'keep';
|
|
106
120
|
}
|
|
@@ -82,6 +82,9 @@ export function classifyChatAuthPage(args) {
|
|
|
82
82
|
/we detect suspicious activity/i,
|
|
83
83
|
];
|
|
84
84
|
if (challengePatterns.some((pattern) => pattern.test(text))) {
|
|
85
|
+
if (/verification successful|waiting for chatgpt\.com to respond/i.test(text)) {
|
|
86
|
+
return { state: "unknown", message: "ChatGPT verification is still settling." };
|
|
87
|
+
}
|
|
85
88
|
return {
|
|
86
89
|
state: "challenge_blocking",
|
|
87
90
|
message:
|
|
@@ -109,7 +112,9 @@ export function classifyChatAuthPage(args) {
|
|
|
109
112
|
return { state: "transient_outage_error", message: `ChatGPT is showing a transient outage/error page. Logs: ${args.logPath}` };
|
|
110
113
|
}
|
|
111
114
|
|
|
112
|
-
|
|
115
|
+
const probeHasAccountIdentity = args.probe?.bodyHasId === true || args.probe?.bodyHasEmail === true;
|
|
116
|
+
|
|
117
|
+
if (args.probe?.status === 401 || (args.probe?.status === 403 && (!onAllowedOrigin || !hasUsableComposer))) {
|
|
113
118
|
return {
|
|
114
119
|
state: "login_required",
|
|
115
120
|
message:
|
|
@@ -120,7 +125,7 @@ export function classifyChatAuthPage(args) {
|
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
if (args.probe?.onAuthPage) {
|
|
123
|
-
if (
|
|
128
|
+
if (probeHasAccountIdentity) {
|
|
124
129
|
return {
|
|
125
130
|
state: "auth_transitioning",
|
|
126
131
|
message:
|
|
@@ -137,6 +142,16 @@ export function classifyChatAuthPage(args) {
|
|
|
137
142
|
};
|
|
138
143
|
}
|
|
139
144
|
|
|
145
|
+
if (onAllowedOrigin && hasUsableComposer && args.probe?.domLoginCta && !probeHasAccountIdentity) {
|
|
146
|
+
return {
|
|
147
|
+
state: "login_required",
|
|
148
|
+
message:
|
|
149
|
+
`ChatGPT still shows public login controls after syncing cookies from ${args.cookieSourceLabel}. ` +
|
|
150
|
+
`The cookie DB may be stale, from the wrong browser profile, or for an account that is logged out. ` +
|
|
151
|
+
`Check auth.chromeProfile/auth.chromeCookiePath/auth.chromiumKeychain and inspect ${args.logPath}.`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
140
155
|
if (onAllowedOrigin && (args.probe?.status === 200 || args.probe?.status === 403) && hasUsableComposer) {
|
|
141
156
|
if (!args.probe?.domLoginCta) {
|
|
142
157
|
return {
|
|
@@ -145,12 +160,12 @@ export function classifyChatAuthPage(args) {
|
|
|
145
160
|
};
|
|
146
161
|
}
|
|
147
162
|
|
|
163
|
+
// The public logged-out composer case returned above, so a remaining visible login CTA here still has account-like probe data.
|
|
148
164
|
return {
|
|
149
165
|
state: "auth_transitioning",
|
|
150
166
|
message:
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
: `ChatGPT accepted cookies but is still hydrating/auth-selecting. Logs: ${args.logPath}`,
|
|
167
|
+
`ChatGPT backend probe returned account-like fields, but the shell still shows public login controls. ` +
|
|
168
|
+
`Trying to resolve the account shell. Logs: ${args.logPath}`,
|
|
154
169
|
};
|
|
155
170
|
}
|
|
156
171
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Purpose: Provide pure
|
|
1
|
+
// Purpose: Provide pure provider conversation-state helpers used by the oracle worker.
|
|
2
2
|
// Responsibilities: Slice assistant snapshot regions, normalize URLs, and track stable conversation URL observations.
|
|
3
3
|
// Scope: Pure worker flow logic only; browser I/O and polling loops stay in run-job.mjs.
|
|
4
4
|
// Usage: Imported by run-job.mjs and sanity tests to validate conversation-state heuristics without driving a browser.
|
|
@@ -53,7 +53,7 @@ export function stripUrlQueryAndHash(url) {
|
|
|
53
53
|
*/
|
|
54
54
|
export function isConversationPathUrl(url) {
|
|
55
55
|
try {
|
|
56
|
-
return /\/c\/[A-Za-z0-9-]+$/i.test(new URL(url).pathname);
|
|
56
|
+
return /\/(?:c|chat)\/[A-Za-z0-9-]+$/i.test(new URL(url).pathname);
|
|
57
57
|
} catch {
|
|
58
58
|
return false;
|
|
59
59
|
}
|
|
@@ -30,6 +30,7 @@ const THINKING_EFFORT_COMBOBOX_LABEL = "Thinking effort";
|
|
|
30
30
|
const PRO_THINKING_EFFORT_COMBOBOX_LABEL = "Pro thinking effort";
|
|
31
31
|
const EFFORT_LABELS = new Set(["Light", "Standard", "Extended", "Heavy"]);
|
|
32
32
|
const BARE_EFFORT_PATTERN = /^(light|standard|extended|heavy)(?:, click to remove)?$/i;
|
|
33
|
+
const INSTANT_CHIP_PATTERN = /^instant(?:, click to remove)?$/i;
|
|
33
34
|
const THINKING_CHIP_PATTERN = /^(?:(light|standard|extended|heavy)\s+)?thinking(?:, click to remove)?$/i;
|
|
34
35
|
const PRO_CHIP_PATTERN = /^(?:(light|standard|extended|heavy)\s+)?pro(?:, click to remove)?$/i;
|
|
35
36
|
const MODEL_FAMILY_CONTROL_KINDS = new Set(["button", "radio", "menuitemradio"]);
|
|
@@ -125,6 +126,12 @@ function parseComposerChipSelection(label) {
|
|
|
125
126
|
};
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
if (INSTANT_CHIP_PATTERN.test(normalized)) {
|
|
130
|
+
return {
|
|
131
|
+
modelFamily: /** @type {OracleUiModelFamily} */ ("instant"),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
128
135
|
const thinkingMatch = normalized.match(THINKING_CHIP_PATTERN);
|
|
129
136
|
if (thinkingMatch) {
|
|
130
137
|
return {
|
|
@@ -240,11 +247,20 @@ export function snapshotHasModelConfigurationUi(snapshot) {
|
|
|
240
247
|
.filter((family) => matchesModelFamilyLabel(entry.label, family)),
|
|
241
248
|
),
|
|
242
249
|
);
|
|
250
|
+
const visibleRadioFamilies = new Set(
|
|
251
|
+
entries
|
|
252
|
+
.filter((entry) => entry.kind === "radio" && typeof entry.label === "string")
|
|
253
|
+
.flatMap((entry) =>
|
|
254
|
+
/** @type {OracleUiModelFamily[]} */ (["instant", "thinking", "pro"])
|
|
255
|
+
.filter((family) => matchesModelFamilyLabel(entry.label, family)),
|
|
256
|
+
),
|
|
257
|
+
);
|
|
243
258
|
const hasCloseButton = entries.some((entry) => entry.kind === "button" && entry.label === "Close" && !entry.disabled);
|
|
259
|
+
const hasIntelligenceHeading = entries.some((entry) => entry.kind === "heading" && normalizeText(entry.label) === "Intelligence" && !entry.disabled);
|
|
244
260
|
const hasEffortCombobox = entries.some(
|
|
245
261
|
(entry) => entry.kind === "combobox" && EFFORT_LABELS.has(entry.value || "") && !entry.disabled,
|
|
246
262
|
);
|
|
247
|
-
return visibleFamilies.size >= 2 || hasCloseButton || hasEffortCombobox;
|
|
263
|
+
return visibleFamilies.size >= 2 || visibleRadioFamilies.size >= 2 || hasCloseButton || hasIntelligenceHeading || hasEffortCombobox;
|
|
248
264
|
}
|
|
249
265
|
|
|
250
266
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Purpose: Read
|
|
1
|
+
// Purpose: Read provider cookies from arbitrary macOS Chromium-family cookie stores when sweet-cookie's built-in browser list is too narrow.
|
|
2
2
|
// Responsibilities: Snapshot a Chromium Cookies SQLite DB, decrypt AES-CBC cookie values with a configured Keychain item, and return sweet-cookie-shaped cookie objects.
|
|
3
3
|
// Scope: macOS Chromium cookie extraction only; auth policy filtering and browser seeding stay in auth-bootstrap.mjs.
|
|
4
4
|
// Usage: auth-bootstrap.mjs uses this when auth.chromiumKeychain is configured alongside auth.chromeCookiePath.
|
|
@@ -117,7 +117,7 @@ function buildHostWhereClause(origins) {
|
|
|
117
117
|
try {
|
|
118
118
|
for (const domain of parentCookieDomains(new URL(origin).hostname)) domains.add(domain);
|
|
119
119
|
} catch {
|
|
120
|
-
// Ignore malformed origins; validated
|
|
120
|
+
// Ignore malformed origins; validated provider config supplies the real set.
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
if (domains.size === 0) return "0";
|