@steipete/oracle 0.20.3 → 0.21.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/dist/bin/oracle-cli.js +10 -5
- package/dist/docs-site/.nojekyll +0 -0
- package/dist/docs-site/CNAME +1 -0
- package/dist/docs-site/RELEASING.html +403 -0
- package/dist/docs-site/advisor.html +317 -0
- package/dist/docs-site/agents.html +375 -0
- package/dist/docs-site/anthropic.html +368 -0
- package/dist/docs-site/bridge.html +421 -0
- package/dist/docs-site/browser-mode.html +637 -0
- package/dist/docs-site/chromium-forks.html +355 -0
- package/dist/docs-site/cli-reference.html +347 -0
- package/dist/docs-site/configuration.html +464 -0
- package/dist/docs-site/favicon.svg +14 -0
- package/dist/docs-site/followup.html +375 -0
- package/dist/docs-site/gemini.html +388 -0
- package/dist/docs-site/grok.html +325 -0
- package/dist/docs-site/index.html +360 -0
- package/dist/docs-site/install.html +335 -0
- package/dist/docs-site/linux.html +321 -0
- package/dist/docs-site/llms.txt +44 -0
- package/dist/docs-site/manual-tests.html +621 -0
- package/dist/docs-site/mcp.html +412 -0
- package/dist/docs-site/multimodel.html +364 -0
- package/dist/docs-site/mythical-pro-agents.html +360 -0
- package/dist/docs-site/notifier.html +338 -0
- package/dist/docs-site/openai-endpoints.html +417 -0
- package/dist/docs-site/openrouter.html +344 -0
- package/dist/docs-site/quickstart.html +369 -0
- package/dist/docs-site/refactor/ux.html +321 -0
- package/dist/docs-site/sessions.html +397 -0
- package/dist/docs-site/social-card.png +0 -0
- package/dist/docs-site/social-card.svg +79 -0
- package/dist/docs-site/spec.html +363 -0
- package/dist/docs-site/testing.html +323 -0
- package/dist/docs-site/tui-debug.html +326 -0
- package/dist/docs-site/windows-work.html +348 -0
- package/dist/docs-site/windows.html +320 -0
- package/dist/src/browser/chatgptConversation.js +309 -0
- package/dist/src/browser/config.js +2 -0
- package/dist/src/browser/executor.js +9 -4
- package/dist/src/browser/index.js +78 -8
- package/dist/src/browser/provider.js +15 -1
- package/dist/src/browser/sessionRunner.js +3 -2
- package/dist/src/cli/browserConfig.js +1 -0
- package/dist/src/cli/browserDefaults.js +4 -0
- package/dist/src/cli/sessionRunner.js +1 -0
- package/dist/src/config.js +1 -0
- package/dist/src/gemini-web/browserSessionManager.js +15 -2
- package/dist/src/gemini-web/executor.js +1 -1
- package/dist/src/gemini-web/http.js +11 -2
- package/dist/src/remote/client.js +25 -9
- package/dist/src/remote/server.js +20 -5
- package/package.json +2 -1
|
@@ -58,6 +58,7 @@ export const DEFAULT_BROWSER_CONFIG = {
|
|
|
58
58
|
researchMode: "off",
|
|
59
59
|
archiveConversations: "auto",
|
|
60
60
|
resumeConversationUrl: null,
|
|
61
|
+
captureProviderNative: false,
|
|
61
62
|
};
|
|
62
63
|
export function resolveBrowserConfig(config) {
|
|
63
64
|
const debugPortEnv = parseDebugPort(process.env.ORACLE_BROWSER_PORT ?? process.env.ORACLE_BROWSER_DEBUG_PORT);
|
|
@@ -117,6 +118,7 @@ export function resolveBrowserConfig(config) {
|
|
|
117
118
|
researchMode,
|
|
118
119
|
archiveConversations,
|
|
119
120
|
resumeConversationUrl: config?.resumeConversationUrl ?? DEFAULT_BROWSER_CONFIG.resumeConversationUrl,
|
|
121
|
+
captureProviderNative: config?.captureProviderNative ?? DEFAULT_BROWSER_CONFIG.captureProviderNative,
|
|
120
122
|
manualLogin,
|
|
121
123
|
manualLoginProfileDir: manualLogin ? resolvedProfileDir : null,
|
|
122
124
|
manualLoginCookieSync: config?.manualLoginCookieSync ?? DEFAULT_BROWSER_CONFIG.manualLoginCookieSync,
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { resolveBrowserProvider } from "./provider.js";
|
|
2
|
-
export async function
|
|
2
|
+
export async function resolveBrowserExecutor(options, remote) {
|
|
3
3
|
const provider = resolveBrowserProvider(options.model);
|
|
4
|
+
if (!provider) {
|
|
5
|
+
throw new Error(`Unsupported browser model: ${options.model}. Use a GPT or Gemini model.`);
|
|
6
|
+
}
|
|
7
|
+
if (remote) {
|
|
8
|
+
const { createRemoteBrowserExecutor } = await import("../remote/client.js");
|
|
9
|
+
return createRemoteBrowserExecutor({ ...remote, runOptions: options });
|
|
10
|
+
}
|
|
4
11
|
if (provider === "gemini") {
|
|
5
12
|
const { createGeminiWebExecutor } = await import("../gemini-web/index.js");
|
|
6
13
|
return createGeminiWebExecutor({
|
|
@@ -13,7 +20,5 @@ export async function resolveLocalBrowserExecutor(options) {
|
|
|
13
20
|
allowModelFallback: options.geminiAllowModelFallback,
|
|
14
21
|
});
|
|
15
22
|
}
|
|
16
|
-
|
|
17
|
-
return (await import("../browserMode.js")).runBrowserMode;
|
|
18
|
-
throw new Error(`Unsupported browser model: ${options.model}. Use a GPT or Gemini model.`);
|
|
23
|
+
return (await import("../browserMode.js")).runBrowserMode;
|
|
19
24
|
}
|
|
@@ -16,6 +16,7 @@ import { INPUT_SELECTORS } from "./constants.js";
|
|
|
16
16
|
import { uploadAttachmentViaDataTransfer } from "./actions/remoteFileTransfer.js";
|
|
17
17
|
import { ensureThinkingTime } from "./actions/thinkingTime.js";
|
|
18
18
|
import { throwIfAssistantUiError } from "./actions/assistantResponse.js";
|
|
19
|
+
import { finalizeProviderNativeCapture, } from "./chatgptConversation.js";
|
|
19
20
|
import { startThinkingStatusMonitor } from "./actions/thinkingStatus.js";
|
|
20
21
|
import { classifyChatGptUiWarningText, collectChatGptUiWarnings, createAssistantTimeoutError, throwChatGptUiWarningIfPresent, } from "./uiWarnings.js";
|
|
21
22
|
import { activateDeepResearch, captureDeepResearchTargetKeys, waitForDeepResearchCompletion, waitForResearchPlanAutoConfirm, } from "./actions/deepResearch.js";
|
|
@@ -443,6 +444,31 @@ function formatBrowserLeaseDiagnostics(options) {
|
|
|
443
444
|
`launch=${options.launchDisposition ?? "unknown"}`,
|
|
444
445
|
].join("; ");
|
|
445
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* Provider-native capture, gated on explicit opt-in.
|
|
449
|
+
*
|
|
450
|
+
* Off by default because it costs two extra authenticated requests per run and
|
|
451
|
+
* only matters when a caller intends to treat the transcript as evidence rather
|
|
452
|
+
* than as an answer. When it is on and it fails, the run is unaffected: the
|
|
453
|
+
* summary records why, and nothing throws.
|
|
454
|
+
*/
|
|
455
|
+
async function runProviderNativeCapture(params) {
|
|
456
|
+
if (!params.config.captureProviderNative) {
|
|
457
|
+
return { artifacts: [] };
|
|
458
|
+
}
|
|
459
|
+
const conversationId = params.conversationUrl
|
|
460
|
+
? extractConversationIdFromUrl(params.conversationUrl)
|
|
461
|
+
: undefined;
|
|
462
|
+
return finalizeProviderNativeCapture({
|
|
463
|
+
Runtime: params.Runtime,
|
|
464
|
+
conversationId,
|
|
465
|
+
conversationUrl: params.conversationUrl,
|
|
466
|
+
sessionId: params.sessionId,
|
|
467
|
+
answerMarkdown: params.answerMarkdown,
|
|
468
|
+
answerMessageId: params.answerMessageId,
|
|
469
|
+
logger: params.logger,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
446
472
|
function buildSkippedModelSelectionEvidence(desiredModel, strategy) {
|
|
447
473
|
return {
|
|
448
474
|
requestedModel: desiredModel ?? null,
|
|
@@ -682,6 +708,7 @@ async function runBrowserModeInternal(options, cancellation) {
|
|
|
682
708
|
const startedAt = Date.now();
|
|
683
709
|
let answerText = "";
|
|
684
710
|
let answerMarkdown = "";
|
|
711
|
+
let answerMessageId;
|
|
685
712
|
let answerHtml = "";
|
|
686
713
|
let runStatus = "attempted";
|
|
687
714
|
let connectionClosedUnexpectedly = false;
|
|
@@ -1227,15 +1254,23 @@ async function runBrowserModeInternal(options, cancellation) {
|
|
|
1227
1254
|
conversationUrl: lastUrl,
|
|
1228
1255
|
logger,
|
|
1229
1256
|
}), logger);
|
|
1257
|
+
const providerCapture = await runProviderNativeCapture({
|
|
1258
|
+
Runtime,
|
|
1259
|
+
config,
|
|
1260
|
+
conversationUrl: lastUrl,
|
|
1261
|
+
sessionId: options.sessionId,
|
|
1262
|
+
answerMarkdown: researchResult.text,
|
|
1263
|
+
logger,
|
|
1264
|
+
});
|
|
1230
1265
|
const transcriptArtifact = await saveOptionalArtifact(() => saveBrowserTranscriptArtifact({
|
|
1231
1266
|
sessionId: options.sessionId,
|
|
1232
1267
|
prompt: promptText,
|
|
1233
1268
|
answerMarkdown: researchResult.text,
|
|
1234
1269
|
conversationUrl: lastUrl,
|
|
1235
|
-
artifacts: appendArtifacts(undefined, [reportArtifact]),
|
|
1270
|
+
artifacts: appendArtifacts(appendArtifacts(undefined, [reportArtifact]), providerCapture.artifacts),
|
|
1236
1271
|
logger,
|
|
1237
1272
|
}), logger);
|
|
1238
|
-
const savedArtifacts = appendArtifacts(undefined, [reportArtifact, transcriptArtifact]);
|
|
1273
|
+
const savedArtifacts = appendArtifacts(appendArtifacts(undefined, [reportArtifact, transcriptArtifact]), providerCapture.artifacts);
|
|
1239
1274
|
const archive = await maybeArchiveCompletedConversation({
|
|
1240
1275
|
Runtime,
|
|
1241
1276
|
logger,
|
|
@@ -1249,6 +1284,7 @@ async function runBrowserModeInternal(options, cancellation) {
|
|
|
1249
1284
|
answerMarkdown: researchResult.text,
|
|
1250
1285
|
answerHtml: researchResult.html,
|
|
1251
1286
|
artifacts: savedArtifacts,
|
|
1287
|
+
providerNativeCapture: providerCapture.summary,
|
|
1252
1288
|
archive,
|
|
1253
1289
|
modelSelection: modelSelectionEvidence,
|
|
1254
1290
|
thinkingSelection: thinkingSelectionEvidence,
|
|
@@ -1561,6 +1597,7 @@ async function runBrowserModeInternal(options, cancellation) {
|
|
|
1561
1597
|
turnAnswerMarkdown = bestText;
|
|
1562
1598
|
}
|
|
1563
1599
|
}
|
|
1600
|
+
answerMessageId = turnAnswer.meta.messageId ?? undefined;
|
|
1564
1601
|
return {
|
|
1565
1602
|
label,
|
|
1566
1603
|
answerText: turnAnswerText,
|
|
@@ -1664,15 +1701,25 @@ async function runBrowserModeInternal(options, cancellation) {
|
|
|
1664
1701
|
});
|
|
1665
1702
|
const savedImageArtifacts = appendArtifacts(undefined, imageArtifacts.savedImages);
|
|
1666
1703
|
const savedBrowserArtifacts = appendArtifacts(savedImageArtifacts, fileArtifacts.savedFiles);
|
|
1704
|
+
const providerCapture = await runProviderNativeCapture({
|
|
1705
|
+
Runtime,
|
|
1706
|
+
config,
|
|
1707
|
+
conversationUrl: lastUrl,
|
|
1708
|
+
sessionId: options.sessionId,
|
|
1709
|
+
answerMarkdown,
|
|
1710
|
+
answerMessageId,
|
|
1711
|
+
logger,
|
|
1712
|
+
});
|
|
1713
|
+
const browserArtifactsWithCapture = appendArtifacts(savedBrowserArtifacts, providerCapture.artifacts);
|
|
1667
1714
|
const transcriptArtifact = await saveOptionalArtifact(() => saveBrowserTranscriptArtifact({
|
|
1668
1715
|
sessionId: options.sessionId,
|
|
1669
1716
|
prompt: promptText,
|
|
1670
1717
|
answerMarkdown,
|
|
1671
1718
|
conversationUrl: lastUrl,
|
|
1672
|
-
artifacts:
|
|
1719
|
+
artifacts: browserArtifactsWithCapture,
|
|
1673
1720
|
logger,
|
|
1674
1721
|
}), logger);
|
|
1675
|
-
const savedArtifacts = appendArtifacts(
|
|
1722
|
+
const savedArtifacts = appendArtifacts(browserArtifactsWithCapture, [transcriptArtifact]);
|
|
1676
1723
|
const archive = await maybeArchiveCompletedConversation({
|
|
1677
1724
|
Runtime,
|
|
1678
1725
|
logger,
|
|
@@ -1692,6 +1739,7 @@ async function runBrowserModeInternal(options, cancellation) {
|
|
|
1692
1739
|
answerMarkdown,
|
|
1693
1740
|
answerHtml: answerHtml.length > 0 ? answerHtml : undefined,
|
|
1694
1741
|
artifacts: savedArtifacts,
|
|
1742
|
+
providerNativeCapture: providerCapture.summary,
|
|
1695
1743
|
generatedImages: imageArtifacts.generatedImages,
|
|
1696
1744
|
savedImages: imageArtifacts.savedImages,
|
|
1697
1745
|
downloadableFiles: fileArtifacts.files,
|
|
@@ -2233,6 +2281,7 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2233
2281
|
const startedAt = Date.now();
|
|
2234
2282
|
let answerText = "";
|
|
2235
2283
|
let answerMarkdown = "";
|
|
2284
|
+
let answerMessageId;
|
|
2236
2285
|
let answerHtml = "";
|
|
2237
2286
|
let connectionClosedUnexpectedly = false;
|
|
2238
2287
|
let runStatus = "attempted";
|
|
@@ -2552,15 +2601,23 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2552
2601
|
conversationUrl: lastUrl,
|
|
2553
2602
|
logger,
|
|
2554
2603
|
}), logger);
|
|
2604
|
+
const providerCapture = await runProviderNativeCapture({
|
|
2605
|
+
Runtime,
|
|
2606
|
+
config,
|
|
2607
|
+
conversationUrl: lastUrl,
|
|
2608
|
+
sessionId: options.sessionId,
|
|
2609
|
+
answerMarkdown: researchResult.text,
|
|
2610
|
+
logger,
|
|
2611
|
+
});
|
|
2555
2612
|
const transcriptArtifact = await saveOptionalArtifact(() => saveBrowserTranscriptArtifact({
|
|
2556
2613
|
sessionId: options.sessionId,
|
|
2557
2614
|
prompt: promptText,
|
|
2558
2615
|
answerMarkdown: researchResult.text,
|
|
2559
2616
|
conversationUrl: lastUrl,
|
|
2560
|
-
artifacts: appendArtifacts(undefined, [reportArtifact]),
|
|
2617
|
+
artifacts: appendArtifacts(appendArtifacts(undefined, [reportArtifact]), providerCapture.artifacts),
|
|
2561
2618
|
logger,
|
|
2562
2619
|
}), logger);
|
|
2563
|
-
const savedArtifacts = appendArtifacts(undefined, [reportArtifact, transcriptArtifact]);
|
|
2620
|
+
const savedArtifacts = appendArtifacts(appendArtifacts(undefined, [reportArtifact, transcriptArtifact]), providerCapture.artifacts);
|
|
2564
2621
|
const archive = await maybeArchiveCompletedConversation({
|
|
2565
2622
|
Runtime,
|
|
2566
2623
|
logger,
|
|
@@ -2575,6 +2632,7 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2575
2632
|
answerMarkdown: researchResult.text,
|
|
2576
2633
|
answerHtml: researchResult.html,
|
|
2577
2634
|
artifacts: savedArtifacts,
|
|
2635
|
+
providerNativeCapture: providerCapture.summary,
|
|
2578
2636
|
archive,
|
|
2579
2637
|
modelSelection: modelSelectionEvidence,
|
|
2580
2638
|
thinkingSelection: thinkingSelectionEvidence,
|
|
@@ -2852,6 +2910,7 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2852
2910
|
turnAnswerMarkdown = bestText;
|
|
2853
2911
|
}
|
|
2854
2912
|
}
|
|
2913
|
+
answerMessageId = turnAnswer.meta.messageId ?? undefined;
|
|
2855
2914
|
return {
|
|
2856
2915
|
label,
|
|
2857
2916
|
answerText: turnAnswerText,
|
|
@@ -2947,15 +3006,25 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2947
3006
|
});
|
|
2948
3007
|
const savedImageArtifacts = appendArtifacts(undefined, imageArtifacts.savedImages);
|
|
2949
3008
|
const savedBrowserArtifacts = appendArtifacts(savedImageArtifacts, fileArtifacts.savedFiles);
|
|
3009
|
+
const providerCapture = await runProviderNativeCapture({
|
|
3010
|
+
Runtime,
|
|
3011
|
+
config,
|
|
3012
|
+
conversationUrl: lastUrl,
|
|
3013
|
+
sessionId: options.sessionId,
|
|
3014
|
+
answerMarkdown,
|
|
3015
|
+
answerMessageId,
|
|
3016
|
+
logger,
|
|
3017
|
+
});
|
|
3018
|
+
const browserArtifactsWithCapture = appendArtifacts(savedBrowserArtifacts, providerCapture.artifacts);
|
|
2950
3019
|
const transcriptArtifact = await saveOptionalArtifact(() => saveBrowserTranscriptArtifact({
|
|
2951
3020
|
sessionId: options.sessionId,
|
|
2952
3021
|
prompt: promptText,
|
|
2953
3022
|
answerMarkdown,
|
|
2954
3023
|
conversationUrl: lastUrl,
|
|
2955
|
-
artifacts:
|
|
3024
|
+
artifacts: browserArtifactsWithCapture,
|
|
2956
3025
|
logger,
|
|
2957
3026
|
}), logger);
|
|
2958
|
-
const savedArtifacts = appendArtifacts(
|
|
3027
|
+
const savedArtifacts = appendArtifacts(browserArtifactsWithCapture, [transcriptArtifact]);
|
|
2959
3028
|
const archive = await maybeArchiveCompletedConversation({
|
|
2960
3029
|
Runtime,
|
|
2961
3030
|
logger,
|
|
@@ -2991,6 +3060,7 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2991
3060
|
submittedPromptHash,
|
|
2992
3061
|
ownedRecoveryTarget,
|
|
2993
3062
|
artifacts: savedArtifacts,
|
|
3063
|
+
providerNativeCapture: providerCapture.summary,
|
|
2994
3064
|
generatedImages: imageArtifacts.generatedImages,
|
|
2995
3065
|
savedImages: imageArtifacts.savedImages,
|
|
2996
3066
|
downloadableFiles: fileArtifacts.files,
|
|
@@ -8,4 +8,18 @@ export function resolveBrowserProvider(model) {
|
|
|
8
8
|
return "chatgpt";
|
|
9
9
|
return undefined;
|
|
10
10
|
}
|
|
11
|
-
export
|
|
11
|
+
export function resolveRemoteBrowserModel(model, desiredModel) {
|
|
12
|
+
if (model !== undefined) {
|
|
13
|
+
if (typeof model === "string" && resolveBrowserProvider(model))
|
|
14
|
+
return model;
|
|
15
|
+
throw new Error(`Unsupported browser model: ${String(model)}. Use a GPT or Gemini model.`);
|
|
16
|
+
}
|
|
17
|
+
if (typeof desiredModel === "string" && resolveBrowserProvider(desiredModel))
|
|
18
|
+
return desiredModel;
|
|
19
|
+
// Older ChatGPT clients send only a picker label, or omit the selection entirely.
|
|
20
|
+
if (desiredModel == null ||
|
|
21
|
+
(typeof desiredModel === "string" &&
|
|
22
|
+
/^(?:|latest|auto|pro|thinking|instant)(?:\s.*)?$/i.test(desiredModel.trim())))
|
|
23
|
+
return undefined;
|
|
24
|
+
throw new Error(`Unsupported browser model: ${String(desiredModel)}. Use a GPT or Gemini model.`);
|
|
25
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { formatTokenCount } from "../oracle/runUtils.js";
|
|
3
3
|
import { formatFinishLine } from "../oracle/finishLine.js";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveBrowserExecutor } from "./executor.js";
|
|
5
5
|
import { DEFAULT_BROWSER_CONFIG } from "./config.js";
|
|
6
6
|
import { redactBrowserConfigForDebugLog } from "./configLogging.js";
|
|
7
7
|
import { assembleBrowserPrompt, cleanupGeneratedBrowserBundles, materializeBrowserFallback, } from "./prompt.js";
|
|
@@ -67,7 +67,7 @@ export async function runBrowserSessionExecution({ runOptions, browserConfig, cw
|
|
|
67
67
|
if (signal?.aborted) {
|
|
68
68
|
throw new BrowserRunCancelledError();
|
|
69
69
|
}
|
|
70
|
-
const executeBrowser = deps.executeBrowser ?? (await
|
|
70
|
+
const executeBrowser = deps.executeBrowser ?? (await resolveBrowserExecutor(runOptions));
|
|
71
71
|
try {
|
|
72
72
|
promptArtifacts = await Promise.race([
|
|
73
73
|
assemblePrompt(runOptions, { cwd }).then(async (artifacts) => {
|
|
@@ -313,6 +313,7 @@ async function executeAssembledBrowserSession({ runOptions, browserConfig, log,
|
|
|
313
313
|
archive: browserResult.archive,
|
|
314
314
|
modelSelection,
|
|
315
315
|
thinkingSelection,
|
|
316
|
+
providerNativeCapture: browserResult.providerNativeCapture,
|
|
316
317
|
warnings,
|
|
317
318
|
answerText,
|
|
318
319
|
artifacts: savedArtifacts,
|
|
@@ -238,6 +238,7 @@ export async function buildBrowserConfig(options) {
|
|
|
238
238
|
? options.browserResearch
|
|
239
239
|
: "off",
|
|
240
240
|
archiveConversations: options.browserArchive,
|
|
241
|
+
captureProviderNative: options.browserCaptureProviderNative,
|
|
241
242
|
};
|
|
242
243
|
}
|
|
243
244
|
function assertBrowserModelAvailable(model, modelStrategy) {
|
|
@@ -111,6 +111,10 @@ export function applyBrowserDefaultsFromConfig(options, config, getSource) {
|
|
|
111
111
|
browser.thinkingTime !== undefined) {
|
|
112
112
|
options.browserThinkingTime = normalizeThinkingTimeLevel(browser.thinkingTime) ?? undefined;
|
|
113
113
|
}
|
|
114
|
+
if (isUnset("browserCaptureProviderNative") &&
|
|
115
|
+
typeof browser.captureProviderNative === "boolean") {
|
|
116
|
+
options.browserCaptureProviderNative = browser.captureProviderNative;
|
|
117
|
+
}
|
|
114
118
|
if (isUnset("browserResearch") && browser.researchMode !== undefined) {
|
|
115
119
|
options.browserResearch = browser.researchMode;
|
|
116
120
|
}
|
|
@@ -161,6 +161,7 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
161
161
|
archive: result.archive,
|
|
162
162
|
modelSelection: result.modelSelection,
|
|
163
163
|
thinkingSelection: result.thinkingSelection,
|
|
164
|
+
providerNativeCapture: result.providerNativeCapture,
|
|
164
165
|
warnings: browserWarnings.length > 0 ? browserWarnings : undefined,
|
|
165
166
|
},
|
|
166
167
|
artifacts: mergeArtifacts(sessionMeta.artifacts, mergeArtifacts(result.artifacts, outputArtifacts.artifacts)),
|
package/dist/src/config.js
CHANGED
|
@@ -131,6 +131,7 @@ function sanitizeProjectConfig(config) {
|
|
|
131
131
|
if (config.browser) {
|
|
132
132
|
sanitized.browser = {};
|
|
133
133
|
const browser = config.browser;
|
|
134
|
+
// Full-conversation retention is user-owned; never allow captureProviderNative here.
|
|
134
135
|
const allowedBrowserKeys = [
|
|
135
136
|
"attachRunning",
|
|
136
137
|
"timeoutMs",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import { mkdir } from "node:fs/promises";
|
|
4
|
-
import { launchChrome, connectWithNewTab, closeTab } from "../browser/chromeLifecycle.js";
|
|
4
|
+
import { launchChrome, connectWithNewTab, connectToRemoteChrome, closeTab, } from "../browser/chromeLifecycle.js";
|
|
5
|
+
import { resolveAttachRunningConnection } from "../browser/attachRunning.js";
|
|
5
6
|
import { resolveBrowserConfig } from "../browser/config.js";
|
|
6
7
|
import { readDevToolsPort, writeDevToolsActivePort, writeChromePid, cleanupStaleProfileState, verifyDevToolsReachable, } from "../browser/profileState.js";
|
|
7
8
|
export async function openGeminiBrowserSession(input) {
|
|
@@ -12,8 +13,20 @@ export async function openGeminiBrowserSession(input) {
|
|
|
12
13
|
keepBrowser: browserConfig?.keepBrowser ?? keepBrowserDefault,
|
|
13
14
|
});
|
|
14
15
|
const profileDir = resolvedConfig.manualLoginProfileDir ?? path.join(os.homedir(), ".oracle", "browser-profile");
|
|
15
|
-
await mkdir(profileDir, { recursive: true });
|
|
16
16
|
const keepBrowser = Boolean(resolvedConfig.keepBrowser);
|
|
17
|
+
if (resolvedConfig.attachRunning || resolvedConfig.remoteChrome) {
|
|
18
|
+
const logger = log ?? (() => { });
|
|
19
|
+
const endpoint = await resolveAttachRunningConnection(resolvedConfig, logger);
|
|
20
|
+
const connection = await connectToRemoteChrome(endpoint.host, endpoint.port, logger, "about:blank", endpoint.browserWSEndpoint, { approvalWaitMs: resolvedConfig.approvalWaitMs, fallbackToDefault: false });
|
|
21
|
+
return {
|
|
22
|
+
profileDir: endpoint.profileRoot ?? profileDir,
|
|
23
|
+
port: endpoint.port,
|
|
24
|
+
client: connection.client,
|
|
25
|
+
targetId: connection.targetId,
|
|
26
|
+
close: () => connection.close({ preserveTarget: keepBrowser }),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
await mkdir(profileDir, { recursive: true });
|
|
17
30
|
let port = await readDevToolsPort(profileDir);
|
|
18
31
|
let launchedChrome = null;
|
|
19
32
|
let chromeWasLaunched = false;
|
|
@@ -228,7 +228,7 @@ async function loadGeminiCookies(browserConfig, log, options) {
|
|
|
228
228
|
if (hasInlineRequired) {
|
|
229
229
|
return inlineResult;
|
|
230
230
|
}
|
|
231
|
-
const manualNoKeychain = Boolean(browserConfig?.manualLogin) || Boolean(options?.preferManualNoKeychain);
|
|
231
|
+
const manualNoKeychain = Boolean(browserConfig?.manualLogin || browserConfig?.attachRunning || browserConfig?.remoteChrome) || Boolean(options?.preferManualNoKeychain);
|
|
232
232
|
if (manualNoKeychain) {
|
|
233
233
|
log?.("[gemini-web] Using manual-login cookie extraction path (no keychain cookie read).");
|
|
234
234
|
const cdpResult = await loadGeminiCookiesFromCDP(browserConfig, log);
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
import { EnvHttpProxyAgent } from "undici";
|
|
2
|
+
// Gemini's /app policy/reporting headers exceed Node's default 16 KiB limit.
|
|
3
|
+
const GEMINI_MAX_HEADER_SIZE = 64 * 1024;
|
|
4
|
+
let geminiDispatcher;
|
|
5
|
+
export function createGeminiWebDispatcher(options = {}) {
|
|
6
|
+
return new EnvHttpProxyAgent({ ...options, maxHeaderSize: GEMINI_MAX_HEADER_SIZE });
|
|
7
|
+
}
|
|
1
8
|
export async function fetchGeminiWebResource(url, init = {}) {
|
|
2
9
|
try {
|
|
3
|
-
|
|
10
|
+
const dispatcher = init.dispatcher ?? (geminiDispatcher ??= createGeminiWebDispatcher());
|
|
11
|
+
const options = { ...init, dispatcher };
|
|
12
|
+
return await fetch(url, options);
|
|
4
13
|
}
|
|
5
14
|
catch (error) {
|
|
6
15
|
const cause = error instanceof Error ? error.cause : undefined;
|
|
@@ -8,7 +17,7 @@ export async function fetchGeminiWebResource(url, init = {}) {
|
|
|
8
17
|
typeof cause === "object" &&
|
|
9
18
|
"code" in cause &&
|
|
10
19
|
cause.code === "UND_ERR_HEADERS_OVERFLOW") {
|
|
11
|
-
throw new Error("Gemini response headers exceed
|
|
20
|
+
throw new Error("Gemini response headers exceed the HTTP transport's configured limit (Oracle's default is 64 KiB).", { cause: error });
|
|
12
21
|
}
|
|
13
22
|
throw error;
|
|
14
23
|
}
|
|
@@ -11,16 +11,18 @@ import { checkRemoteHealth } from "./health.js";
|
|
|
11
11
|
import { parseHostPort } from "../bridge/connection.js";
|
|
12
12
|
import { BrowserRunCancelledError } from "../oracle/errors.js";
|
|
13
13
|
import { resolveSiblingImagePath } from "../browser/chatgptImages.js";
|
|
14
|
-
import { resolveBrowserProvider,
|
|
15
|
-
export function createRemoteBrowserExecutor({ host, token }) {
|
|
14
|
+
import { resolveBrowserProvider, resolveRemoteBrowserModel } from "../browser/provider.js";
|
|
15
|
+
export function createRemoteBrowserExecutor({ host, token, runOptions }) {
|
|
16
16
|
// Return a drop-in replacement for runBrowserMode so the browser session runner can stay unchanged.
|
|
17
17
|
return async function remoteBrowserExecutor(options) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
const model = resolveRemoteBrowserModel(options.model ?? runOptions?.model, options.config?.desiredModel);
|
|
19
|
+
const gemini = resolveBrowserProvider(model) === "gemini";
|
|
20
|
+
if (gemini &&
|
|
21
|
+
(runOptions?.editImage ||
|
|
22
|
+
runOptions?.generateImage ||
|
|
23
|
+
options.generateImagePath ||
|
|
24
|
+
options.outputPath)) {
|
|
25
|
+
throw new Error("Remote Gemini image generation and editing are not supported; run these requests locally.");
|
|
24
26
|
}
|
|
25
27
|
if (options.config?.researchMode === "search") {
|
|
26
28
|
throw new Error("Web Search is a local browser pilot; --remote-host does not negotiate this capability yet. Use local Chrome or --browser-attach-running.");
|
|
@@ -46,8 +48,22 @@ export function createRemoteBrowserExecutor({ host, token }) {
|
|
|
46
48
|
prompt: options.prompt,
|
|
47
49
|
attachments: await serializeAttachments(options.attachments ?? []),
|
|
48
50
|
fallbackSubmission: await serializeFallback(options.fallbackSubmission, { host, token }),
|
|
49
|
-
browserConfig:
|
|
51
|
+
browserConfig: {
|
|
52
|
+
...options.config,
|
|
53
|
+
// Keep old hosts fail-closed for Gemini even when the picker label is unrelated.
|
|
54
|
+
...(gemini ? { desiredModel: model } : {}),
|
|
55
|
+
inlineCookies: null,
|
|
56
|
+
inlineCookiesSource: null,
|
|
57
|
+
},
|
|
50
58
|
options: {
|
|
59
|
+
model,
|
|
60
|
+
...(gemini
|
|
61
|
+
? {
|
|
62
|
+
youtube: runOptions?.youtube,
|
|
63
|
+
geminiShowThoughts: runOptions?.geminiShowThoughts,
|
|
64
|
+
geminiAllowModelFallback: runOptions?.geminiAllowModelFallback,
|
|
65
|
+
}
|
|
66
|
+
: {}),
|
|
51
67
|
heartbeatIntervalMs: options.heartbeatIntervalMs,
|
|
52
68
|
verbose: options.verbose,
|
|
53
69
|
sessionId: options.sessionId,
|
|
@@ -9,7 +9,7 @@ import { spawn, spawnSync } from "node:child_process";
|
|
|
9
9
|
import { mkdtemp, rm, mkdir, writeFile, stat, realpath } from "node:fs/promises";
|
|
10
10
|
import chalk from "chalk";
|
|
11
11
|
import { materializeStagedFallbackBundle } from "../browser/prompt.js";
|
|
12
|
-
import {
|
|
12
|
+
import { resolveBrowserExecutor } from "../browser/executor.js";
|
|
13
13
|
import { resolveBrowserConfig } from "../browser/config.js";
|
|
14
14
|
import { RunSlots } from "./runSlots.js";
|
|
15
15
|
export { RunSlots } from "./runSlots.js";
|
|
@@ -19,7 +19,7 @@ import { getCookies } from "@steipete/sweet-cookie";
|
|
|
19
19
|
import { CHATGPT_URL } from "../browser/constants.js";
|
|
20
20
|
import { getCliVersion } from "../version.js";
|
|
21
21
|
import { getOracleHomeDir } from "../oracleHome.js";
|
|
22
|
-
import { resolveBrowserProvider,
|
|
22
|
+
import { resolveBrowserProvider, resolveRemoteBrowserModel } from "../browser/provider.js";
|
|
23
23
|
import { cleanupStaleProfileState, readDevToolsPort, verifyDevToolsReachable, writeChromePid, writeDevToolsActivePort, } from "../browser/profileState.js";
|
|
24
24
|
import { normalizeChatgptUrl } from "../browser/utils.js";
|
|
25
25
|
import { computeFileSha256, resolveSessionArtifactsDir, sanitizeArtifactFilename, sanitizeArtifactMimeType, validateArtifactFile, } from "../browser/artifacts.js";
|
|
@@ -60,7 +60,6 @@ function validateAdmissionOptions(options) {
|
|
|
60
60
|
throw new Error("--max-queued-runs must be a nonnegative integer.");
|
|
61
61
|
}
|
|
62
62
|
export async function createRemoteServer(options = {}, deps = {}) {
|
|
63
|
-
const runBrowser = deps.runBrowser ?? runBrowserMode;
|
|
64
63
|
const attachedBrowser = usesHostBrowserAttachment(options.browserConfig);
|
|
65
64
|
const manualLoginDefault = !attachedBrowser && options.manualLoginDefault;
|
|
66
65
|
const hostBrowserConfig = options.browserConfig
|
|
@@ -231,13 +230,20 @@ export async function createRemoteServer(options = {}, deps = {}) {
|
|
|
231
230
|
await abandon();
|
|
232
231
|
return;
|
|
233
232
|
}
|
|
234
|
-
|
|
233
|
+
let model;
|
|
234
|
+
try {
|
|
235
|
+
model = resolveRemoteBrowserModel(payload.options.model, payload.browserConfig?.desiredModel);
|
|
236
|
+
if (resolveBrowserProvider(model) === "gemini" && payload.options.imageOutputRequested) {
|
|
237
|
+
throw new Error("Remote Gemini image generation and editing are not supported; run these requests locally.");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
235
241
|
await abandon();
|
|
236
242
|
if (!res.destroyed) {
|
|
237
243
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
238
244
|
res.end(JSON.stringify({
|
|
239
245
|
error: "unsupported_browser_provider",
|
|
240
|
-
message:
|
|
246
|
+
message: error instanceof Error ? error.message : String(error),
|
|
241
247
|
}));
|
|
242
248
|
}
|
|
243
249
|
return;
|
|
@@ -362,8 +368,16 @@ export async function createRemoteServer(options = {}, deps = {}) {
|
|
|
362
368
|
logger(`[serve] Enforcing manual-login profile at ${options.manualLoginProfileDir ?? "default"} for remote run ${runId}`);
|
|
363
369
|
}
|
|
364
370
|
}
|
|
371
|
+
const runBrowser = deps.runBrowser ??
|
|
372
|
+
(await resolveBrowserExecutor({
|
|
373
|
+
model: model ?? "gpt-5.5",
|
|
374
|
+
youtube: typeof payload.options.youtube === "string" ? payload.options.youtube : undefined,
|
|
375
|
+
geminiShowThoughts: payload.options.geminiShowThoughts === true,
|
|
376
|
+
geminiAllowModelFallback: payload.options.geminiAllowModelFallback !== false,
|
|
377
|
+
}));
|
|
365
378
|
const result = await runBrowser({
|
|
366
379
|
prompt: payload.prompt,
|
|
380
|
+
model,
|
|
367
381
|
attachments,
|
|
368
382
|
fallbackSubmission,
|
|
369
383
|
config: payload.browserConfig,
|
|
@@ -867,6 +881,7 @@ function sanitizeResult(result, warnings = []) {
|
|
|
867
881
|
answerChars: result.answerChars,
|
|
868
882
|
modelSelection: result.modelSelection,
|
|
869
883
|
thinkingSelection: result.thinkingSelection,
|
|
884
|
+
providerNativeCapture: result.providerNativeCapture,
|
|
870
885
|
researchPlan: result.researchPlan,
|
|
871
886
|
archive: result.archive,
|
|
872
887
|
tabUrl: result.tabUrl,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steipete/oracle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"description": "CLI wrapper around OpenAI Responses API with GPT-5.6 Sol, GPT-5.6, GPT-5.5 Pro, GPT-5.5, GPT-5.4, GPT-5.2, GPT-5.1, and GPT-5.1 Codex high reasoning modes.",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://askoracle.sh",
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"shiki": "^4.4.3",
|
|
80
80
|
"toasted-notifier": "^10.1.0",
|
|
81
81
|
"tokentally": "^0.1.6",
|
|
82
|
+
"undici": "^7.29.1",
|
|
82
83
|
"zod": "^4.6.2"
|
|
83
84
|
},
|
|
84
85
|
"devDependencies": {
|