@steipete/oracle 0.18.0 → 0.20.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/README.md +1 -1
- package/dist/bin/oracle-cli.js +76 -76
- package/dist/bin/oracle-mcp.js +0 -0
- package/dist/src/browser/actions/archiveConversation.js +31 -7
- package/dist/src/browser/actions/assistantResponse.js +43 -1
- package/dist/src/browser/actions/attachmentContext.js +239 -0
- package/dist/src/browser/actions/attachmentDataTransfer.js +19 -1
- package/dist/src/browser/actions/attachmentEvidence.js +78 -0
- package/dist/src/browser/actions/attachmentProgress.js +41 -0
- package/dist/src/browser/actions/attachmentPrompt.js +130 -0
- package/dist/src/browser/actions/attachments.js +306 -355
- package/dist/src/browser/actions/deepResearch.js +170 -45
- package/dist/src/browser/actions/modelSelection.js +91 -4
- package/dist/src/browser/actions/navigation.js +1 -1
- package/dist/src/browser/actions/promptComposer.js +364 -186
- package/dist/src/browser/actions/remoteFileTransfer.js +4 -2
- package/dist/src/browser/actions/thinkingStatus.js +2 -5
- package/dist/src/browser/actions/thinkingTime.js +156 -16
- package/dist/src/browser/actions/webSearch.js +96 -0
- package/dist/src/browser/attachRunning.js +46 -2
- package/dist/src/browser/attachmentValidation.js +53 -0
- package/dist/src/browser/cancellation.js +105 -0
- package/dist/src/browser/chatgptImages.js +2 -0
- package/dist/src/browser/chatgptThrottle.js +104 -0
- package/dist/src/browser/chromeLifecycle.js +272 -47
- package/dist/src/browser/config.js +14 -1
- package/dist/src/browser/detect.js +1 -1
- package/dist/src/browser/index.js +525 -274
- package/dist/src/browser/liveTabs.js +3 -4
- package/dist/src/browser/modelDisplay.js +9 -0
- package/dist/src/browser/pageActions.js +2 -2
- package/dist/src/browser/profileState.js +114 -9
- package/dist/src/browser/projectSourcesRunner.js +83 -15
- package/dist/src/browser/prompt.js +272 -113
- package/dist/src/browser/providers/chatgptDomProvider.js +3 -0
- package/dist/src/browser/reattach.js +31 -4
- package/dist/src/browser/recoveryTarget.js +131 -0
- package/dist/src/browser/sessionRunner.js +73 -11
- package/dist/src/browser/tabLeaseRegistry.js +434 -38
- package/dist/src/browser/targetClaim.js +54 -0
- package/dist/src/browser/utils.js +20 -2
- package/dist/src/cli/browserConfig.js +36 -4
- package/dist/src/cli/browserDefaults.js +10 -0
- package/dist/src/cli/browserTabs.js +15 -29
- package/dist/src/cli/detachedSession.js +69 -0
- package/dist/src/cli/dryRun.js +58 -48
- package/dist/src/cli/harvestIntegrity.js +140 -0
- package/dist/src/cli/options.js +15 -0
- package/dist/src/cli/recoveredBrowserHarvest.js +50 -0
- package/dist/src/cli/runOptions.js +19 -4
- package/dist/src/cli/serveBrowserConfig.js +24 -0
- package/dist/src/cli/sessionDisplay.js +13 -7
- package/dist/src/cli/sessionRunner.js +95 -12
- package/dist/src/config.js +1 -0
- package/dist/src/gemini-web/client.js +67 -21
- package/dist/src/gemini-web/executor.js +4 -0
- package/dist/src/gemini-web/http.js +15 -0
- package/dist/src/gemini-web/models.js +15 -11
- package/dist/src/mcp/server.js +11 -14
- package/dist/src/mcp/tools/chatgptImage.js +4 -4
- package/dist/src/mcp/tools/consult.js +86 -24
- package/dist/src/mcp/tools/projectSources.js +4 -6
- package/dist/src/mcp/tools/sessionResources.js +1 -1
- package/dist/src/mcp/tools/sessions.js +2 -2
- package/dist/src/mcp/tools/wait.js +182 -0
- package/dist/src/mcp/types.js +8 -1
- package/dist/src/oracle/config.js +14 -0
- package/dist/src/oracle/errors.js +11 -0
- package/dist/src/oracle/geminiModels.js +1 -0
- package/dist/src/oracle/providerRoutePlan.js +3 -2
- package/dist/src/oracle/run.js +27 -4
- package/dist/src/remote/client.js +73 -6
- package/dist/src/remote/health.js +6 -2
- package/dist/src/remote/runSlots.js +83 -0
- package/dist/src/remote/server.js +346 -71
- package/dist/src/sessionManager.js +36 -1
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/package.json +25 -23
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/dist/bin/oracle.js +0 -683
- package/dist/docs-site/.nojekyll +0 -0
- package/dist/docs-site/CNAME +0 -1
- package/dist/docs-site/RELEASING.html +0 -410
- package/dist/docs-site/agents.html +0 -374
- package/dist/docs-site/anthropic.html +0 -368
- package/dist/docs-site/bridge.html +0 -400
- package/dist/docs-site/browser-mode.html +0 -588
- package/dist/docs-site/chromium-forks.html +0 -347
- package/dist/docs-site/cli-reference.html +0 -346
- package/dist/docs-site/configuration.html +0 -436
- package/dist/docs-site/favicon.svg +0 -14
- package/dist/docs-site/followup.html +0 -368
- package/dist/docs-site/gemini.html +0 -379
- package/dist/docs-site/grok.html +0 -325
- package/dist/docs-site/index.html +0 -360
- package/dist/docs-site/install.html +0 -335
- package/dist/docs-site/linux.html +0 -321
- package/dist/docs-site/llms.txt +0 -43
- package/dist/docs-site/manual-tests.html +0 -595
- package/dist/docs-site/mcp.html +0 -368
- package/dist/docs-site/multimodel.html +0 -364
- package/dist/docs-site/mythical-pro-agents.html +0 -360
- package/dist/docs-site/notifier.html +0 -338
- package/dist/docs-site/openai-endpoints.html +0 -387
- package/dist/docs-site/openrouter.html +0 -344
- package/dist/docs-site/quickstart.html +0 -369
- package/dist/docs-site/refactor/ux.html +0 -532
- package/dist/docs-site/sessions.html +0 -388
- package/dist/docs-site/social-card.png +0 -0
- package/dist/docs-site/social-card.svg +0 -79
- package/dist/docs-site/spec.html +0 -363
- package/dist/docs-site/testing.html +0 -320
- package/dist/docs-site/tui-debug.html +0 -326
- package/dist/docs-site/windows-work.html +0 -323
- package/dist/docs-site/windows.html +0 -320
- package/dist/markdansi/types/index.js +0 -4
- package/dist/oracle/bin/oracle-cli.js +0 -472
- package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
- package/dist/oracle/src/browser/actions/attachments.js +0 -82
- package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
- package/dist/oracle/src/browser/actions/navigation.js +0 -75
- package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
- package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
- package/dist/oracle/src/browser/config.js +0 -33
- package/dist/oracle/src/browser/constants.js +0 -40
- package/dist/oracle/src/browser/cookies.js +0 -210
- package/dist/oracle/src/browser/domDebug.js +0 -36
- package/dist/oracle/src/browser/index.js +0 -331
- package/dist/oracle/src/browser/pageActions.js +0 -5
- package/dist/oracle/src/browser/prompt.js +0 -88
- package/dist/oracle/src/browser/promptSummary.js +0 -20
- package/dist/oracle/src/browser/sessionRunner.js +0 -80
- package/dist/oracle/src/browser/types.js +0 -1
- package/dist/oracle/src/browser/utils.js +0 -62
- package/dist/oracle/src/browserMode.js +0 -1
- package/dist/oracle/src/cli/browserConfig.js +0 -44
- package/dist/oracle/src/cli/dryRun.js +0 -59
- package/dist/oracle/src/cli/engine.js +0 -17
- package/dist/oracle/src/cli/errorUtils.js +0 -9
- package/dist/oracle/src/cli/help.js +0 -70
- package/dist/oracle/src/cli/markdownRenderer.js +0 -15
- package/dist/oracle/src/cli/options.js +0 -103
- package/dist/oracle/src/cli/promptRequirement.js +0 -14
- package/dist/oracle/src/cli/rootAlias.js +0 -30
- package/dist/oracle/src/cli/sessionCommand.js +0 -77
- package/dist/oracle/src/cli/sessionDisplay.js +0 -270
- package/dist/oracle/src/cli/sessionRunner.js +0 -94
- package/dist/oracle/src/heartbeat.js +0 -43
- package/dist/oracle/src/oracle/client.js +0 -48
- package/dist/oracle/src/oracle/config.js +0 -29
- package/dist/oracle/src/oracle/errors.js +0 -101
- package/dist/oracle/src/oracle/files.js +0 -220
- package/dist/oracle/src/oracle/format.js +0 -33
- package/dist/oracle/src/oracle/fsAdapter.js +0 -7
- package/dist/oracle/src/oracle/oscProgress.js +0 -60
- package/dist/oracle/src/oracle/request.js +0 -48
- package/dist/oracle/src/oracle/run.js +0 -444
- package/dist/oracle/src/oracle/tokenStats.js +0 -39
- package/dist/oracle/src/oracle/types.js +0 -1
- package/dist/oracle/src/oracle.js +0 -9
- package/dist/oracle/src/sessionManager.js +0 -205
- package/dist/oracle/src/version.js +0 -39
- package/dist/scripts/chrome/browser-tools.js +0 -295
- package/dist/src/browser/chromeCookies.js +0 -312
- package/dist/src/browser/keytarShim.js +0 -56
- package/dist/src/browser/profileSync.js +0 -141
- package/dist/src/browser/windowsCookies.js +0 -219
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { sessionStore } from "../sessionStore.js";
|
|
3
|
+
import { isRecoveredConversationHarvestReady } from "../browser/recoverConversation.js";
|
|
4
|
+
import { hasOtherLiveBrowserController, matchesOwnedRecoveryTarget, retireRecoveredBrowserTarget, } from "../browser/recoveryTarget.js";
|
|
5
|
+
import { extractConversationIdFromUrl } from "../browser/reattachHelpers.js";
|
|
6
|
+
import { estimateTokenCount } from "../browser/utils.js";
|
|
7
|
+
export async function completeOwnedBrowserHarvest(sessionId, harvested, integrity, logger) {
|
|
8
|
+
if (harvested.state !== "completed" ||
|
|
9
|
+
harvested.stopExists ||
|
|
10
|
+
integrity.status !== "matched" ||
|
|
11
|
+
integrity.explicitTarget)
|
|
12
|
+
return;
|
|
13
|
+
const metadata = await sessionStore.readSession(sessionId);
|
|
14
|
+
if (!metadata?.browser?.runtime?.ownedRecoveryTarget ||
|
|
15
|
+
!isRecoveredConversationHarvestReady(harvested))
|
|
16
|
+
return;
|
|
17
|
+
const runtime = metadata.browser?.runtime;
|
|
18
|
+
const capture = {
|
|
19
|
+
host: harvested.host ?? "",
|
|
20
|
+
port: harvested.port ?? 0,
|
|
21
|
+
targetId: harvested.targetId,
|
|
22
|
+
browserWSEndpoint: runtime?.chromeBrowserWSEndpoint,
|
|
23
|
+
conversationId: harvested.conversationId ?? extractConversationIdFromUrl(harvested.url),
|
|
24
|
+
};
|
|
25
|
+
if (hasOtherLiveBrowserController(metadata) || !matchesOwnedRecoveryTarget(metadata, capture))
|
|
26
|
+
return;
|
|
27
|
+
// Harvest previously persisted only a snippet/hash. Save the full answer before retirement.
|
|
28
|
+
const answer = harvested.lastAssistantMarkdown || harvested.lastAssistantText;
|
|
29
|
+
const paths = await sessionStore.getPaths(sessionId);
|
|
30
|
+
await fs.appendFile(paths.log, `[reattach] harvested assistant response from existing Chrome tab\nAnswer:\n${answer}\n`, "utf8");
|
|
31
|
+
const outputTokens = estimateTokenCount(answer);
|
|
32
|
+
const usage = { inputTokens: 0, outputTokens, reasoningTokens: 0, totalTokens: outputTokens };
|
|
33
|
+
if (metadata.model) {
|
|
34
|
+
await sessionStore.updateModelRun(sessionId, metadata.model, {
|
|
35
|
+
status: "completed",
|
|
36
|
+
completedAt: new Date().toISOString(),
|
|
37
|
+
usage,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
await sessionStore.updateSession(sessionId, {
|
|
41
|
+
status: "completed",
|
|
42
|
+
completedAt: new Date().toISOString(),
|
|
43
|
+
usage,
|
|
44
|
+
errorMessage: undefined,
|
|
45
|
+
error: undefined,
|
|
46
|
+
transport: undefined,
|
|
47
|
+
response: { status: "completed" },
|
|
48
|
+
});
|
|
49
|
+
await retireRecoveredBrowserTarget(sessionId, capture, logger);
|
|
50
|
+
}
|
|
@@ -4,7 +4,7 @@ import { normalizeModelOption, inferModelFromLabel, resolveApiModel, normalizeBa
|
|
|
4
4
|
import { resolveGeminiModelId } from "../oracle/gemini.js";
|
|
5
5
|
import { resolveOverriddenApiModel } from "../oracle/modelResolver.js";
|
|
6
6
|
import { PromptValidationError } from "../oracle/errors.js";
|
|
7
|
-
import { normalizeChatGptModelForBrowser } from "./browserConfig.js";
|
|
7
|
+
import { normalizeChatGptModelForBrowser, isGpt6ProAlias } from "./browserConfig.js";
|
|
8
8
|
import { resolveConfiguredMaxFileSizeBytes } from "./fileSize.js";
|
|
9
9
|
import { isAzureOpenAICandidateModel } from "../oracle/providerRouting.js";
|
|
10
10
|
export function resolveRunOptionsFromConfig({ prompt, files = [], model, models, engine, userConfig, env = process.env, }) {
|
|
@@ -24,7 +24,10 @@ export function resolveRunOptionsFromConfig({ prompt, files = [], model, models,
|
|
|
24
24
|
.map((entry) => normalizeModelOption(entry))
|
|
25
25
|
.filter(Boolean);
|
|
26
26
|
const cliModelArg = normalizeModelOption(model ?? userConfig?.model) || DEFAULT_MODEL;
|
|
27
|
-
const
|
|
27
|
+
const isGpt6Pro = isGpt6ProAlias(cliModelArg);
|
|
28
|
+
const apiModel = isGpt6Pro && (resolvedEngine === "browser" || browserEngineRequested)
|
|
29
|
+
? "gpt-6-pro"
|
|
30
|
+
: resolveApiModel(cliModelArg);
|
|
28
31
|
// Browser label inference is intentionally engine-scoped: API model ids such as
|
|
29
32
|
// gpt-5.6-luna must remain provider values even though browser mode rejects
|
|
30
33
|
// unrecognized GPT-5.6 picker variants.
|
|
@@ -36,9 +39,11 @@ export function resolveRunOptionsFromConfig({ prompt, files = [], model, models,
|
|
|
36
39
|
const isGrok = apiModel.startsWith("grok");
|
|
37
40
|
const engineWasBrowser = resolvedEngine === "browser";
|
|
38
41
|
const allModels = normalizedRequestedModels.length > 0
|
|
39
|
-
? Array.from(new Set(normalizedRequestedModels.map((entry) =>
|
|
42
|
+
? Array.from(new Set(normalizedRequestedModels.map((entry) => isGpt6ProAlias(entry) && (resolvedEngine === "browser" || browserEngineRequested)
|
|
43
|
+
? "gpt-6-pro"
|
|
44
|
+
: resolveApiModel(entry))))
|
|
40
45
|
: [apiModel];
|
|
41
|
-
const browserCompatibilityModels = normalizedRequestedModels.length > 0 ? allModels : [browserModel];
|
|
46
|
+
const browserCompatibilityModels = normalizedRequestedModels.length > 0 ? allModels : [browserModel ?? apiModel];
|
|
42
47
|
const isBrowserCompatible = (m) => m.startsWith("gpt-") || m.startsWith("gemini");
|
|
43
48
|
const hasNonBrowserCompatibleTarget = browserEngineRequested && browserCompatibilityModels.some((m) => !isBrowserCompatible(m));
|
|
44
49
|
if (hasNonBrowserCompatibleTarget) {
|
|
@@ -52,6 +57,16 @@ export function resolveRunOptionsFromConfig({ prompt, files = [], model, models,
|
|
|
52
57
|
const fixedEngine = isCodex || isClaude || isGrok || azureAutoApi || normalizedRequestedModels.length > 0
|
|
53
58
|
? "api"
|
|
54
59
|
: resolvedEngine;
|
|
60
|
+
if (fixedEngine === "api") {
|
|
61
|
+
if (isGpt6ProAlias(cliModelArg)) {
|
|
62
|
+
resolveApiModel(cliModelArg);
|
|
63
|
+
}
|
|
64
|
+
for (const entry of normalizedRequestedModels) {
|
|
65
|
+
if (isGpt6ProAlias(entry)) {
|
|
66
|
+
resolveApiModel(entry);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
55
70
|
// Browser runs use ChatGPT picker labels/aliases; API runs must keep API model ids intact.
|
|
56
71
|
const resolvedModel = fixedEngine === "browser" ? browserModel : apiModel;
|
|
57
72
|
const promptWithSuffix = userConfig?.promptSuffix && userConfig.promptSuffix.trim().length > 0
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { resolveBrowserApprovalWait } from "../browser/config.js";
|
|
2
|
+
import { parseRemoteChromeTarget } from "./browserConfig.js";
|
|
3
|
+
import { applyBrowserDefaultsFromConfig } from "./browserDefaults.js";
|
|
4
|
+
export function buildServeBrowserConfig(options, config) {
|
|
5
|
+
const flags = {
|
|
6
|
+
browserAttachRunning: options.browserAttachRunning,
|
|
7
|
+
remoteChrome: options.remoteChrome,
|
|
8
|
+
browserApprovalWait: options.browserApprovalWait ??
|
|
9
|
+
(process.env.ORACLE_BROWSER_APPROVAL_WAIT?.trim() || undefined),
|
|
10
|
+
};
|
|
11
|
+
const browser = config.browser;
|
|
12
|
+
applyBrowserDefaultsFromConfig(flags, {
|
|
13
|
+
browser: {
|
|
14
|
+
attachRunning: browser?.attachRunning,
|
|
15
|
+
remoteChrome: browser?.remoteChrome,
|
|
16
|
+
approvalWaitMs: browser?.approvalWaitMs,
|
|
17
|
+
},
|
|
18
|
+
}, (key) => (flags[key] === undefined ? undefined : "cli"));
|
|
19
|
+
return {
|
|
20
|
+
attachRunning: flags.browserAttachRunning ?? false,
|
|
21
|
+
remoteChrome: flags.remoteChrome ? parseRemoteChromeTarget(flags.remoteChrome) : null,
|
|
22
|
+
approvalWaitMs: resolveBrowserApprovalWait(flags.browserApprovalWait),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -6,13 +6,14 @@ import { formatFinishLine } from "../oracle/finishLine.js";
|
|
|
6
6
|
import { sessionStore, wait } from "../sessionStore.js";
|
|
7
7
|
import { formatTokenCount, formatTokenValue } from "../oracle/runUtils.js";
|
|
8
8
|
import { resumeBrowserSession } from "../browser/reattach.js";
|
|
9
|
+
import { retireRecoveredBrowserTarget } from "../browser/recoveryTarget.js";
|
|
9
10
|
import { hasRecoverableChatGptConversation } from "../browser/reattachability.js";
|
|
10
11
|
import { appendArtifacts, saveBrowserTranscriptArtifact, saveDeepResearchReportArtifact, } from "../browser/artifacts.js";
|
|
11
12
|
import { estimateTokenCount } from "../browser/utils.js";
|
|
12
13
|
import { formatSessionTableHeader, formatSessionTableRow, resolveSessionCost, } from "./sessionTable.js";
|
|
13
14
|
import { abbreviateResponseId, buildResponseOwnerIndex, resolveSessionLineage, } from "./sessionLineage.js";
|
|
14
15
|
import { formatSessionExecutionLabel } from "./sessionLifecycle.js";
|
|
15
|
-
import { formatBrowserModelSelectionEvidence, formatSessionBrowserModelWithRequestedKey, resolveSessionBrowserModelDisplayName, } from "../browser/modelDisplay.js";
|
|
16
|
+
import { formatBrowserModelSelectionEvidence, formatBrowserThinkingSelectionEvidence, formatSessionBrowserModelWithRequestedKey, resolveSessionBrowserModelDisplayName, } from "../browser/modelDisplay.js";
|
|
16
17
|
const isTty = () => Boolean(process.stdout.isTTY);
|
|
17
18
|
const dim = (text) => (isTty() ? kleur.dim(text) : text);
|
|
18
19
|
export const MAX_RENDER_BYTES = 200_000;
|
|
@@ -88,11 +89,8 @@ async function writeReattachAnswer(sessionId, result, replaceExistingLog) {
|
|
|
88
89
|
await fs.writeFile(paths.log, `[reattach] replaced incomplete Deep Research capture from existing Chrome tab\nAnswer:\n${body}\n`, "utf8");
|
|
89
90
|
return;
|
|
90
91
|
}
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
logWriter.logLine("Answer:");
|
|
94
|
-
logWriter.logLine(body);
|
|
95
|
-
logWriter.stream.end();
|
|
92
|
+
const paths = await sessionStore.getPaths(sessionId);
|
|
93
|
+
await fs.appendFile(paths.log, `[reattach] captured assistant response from existing Chrome tab\nAnswer:\n${body}\n`, "utf8");
|
|
96
94
|
}
|
|
97
95
|
async function saveReattachBrowserArtifacts(sessionId, metadata, result) {
|
|
98
96
|
const body = result.answerMarkdown || result.answerText;
|
|
@@ -255,6 +253,7 @@ export async function attachSession(sessionId, options) {
|
|
|
255
253
|
config: metadata.browser?.config,
|
|
256
254
|
runtime,
|
|
257
255
|
modelSelection: metadata.browser?.modelSelection,
|
|
256
|
+
thinkingSelection: metadata.browser?.thinkingSelection,
|
|
258
257
|
warnings: metadata.browser?.warnings,
|
|
259
258
|
},
|
|
260
259
|
artifacts,
|
|
@@ -263,6 +262,7 @@ export async function attachSession(sessionId, options) {
|
|
|
263
262
|
transport: undefined,
|
|
264
263
|
});
|
|
265
264
|
console.log(chalk.green("Reattach succeeded; session marked completed."));
|
|
265
|
+
await retireRecoveredBrowserTarget(sessionId, result.captureTarget, (line) => console.log(dim(line)));
|
|
266
266
|
metadata = (await sessionStore.readSession(sessionId)) ?? metadata;
|
|
267
267
|
}
|
|
268
268
|
catch (error) {
|
|
@@ -603,7 +603,9 @@ export function formatUserErrorMetadata(metadata) {
|
|
|
603
603
|
}
|
|
604
604
|
export function formatBrowserEvidence(metadata) {
|
|
605
605
|
const browser = metadata.browser;
|
|
606
|
-
if (!browser?.modelSelection &&
|
|
606
|
+
if (!browser?.modelSelection &&
|
|
607
|
+
!browser?.thinkingSelection &&
|
|
608
|
+
(!browser?.warnings || browser.warnings.length === 0)) {
|
|
607
609
|
return null;
|
|
608
610
|
}
|
|
609
611
|
const lines = [];
|
|
@@ -611,6 +613,10 @@ export function formatBrowserEvidence(metadata) {
|
|
|
611
613
|
if (evidence) {
|
|
612
614
|
lines.push(`model ${formatBrowserModelSelectionEvidence(evidence, metadata.model)}`);
|
|
613
615
|
}
|
|
616
|
+
const thinkingEvidence = browser.thinkingSelection;
|
|
617
|
+
if (thinkingEvidence) {
|
|
618
|
+
lines.push(`effort ${formatBrowserThinkingSelectionEvidence(thinkingEvidence)}`);
|
|
619
|
+
}
|
|
614
620
|
for (const warning of browser.warnings ?? []) {
|
|
615
621
|
lines.push(`warning ${warning.code}: ${warning.message}`);
|
|
616
622
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import kleur from "kleur";
|
|
2
|
+
import { constants as fsConstants } from "node:fs";
|
|
2
3
|
import fs from "node:fs/promises";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { runOracle, OracleResponseError, OracleTransportError, extractResponseMetadata, asOracleUserError, extractTextOutput, classifyProviderFailure, } from "../oracle.js";
|
|
@@ -21,8 +22,10 @@ import { sanitizeOscProgress } from "./oscUtils.js";
|
|
|
21
22
|
import { readFiles } from "../oracle/files.js";
|
|
22
23
|
import { cwd as getCwd } from "node:process";
|
|
23
24
|
import { resumeBrowserSession } from "../browser/reattach.js";
|
|
25
|
+
import { retireRecoveredBrowserTarget } from "../browser/recoveryTarget.js";
|
|
24
26
|
import { hasRecoverableChatGptConversation } from "../browser/reattachability.js";
|
|
25
27
|
import { estimateTokenCount } from "../browser/utils.js";
|
|
28
|
+
import { computeFileSha256, sanitizeArtifactFilename } from "../browser/artifacts.js";
|
|
26
29
|
import { formatElapsed } from "../oracle/format.js";
|
|
27
30
|
import { formatBrowserReattachGuidance } from "./reattachGuidance.js";
|
|
28
31
|
const isTty = process.stdout.isTTY;
|
|
@@ -78,7 +81,13 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
78
81
|
cwd,
|
|
79
82
|
log,
|
|
80
83
|
}, runnerDeps);
|
|
81
|
-
await writeAssistantOutput(runOptions.writeOutputPath, result.answerText ?? "", log);
|
|
84
|
+
const writtenOutputPath = await writeAssistantOutput(runOptions.writeOutputPath, result.answerText ?? "", log);
|
|
85
|
+
const outputArtifacts = await copyBrowserOutputArtifacts({
|
|
86
|
+
outputPath: writtenOutputPath,
|
|
87
|
+
savedFiles: runOptions.writeArtifacts ? result.savedFiles : undefined,
|
|
88
|
+
log,
|
|
89
|
+
});
|
|
90
|
+
const browserWarnings = [...(result.warnings ?? []), ...outputArtifacts.warnings];
|
|
82
91
|
await sendSessionNotification({
|
|
83
92
|
sessionId: sessionMeta.id,
|
|
84
93
|
sessionName: sessionMeta.options?.slug ?? sessionMeta.id,
|
|
@@ -105,9 +114,10 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
105
114
|
runtime: result.runtime,
|
|
106
115
|
archive: result.archive,
|
|
107
116
|
modelSelection: result.modelSelection,
|
|
108
|
-
|
|
117
|
+
thinkingSelection: result.thinkingSelection,
|
|
118
|
+
warnings: browserWarnings.length > 0 ? browserWarnings : undefined,
|
|
109
119
|
},
|
|
110
|
-
artifacts: mergeArtifacts(sessionMeta.artifacts, result.artifacts),
|
|
120
|
+
artifacts: mergeArtifacts(sessionMeta.artifacts, mergeArtifacts(result.artifacts, outputArtifacts.artifacts)),
|
|
111
121
|
response: undefined,
|
|
112
122
|
transport: undefined,
|
|
113
123
|
error: undefined,
|
|
@@ -848,6 +858,81 @@ function resolveSessionPath(sessionDir, targetPath) {
|
|
|
848
858
|
}
|
|
849
859
|
return path.join(sessionDir, targetPath);
|
|
850
860
|
}
|
|
861
|
+
async function copyBrowserOutputArtifacts(params) {
|
|
862
|
+
const artifacts = [];
|
|
863
|
+
const warnings = [];
|
|
864
|
+
if (!params.outputPath || !params.savedFiles?.length) {
|
|
865
|
+
return { artifacts, warnings };
|
|
866
|
+
}
|
|
867
|
+
const outputDir = path.dirname(params.outputPath);
|
|
868
|
+
for (const savedFile of params.savedFiles) {
|
|
869
|
+
const filename = sanitizeArtifactFilename(savedFile.filename ?? path.basename(savedFile.path), "artifact.bin");
|
|
870
|
+
let copied;
|
|
871
|
+
try {
|
|
872
|
+
copied = await copyFileWithoutOverwrite(savedFile.path, path.join(outputDir, filename));
|
|
873
|
+
const [stat, sha256] = await Promise.all([fs.stat(copied), computeFileSha256(copied)]);
|
|
874
|
+
if (savedFile.sizeBytes != null && stat.size !== savedFile.sizeBytes) {
|
|
875
|
+
throw new Error(`size mismatch (expected ${savedFile.sizeBytes}, received ${stat.size})`);
|
|
876
|
+
}
|
|
877
|
+
if (savedFile.sha256 && sha256 !== savedFile.sha256) {
|
|
878
|
+
throw new Error(`sha256 mismatch (expected ${savedFile.sha256}, received ${sha256})`);
|
|
879
|
+
}
|
|
880
|
+
const artifact = {
|
|
881
|
+
kind: "file",
|
|
882
|
+
path: copied,
|
|
883
|
+
label: `${savedFile.label ?? filename} (write-output)`,
|
|
884
|
+
mimeType: savedFile.mimeType,
|
|
885
|
+
sizeBytes: stat.size,
|
|
886
|
+
sourceUrl: savedFile.sourceUrl,
|
|
887
|
+
sha256,
|
|
888
|
+
validation: savedFile.validation,
|
|
889
|
+
transfer: { status: "not-needed" },
|
|
890
|
+
origin: { mode: "local" },
|
|
891
|
+
};
|
|
892
|
+
artifacts.push(artifact);
|
|
893
|
+
params.log(dim(`[browser] Saved write-output file artifact to ${copied} sha256=${sha256}`));
|
|
894
|
+
}
|
|
895
|
+
catch (error) {
|
|
896
|
+
if (copied) {
|
|
897
|
+
await fs.unlink(copied).catch(() => undefined);
|
|
898
|
+
}
|
|
899
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
900
|
+
const message = `Failed to copy saved file artifact ${savedFile.path} beside ${params.outputPath}: ${reason}`;
|
|
901
|
+
params.log(dim(`[browser] ${message}`));
|
|
902
|
+
warnings.push({
|
|
903
|
+
code: "browser-output-artifact-copy-failed",
|
|
904
|
+
severity: "warning",
|
|
905
|
+
message,
|
|
906
|
+
details: {
|
|
907
|
+
sourcePath: savedFile.path,
|
|
908
|
+
outputPath: params.outputPath,
|
|
909
|
+
sha256: savedFile.sha256,
|
|
910
|
+
},
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
return { artifacts, warnings };
|
|
915
|
+
}
|
|
916
|
+
async function copyFileWithoutOverwrite(sourcePath, baseTargetPath) {
|
|
917
|
+
const ext = path.extname(baseTargetPath);
|
|
918
|
+
const stem = ext ? path.basename(baseTargetPath, ext) : path.basename(baseTargetPath);
|
|
919
|
+
const dir = path.dirname(baseTargetPath);
|
|
920
|
+
let suffix = 1;
|
|
921
|
+
for (;;) {
|
|
922
|
+
const targetPath = suffix === 1 ? baseTargetPath : path.join(dir, `${stem}-${suffix}${ext}`);
|
|
923
|
+
try {
|
|
924
|
+
await fs.copyFile(sourcePath, targetPath, fsConstants.COPYFILE_EXCL);
|
|
925
|
+
return targetPath;
|
|
926
|
+
}
|
|
927
|
+
catch (error) {
|
|
928
|
+
if (isErrorCode(error, "EEXIST")) {
|
|
929
|
+
suffix += 1;
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
throw error;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
851
936
|
async function writeAssistantOutput(targetPath, content, log) {
|
|
852
937
|
if (!targetPath)
|
|
853
938
|
return;
|
|
@@ -955,11 +1040,8 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
955
1040
|
existingArtifacts: sessionMeta.artifacts,
|
|
956
1041
|
logger,
|
|
957
1042
|
});
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
logWriter.logLine("Answer:");
|
|
961
|
-
logWriter.logLine(answerText);
|
|
962
|
-
logWriter.stream.end();
|
|
1043
|
+
const paths = await sessionStore.getPaths(sessionMeta.id);
|
|
1044
|
+
await fs.appendFile(paths.log, `[auto-reattach] captured assistant response on attempt ${attempt}\nAnswer:\n${answerText}\n`, "utf8");
|
|
963
1045
|
if (modelForStatus) {
|
|
964
1046
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
965
1047
|
status: "completed",
|
|
@@ -1005,6 +1087,7 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
1005
1087
|
transport: undefined,
|
|
1006
1088
|
});
|
|
1007
1089
|
log(kleur.green("Auto-reattach succeeded; session marked completed."));
|
|
1090
|
+
await retireRecoveredBrowserTarget(sessionMeta.id, result.captureTarget, logger);
|
|
1008
1091
|
return true;
|
|
1009
1092
|
}
|
|
1010
1093
|
catch (error) {
|
|
@@ -1057,11 +1140,11 @@ export function deriveModelOutputPath(basePath, model) {
|
|
|
1057
1140
|
const suffix = ext.length > 0 ? `${stem}.${model}${ext}` : `${stem}.${model}`;
|
|
1058
1141
|
return path.join(dir, suffix);
|
|
1059
1142
|
}
|
|
1143
|
+
function isErrorCode(error, expected) {
|
|
1144
|
+
return error instanceof Error && error.code === expected;
|
|
1145
|
+
}
|
|
1060
1146
|
function isPermissionError(error) {
|
|
1061
|
-
|
|
1062
|
-
return false;
|
|
1063
|
-
const code = error.code;
|
|
1064
|
-
return code === "EACCES" || code === "EPERM";
|
|
1147
|
+
return isErrorCode(error, "EACCES") || isErrorCode(error, "EPERM");
|
|
1065
1148
|
}
|
|
1066
1149
|
function buildFallbackPath(original) {
|
|
1067
1150
|
const ext = path.extname(original);
|
package/dist/src/config.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
|
|
4
|
+
import { fetchGeminiWebResource } from "./http.js";
|
|
5
|
+
import { buildGeminiWebModelHeader, FALLBACK_GEMINI_WEB_MODEL, getGeminiWebModelSelection, } from "./models.js";
|
|
6
|
+
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36";
|
|
5
7
|
const MODEL_HEADER_NAME = "x-goog-ext-525001261-jspb";
|
|
6
8
|
const GEMINI_APP_URL = "https://gemini.google.com/app";
|
|
7
9
|
const GEMINI_STREAM_GENERATE_URL = "https://gemini.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate";
|
|
@@ -61,7 +63,7 @@ function buildCookieHeader(cookieMap) {
|
|
|
61
63
|
}
|
|
62
64
|
export async function fetchGeminiAccessToken(cookieMap, signal) {
|
|
63
65
|
const cookieHeader = buildCookieHeader(cookieMap);
|
|
64
|
-
const res = await
|
|
66
|
+
const res = await fetchGeminiWebResource(GEMINI_APP_URL, {
|
|
65
67
|
redirect: "follow",
|
|
66
68
|
signal,
|
|
67
69
|
headers: {
|
|
@@ -112,7 +114,7 @@ function ensureFullSizeImageUrl(url) {
|
|
|
112
114
|
async function fetchWithCookiePreservingRedirects(url, init, signal, maxRedirects = 10) {
|
|
113
115
|
let current = url;
|
|
114
116
|
for (let i = 0; i <= maxRedirects; i += 1) {
|
|
115
|
-
const res = await
|
|
117
|
+
const res = await fetchGeminiWebResource(current, { ...init, redirect: "manual", signal });
|
|
116
118
|
if (res.status >= 300 && res.status < 400) {
|
|
117
119
|
const location = res.headers.get("location");
|
|
118
120
|
if (!location)
|
|
@@ -146,7 +148,7 @@ async function uploadGeminiFile(filePath, signal) {
|
|
|
146
148
|
const mimeType = resolveGeminiUploadMimeType(absPath);
|
|
147
149
|
const form = new FormData();
|
|
148
150
|
form.append("file", new Blob([data], { type: mimeType }), fileName);
|
|
149
|
-
const res = await
|
|
151
|
+
const res = await fetchGeminiWebResource(GEMINI_UPLOAD_URL, {
|
|
150
152
|
method: "POST",
|
|
151
153
|
redirect: "follow",
|
|
152
154
|
signal,
|
|
@@ -162,18 +164,38 @@ async function uploadGeminiFile(filePath, signal) {
|
|
|
162
164
|
}
|
|
163
165
|
return { id: text, name: fileName, mimeType };
|
|
164
166
|
}
|
|
165
|
-
function buildGeminiFReqPayload(prompt, uploaded, chatMetadata) {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
167
|
+
function buildGeminiFReqPayload(prompt, uploaded, chatMetadata, model, requestId) {
|
|
168
|
+
const request = Array(97).fill(null);
|
|
169
|
+
request[0] = [
|
|
170
|
+
prompt,
|
|
171
|
+
0,
|
|
172
|
+
null,
|
|
173
|
+
uploaded.map((file) => [[file.id, 1, null, file.mimeType], file.name]),
|
|
174
|
+
null,
|
|
175
|
+
null,
|
|
176
|
+
0,
|
|
177
|
+
];
|
|
178
|
+
request[1] = ["en"];
|
|
179
|
+
request[2] = chatMetadata ?? ["", "", "", null, null, null, null, null, null, ""];
|
|
180
|
+
request[6] = [1];
|
|
181
|
+
request[7] = 1;
|
|
182
|
+
request[10] = 1;
|
|
183
|
+
request[11] = 0;
|
|
184
|
+
request[17] = [[0]];
|
|
185
|
+
request[18] = 0;
|
|
186
|
+
request[27] = 1;
|
|
187
|
+
request[30] = [4];
|
|
188
|
+
request[41] = [1];
|
|
189
|
+
request[53] = 0;
|
|
190
|
+
request[59] = requestId;
|
|
191
|
+
request[61] = [];
|
|
192
|
+
request[68] = 2;
|
|
193
|
+
const selection = getGeminiWebModelSelection(model);
|
|
194
|
+
request[79] = selection.modelCode;
|
|
195
|
+
request[80] = selection.thinkingCode;
|
|
196
|
+
request[91] = 0;
|
|
197
|
+
request[96] = chatMetadata == null ? 1 : 0;
|
|
198
|
+
return JSON.stringify([null, JSON.stringify(request)]);
|
|
177
199
|
}
|
|
178
200
|
export function parseGeminiStreamGenerateResponse(rawText) {
|
|
179
201
|
const responseJson = JSON.parse(trimGeminiJsonEnvelope(rawText));
|
|
@@ -274,11 +296,12 @@ export async function runGeminiWebOnce(input) {
|
|
|
274
296
|
}
|
|
275
297
|
uploaded.push(await uploadGeminiFile(file, input.signal));
|
|
276
298
|
}
|
|
277
|
-
const
|
|
299
|
+
const requestId = randomUUID().toUpperCase();
|
|
300
|
+
const fReq = buildGeminiFReqPayload(input.prompt, uploaded, input.chatMetadata ?? null, input.model, requestId);
|
|
278
301
|
const params = new URLSearchParams();
|
|
279
302
|
params.set("at", at);
|
|
280
303
|
params.set("f.req", fReq);
|
|
281
|
-
const res = await
|
|
304
|
+
const res = await fetchGeminiWebResource(GEMINI_STREAM_GENERATE_URL, {
|
|
282
305
|
method: "POST",
|
|
283
306
|
redirect: "follow",
|
|
284
307
|
signal: input.signal,
|
|
@@ -290,6 +313,9 @@ export async function runGeminiWebOnce(input) {
|
|
|
290
313
|
"user-agent": USER_AGENT,
|
|
291
314
|
cookie: cookieHeader,
|
|
292
315
|
[MODEL_HEADER_NAME]: buildGeminiWebModelHeader(input.model),
|
|
316
|
+
"x-goog-ext-73010989-jspb": "[0]",
|
|
317
|
+
"x-goog-ext-73010990-jspb": "[0]",
|
|
318
|
+
"x-goog-ext-525005358-jspb": JSON.stringify([requestId, 1]),
|
|
293
319
|
},
|
|
294
320
|
body: params.toString(),
|
|
295
321
|
});
|
|
@@ -338,10 +364,30 @@ export async function runGeminiWebOnce(input) {
|
|
|
338
364
|
export async function runGeminiWebWithFallback(input) {
|
|
339
365
|
const attempt = await runGeminiWebOnce(input);
|
|
340
366
|
if (isGeminiModelUnavailable(attempt.errorCode) && input.model !== FALLBACK_GEMINI_WEB_MODEL) {
|
|
367
|
+
if (input.allowModelFallback === false) {
|
|
368
|
+
throw new Error(`Requested Gemini web model ${input.model} is unavailable and model fallback is disabled.`);
|
|
369
|
+
}
|
|
341
370
|
const fallback = await runGeminiWebOnce({ ...input, model: FALLBACK_GEMINI_WEB_MODEL });
|
|
342
|
-
return {
|
|
371
|
+
return {
|
|
372
|
+
...assertGeminiWebRunSucceeded(fallback),
|
|
373
|
+
effectiveModel: FALLBACK_GEMINI_WEB_MODEL,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
return { ...assertGeminiWebRunSucceeded(attempt), effectiveModel: input.model };
|
|
377
|
+
}
|
|
378
|
+
function assertGeminiWebRunSucceeded(output) {
|
|
379
|
+
if (output.errorCode !== undefined) {
|
|
380
|
+
throw new Error(`Gemini web request failed with error code ${output.errorCode}.`);
|
|
381
|
+
}
|
|
382
|
+
if (output.errorMessage) {
|
|
383
|
+
throw new Error(`Gemini web request failed: ${output.errorMessage}`);
|
|
384
|
+
}
|
|
385
|
+
if (!output.text.trim() &&
|
|
386
|
+
output.images.length === 0 &&
|
|
387
|
+
extractGgdlUrls(output.rawResponseText).length === 0) {
|
|
388
|
+
throw new Error("Gemini web request ended without a response.");
|
|
343
389
|
}
|
|
344
|
-
return
|
|
390
|
+
return output;
|
|
345
391
|
}
|
|
346
392
|
export async function saveFirstGeminiImageFromOutput(output, cookieMap, outputPath, signal) {
|
|
347
393
|
const generatedOrWeb = output.images.find((img) => img.kind === "generated") ?? output.images[0];
|
|
@@ -325,6 +325,7 @@ export function createGeminiWebExecutor(geminiOptions) {
|
|
|
325
325
|
cookieMap: cookieResult.cookieMap,
|
|
326
326
|
chatMetadata: null,
|
|
327
327
|
signal: controller.signal,
|
|
328
|
+
allowModelFallback: geminiOptions.allowModelFallback,
|
|
328
329
|
});
|
|
329
330
|
const editPrompt = `Use image generation tool to ${prompt}`;
|
|
330
331
|
const out = await runGeminiWebWithFallback({
|
|
@@ -334,6 +335,7 @@ export function createGeminiWebExecutor(geminiOptions) {
|
|
|
334
335
|
cookieMap: cookieResult.cookieMap,
|
|
335
336
|
chatMetadata: intro.metadata,
|
|
336
337
|
signal: controller.signal,
|
|
338
|
+
allowModelFallback: geminiOptions.allowModelFallback,
|
|
337
339
|
});
|
|
338
340
|
response = {
|
|
339
341
|
text: out.text ?? null,
|
|
@@ -357,6 +359,7 @@ export function createGeminiWebExecutor(geminiOptions) {
|
|
|
357
359
|
cookieMap: cookieResult.cookieMap,
|
|
358
360
|
chatMetadata: null,
|
|
359
361
|
signal: controller.signal,
|
|
362
|
+
allowModelFallback: geminiOptions.allowModelFallback,
|
|
360
363
|
});
|
|
361
364
|
response = {
|
|
362
365
|
text: out.text ?? null,
|
|
@@ -379,6 +382,7 @@ export function createGeminiWebExecutor(geminiOptions) {
|
|
|
379
382
|
cookieMap: cookieResult.cookieMap,
|
|
380
383
|
chatMetadata: null,
|
|
381
384
|
signal: controller.signal,
|
|
385
|
+
allowModelFallback: geminiOptions.allowModelFallback,
|
|
382
386
|
});
|
|
383
387
|
response = {
|
|
384
388
|
text: out.text ?? null,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export async function fetchGeminiWebResource(url, init = {}) {
|
|
2
|
+
try {
|
|
3
|
+
return await fetch(url, init);
|
|
4
|
+
}
|
|
5
|
+
catch (error) {
|
|
6
|
+
const cause = error instanceof Error ? error.cause : undefined;
|
|
7
|
+
if (cause &&
|
|
8
|
+
typeof cause === "object" &&
|
|
9
|
+
"code" in cause &&
|
|
10
|
+
cause.code === "UND_ERR_HEADERS_OVERFLOW") {
|
|
11
|
+
throw new Error("Gemini response headers exceed Node's configured limit. Start Oracle with NODE_OPTIONS=--max-http-header-size=65536 (preserving any existing Node options) and retry.", { cause: error });
|
|
12
|
+
}
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -6,29 +6,29 @@ const MODEL_SPECS = {
|
|
|
6
6
|
hash: "1d44b34bcaa1c04d",
|
|
7
7
|
modelCode: 6,
|
|
8
8
|
thinkingCode: 1,
|
|
9
|
+
capacity: 2,
|
|
9
10
|
},
|
|
10
11
|
"gemini-3.5-flash": {
|
|
11
12
|
hash: "56fdd199312815e2",
|
|
12
13
|
modelCode: 1,
|
|
13
14
|
thinkingCode: 1,
|
|
15
|
+
capacity: 2,
|
|
14
16
|
},
|
|
15
17
|
"gemini-3.1-pro": {
|
|
16
|
-
hash: "
|
|
18
|
+
hash: "e6fa609c3fa255c0",
|
|
17
19
|
modelCode: 3,
|
|
18
20
|
thinkingCode: 1,
|
|
21
|
+
capacity: 2,
|
|
19
22
|
},
|
|
20
23
|
"gemini-3-pro-deep-think": {
|
|
21
|
-
hash: "
|
|
22
|
-
modelCode:
|
|
23
|
-
thinkingCode:
|
|
24
|
+
hash: "e051ce1aa80aa576",
|
|
25
|
+
modelCode: 5,
|
|
26
|
+
thinkingCode: 2,
|
|
27
|
+
capacity: 2,
|
|
24
28
|
},
|
|
25
29
|
};
|
|
26
|
-
|
|
27
|
-
function
|
|
28
|
-
clientId ??= randomUUID().toUpperCase();
|
|
29
|
-
return clientId;
|
|
30
|
-
}
|
|
31
|
-
export function buildGeminiWebModelHeader(model, webClientId = getGeminiWebClientId()) {
|
|
30
|
+
const clientId = randomUUID().toUpperCase();
|
|
31
|
+
export function buildGeminiWebModelHeader(model, webClientId = clientId) {
|
|
32
32
|
const spec = MODEL_SPECS[model];
|
|
33
33
|
return JSON.stringify([
|
|
34
34
|
1,
|
|
@@ -42,7 +42,7 @@ export function buildGeminiWebModelHeader(model, webClientId = getGeminiWebClien
|
|
|
42
42
|
[4, 5, 6, 8],
|
|
43
43
|
null,
|
|
44
44
|
null,
|
|
45
|
-
|
|
45
|
+
spec.capacity,
|
|
46
46
|
null,
|
|
47
47
|
null,
|
|
48
48
|
spec.modelCode,
|
|
@@ -50,6 +50,10 @@ export function buildGeminiWebModelHeader(model, webClientId = getGeminiWebClien
|
|
|
50
50
|
webClientId,
|
|
51
51
|
]);
|
|
52
52
|
}
|
|
53
|
+
export function getGeminiWebModelSelection(model) {
|
|
54
|
+
const { modelCode, thinkingCode } = MODEL_SPECS[model];
|
|
55
|
+
return { modelCode, thinkingCode };
|
|
56
|
+
}
|
|
53
57
|
export function resolveGeminiWebModel(desiredModel, log) {
|
|
54
58
|
const desired = typeof desiredModel === "string" ? desiredModel.trim() : "";
|
|
55
59
|
if (!desired)
|