@steipete/oracle 0.9.0 → 0.10.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/LICENSE +1 -1
- package/README.md +61 -48
- package/dist/bin/oracle-cli.js +455 -402
- package/dist/bin/oracle-mcp.js +2 -2
- package/dist/bin/oracle.js +165 -279
- package/dist/scripts/agent-send.js +31 -31
- package/dist/scripts/check.js +6 -6
- package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
- package/dist/scripts/docs-list.js +30 -30
- package/dist/scripts/git-policy.js +25 -23
- package/dist/scripts/run-cli.js +8 -8
- package/dist/scripts/runner.js +203 -195
- package/dist/scripts/test-browser.js +21 -18
- package/dist/scripts/test-remote-chrome.js +20 -20
- package/dist/src/bridge/connection.js +18 -18
- package/dist/src/bridge/userConfigFile.js +7 -7
- package/dist/src/browser/actions/assistantResponse.js +149 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +275 -117
- package/dist/src/browser/actions/navigation.js +161 -137
- package/dist/src/browser/actions/promptComposer.js +100 -64
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/chromeLifecycle.js +62 -60
- package/dist/src/browser/config.js +34 -15
- package/dist/src/browser/constants.js +17 -12
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +62 -62
- package/dist/src/browser/domDebug.js +1 -1
- package/dist/src/browser/index.js +390 -295
- package/dist/src/browser/modelStrategy.js +1 -1
- package/dist/src/browser/pageActions.js +5 -5
- package/dist/src/browser/policies.js +16 -13
- package/dist/src/browser/profileState.js +44 -39
- package/dist/src/browser/prompt.js +72 -42
- package/dist/src/browser/promptSummary.js +5 -5
- package/dist/src/browser/providerDomFlow.js +1 -1
- package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
- package/dist/src/browser/providers/index.js +2 -2
- package/dist/src/browser/reattach.js +67 -34
- package/dist/src/browser/reattachHelpers.js +31 -26
- package/dist/src/browser/sessionRunner.js +37 -25
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +16 -16
- package/dist/src/cli/bridge/client.js +28 -20
- package/dist/src/cli/bridge/codexConfig.js +16 -16
- package/dist/src/cli/bridge/doctor.js +47 -39
- package/dist/src/cli/bridge/host.js +58 -56
- package/dist/src/cli/browserConfig.js +62 -48
- package/dist/src/cli/browserDefaults.js +27 -26
- package/dist/src/cli/bundleWarnings.js +1 -1
- package/dist/src/cli/clipboard.js +11 -2
- package/dist/src/cli/detach.js +2 -2
- package/dist/src/cli/dryRun.js +29 -25
- package/dist/src/cli/duplicatePromptGuard.js +3 -3
- package/dist/src/cli/engine.js +9 -9
- package/dist/src/cli/errorUtils.js +1 -1
- package/dist/src/cli/fileSize.js +3 -3
- package/dist/src/cli/format.js +2 -2
- package/dist/src/cli/help.js +28 -28
- package/dist/src/cli/hiddenAliases.js +3 -3
- package/dist/src/cli/markdownBundle.js +7 -7
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +127 -106
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/promptRequirement.js +2 -2
- package/dist/src/cli/renderOutput.js +1 -1
- package/dist/src/cli/rootAlias.js +1 -1
- package/dist/src/cli/runOptions.js +32 -28
- package/dist/src/cli/sessionCommand.js +31 -21
- package/dist/src/cli/sessionDisplay.js +95 -81
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +103 -93
- package/dist/src/cli/sessionTable.js +26 -23
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +139 -128
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +19 -15
- package/dist/src/gemini-web/client.js +76 -70
- package/dist/src/gemini-web/executionMode.js +6 -8
- package/dist/src/gemini-web/executor.js +98 -93
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/server.js +16 -12
- package/dist/src/mcp/tools/consult.js +51 -47
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +5 -5
- package/dist/src/mcp/utils.js +15 -7
- package/dist/src/oracle/background.js +15 -15
- package/dist/src/oracle/claude.js +53 -25
- package/dist/src/oracle/client.js +50 -41
- package/dist/src/oracle/config.js +96 -66
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +55 -46
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -33
- package/dist/src/oracle/logging.js +7 -7
- package/dist/src/oracle/markdown.js +28 -28
- package/dist/src/oracle/modelResolver.js +16 -16
- package/dist/src/oracle/multiModelRunner.js +12 -12
- package/dist/src/oracle/oscProgress.js +8 -8
- package/dist/src/oracle/promptAssembly.js +6 -3
- package/dist/src/oracle/request.js +16 -13
- package/dist/src/oracle/run.js +156 -134
- package/dist/src/oracle/runUtils.js +8 -5
- package/dist/src/oracle/tokenEstimate.js +6 -6
- package/dist/src/oracle/tokenStats.js +5 -5
- package/dist/src/oracle/tokenStringifier.js +5 -5
- package/dist/src/oracle.js +12 -12
- package/dist/src/oracleHome.js +3 -3
- package/dist/src/remote/client.js +25 -25
- package/dist/src/remote/health.js +20 -20
- package/dist/src/remote/remoteServiceConfig.js +9 -9
- package/dist/src/remote/server.js +129 -118
- package/dist/src/sessionManager.js +77 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +66 -62
- package/vendor/oracle-notifier/README.md +2 -0
- 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/profileSync.js +0 -141
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const CLICK_TYPES = [
|
|
2
|
-
export function buildClickDispatcher(functionName =
|
|
1
|
+
const CLICK_TYPES = ["pointerdown", "mousedown", "pointerup", "mouseup", "click"];
|
|
2
|
+
export function buildClickDispatcher(functionName = "dispatchClickSequence") {
|
|
3
3
|
const typesLiteral = JSON.stringify(CLICK_TYPES);
|
|
4
4
|
return `function ${functionName}(target){
|
|
5
5
|
if(!target || !(target instanceof EventTarget)) return false;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { MENU_CONTAINER_SELECTOR, MENU_ITEM_SELECTOR, MODEL_BUTTON_SELECTOR, } from
|
|
2
|
-
import { logDomFailure } from
|
|
3
|
-
import { buildClickDispatcher } from
|
|
4
|
-
export async function ensureModelSelection(Runtime, desiredModel, logger, strategy =
|
|
1
|
+
import { COMPOSER_MODEL_SIGNAL_SELECTOR, MENU_CONTAINER_SELECTOR, MENU_ITEM_SELECTOR, MODEL_BUTTON_SELECTOR, } from "../constants.js";
|
|
2
|
+
import { logDomFailure } from "../domDebug.js";
|
|
3
|
+
import { buildClickDispatcher } from "./domEvents.js";
|
|
4
|
+
export async function ensureModelSelection(Runtime, desiredModel, logger, strategy = "select") {
|
|
5
5
|
const outcome = await Runtime.evaluate({
|
|
6
6
|
expression: buildModelSelectionExpression(desiredModel, strategy),
|
|
7
7
|
awaitPromise: true,
|
|
@@ -9,26 +9,26 @@ export async function ensureModelSelection(Runtime, desiredModel, logger, strate
|
|
|
9
9
|
});
|
|
10
10
|
const result = outcome.result?.value;
|
|
11
11
|
switch (result?.status) {
|
|
12
|
-
case
|
|
13
|
-
case
|
|
14
|
-
case
|
|
12
|
+
case "already-selected":
|
|
13
|
+
case "switched":
|
|
14
|
+
case "switched-best-effort": {
|
|
15
15
|
const label = result.label ?? desiredModel;
|
|
16
16
|
logger(`Model picker: ${label}`);
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
|
-
case
|
|
20
|
-
await logDomFailure(Runtime, logger,
|
|
19
|
+
case "option-not-found": {
|
|
20
|
+
await logDomFailure(Runtime, logger, "model-switcher-option");
|
|
21
21
|
const isTemporary = result.hint?.temporaryChat ?? false;
|
|
22
22
|
const available = (result.hint?.availableOptions ?? []).filter(Boolean);
|
|
23
|
-
const availableHint = available.length > 0 ? ` Available: ${available.join(
|
|
23
|
+
const availableHint = available.length > 0 ? ` Available: ${available.join(", ")}.` : "";
|
|
24
24
|
const tempHint = isTemporary && /\bpro\b/i.test(desiredModel)
|
|
25
25
|
? ' You are in Temporary Chat mode; Pro models are not available there. Remove "temporary-chat=true" from --chatgpt-url or use a non-Pro model (e.g. gpt-5.2).'
|
|
26
|
-
:
|
|
26
|
+
: "";
|
|
27
27
|
throw new Error(`Unable to find model option matching "${desiredModel}" in the model switcher.${availableHint}${tempHint}`);
|
|
28
28
|
}
|
|
29
29
|
default: {
|
|
30
|
-
await logDomFailure(Runtime, logger,
|
|
31
|
-
throw new Error(
|
|
30
|
+
await logDomFailure(Runtime, logger, "model-switcher-button");
|
|
31
|
+
throw new Error("Unable to locate the ChatGPT model selector button.");
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -38,23 +38,33 @@ export async function ensureModelSelection(Runtime, desiredModel, logger, strate
|
|
|
38
38
|
*/
|
|
39
39
|
function buildModelSelectionExpression(targetModel, strategy) {
|
|
40
40
|
const matchers = buildModelMatchersLiteral(targetModel);
|
|
41
|
+
const composerSignalMatchers = buildComposerSignalMatchers(targetModel);
|
|
41
42
|
const labelLiteral = JSON.stringify(matchers.labelTokens);
|
|
42
43
|
const idLiteral = JSON.stringify(matchers.testIdTokens);
|
|
43
44
|
const primaryLabelLiteral = JSON.stringify(targetModel);
|
|
44
45
|
const strategyLiteral = JSON.stringify(strategy);
|
|
46
|
+
const composerSignalSelectorLiteral = JSON.stringify(COMPOSER_MODEL_SIGNAL_SELECTOR);
|
|
47
|
+
const composerIncludesLiteral = JSON.stringify(composerSignalMatchers.includesAny);
|
|
48
|
+
const composerExcludesLiteral = JSON.stringify(composerSignalMatchers.excludesAny);
|
|
49
|
+
const composerAllowBlankLiteral = JSON.stringify(composerSignalMatchers.allowBlank);
|
|
45
50
|
const menuContainerLiteral = JSON.stringify(MENU_CONTAINER_SELECTOR);
|
|
46
51
|
const menuItemLiteral = JSON.stringify(MENU_ITEM_SELECTOR);
|
|
47
52
|
return `(() => {
|
|
48
53
|
${buildClickDispatcher()}
|
|
49
54
|
// Capture the selectors and matcher literals up front so the browser expression stays pure.
|
|
50
55
|
const BUTTON_SELECTOR = '${MODEL_BUTTON_SELECTOR}';
|
|
56
|
+
const COMPOSER_MODEL_SIGNAL_SELECTOR = ${composerSignalSelectorLiteral};
|
|
51
57
|
const LABEL_TOKENS = ${labelLiteral};
|
|
52
58
|
const TEST_IDS = ${idLiteral};
|
|
53
59
|
const PRIMARY_LABEL = ${primaryLabelLiteral};
|
|
54
60
|
const MODEL_STRATEGY = ${strategyLiteral};
|
|
61
|
+
const COMPOSER_SIGNAL_INCLUDES = ${composerIncludesLiteral};
|
|
62
|
+
const COMPOSER_SIGNAL_EXCLUDES = ${composerExcludesLiteral};
|
|
63
|
+
const COMPOSER_SIGNAL_ALLOW_BLANK = ${composerAllowBlankLiteral};
|
|
55
64
|
const INITIAL_WAIT_MS = 150;
|
|
56
65
|
const REOPEN_INTERVAL_MS = 400;
|
|
57
66
|
const MAX_WAIT_MS = 20000;
|
|
67
|
+
const SETTLE_WAIT_MS = 1500;
|
|
58
68
|
const normalizeText = (value) => {
|
|
59
69
|
if (!value) {
|
|
60
70
|
return '';
|
|
@@ -73,7 +83,9 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
73
83
|
const targetWords = normalizedTarget.split(' ').filter(Boolean);
|
|
74
84
|
const desiredVersion = normalizedTarget.includes('5 4')
|
|
75
85
|
? '5-4'
|
|
76
|
-
: normalizedTarget.includes('5
|
|
86
|
+
: normalizedTarget.includes('5 5')
|
|
87
|
+
? '5-5'
|
|
88
|
+
: normalizedTarget.includes('5 2')
|
|
77
89
|
? '5-2'
|
|
78
90
|
: normalizedTarget.includes('5 1')
|
|
79
91
|
? '5-1'
|
|
@@ -83,6 +95,17 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
83
95
|
const wantsPro = normalizedTarget.includes(' pro') || normalizedTarget.endsWith(' pro') || normalizedTokens.includes('pro');
|
|
84
96
|
const wantsInstant = normalizedTarget.includes('instant');
|
|
85
97
|
const wantsThinking = normalizedTarget.includes('thinking');
|
|
98
|
+
const isTargetGpt55VisibleAlias = (value) => {
|
|
99
|
+
if (desiredVersion !== '5-5') return false;
|
|
100
|
+
const label = normalizeText(value);
|
|
101
|
+
if (wantsPro) {
|
|
102
|
+
return label.includes('pro') && label.includes('extended') && !label.includes('thinking');
|
|
103
|
+
}
|
|
104
|
+
if (wantsThinking) {
|
|
105
|
+
return label.includes('thinking') && label.includes('heavy') && !label.includes('pro');
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
};
|
|
86
109
|
|
|
87
110
|
const button = document.querySelector(BUTTON_SELECTOR);
|
|
88
111
|
if (!button) {
|
|
@@ -110,13 +133,19 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
110
133
|
};
|
|
111
134
|
|
|
112
135
|
const getButtonLabel = () => (button.textContent ?? '').trim();
|
|
136
|
+
const getComposerModelLabel = () =>
|
|
137
|
+
(document.querySelector(COMPOSER_MODEL_SIGNAL_SELECTOR)?.textContent ?? '').trim();
|
|
138
|
+
const readComposerModelSignal = () => normalizeText(getComposerModelLabel());
|
|
139
|
+
const getResolvedLabel = (fallback) => getComposerModelLabel() || getButtonLabel() || fallback;
|
|
113
140
|
if (MODEL_STRATEGY === 'current') {
|
|
114
|
-
return { status: 'already-selected', label:
|
|
141
|
+
return { status: 'already-selected', label: getResolvedLabel(PRIMARY_LABEL) };
|
|
115
142
|
}
|
|
116
143
|
const buttonMatchesTarget = () => {
|
|
117
144
|
const normalizedLabel = normalizeText(getButtonLabel());
|
|
118
145
|
if (!normalizedLabel) return false;
|
|
146
|
+
if (isTargetGpt55VisibleAlias(normalizedLabel)) return true;
|
|
119
147
|
if (desiredVersion) {
|
|
148
|
+
if (desiredVersion === '5-5' && !normalizedLabel.includes('5 5')) return false;
|
|
120
149
|
if (desiredVersion === '5-4' && !normalizedLabel.includes('5 4')) return false;
|
|
121
150
|
if (desiredVersion === '5-2' && !normalizedLabel.includes('5 2')) return false;
|
|
122
151
|
if (desiredVersion === '5-1' && !normalizedLabel.includes('5 1')) return false;
|
|
@@ -131,9 +160,47 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
131
160
|
if (!wantsThinking && normalizedLabel.includes('thinking')) return false;
|
|
132
161
|
return true;
|
|
133
162
|
};
|
|
163
|
+
const buttonHasGenericLabel = () => {
|
|
164
|
+
const normalizedLabel = normalizeText(getButtonLabel());
|
|
165
|
+
return !normalizedLabel || normalizedLabel === 'chatgpt';
|
|
166
|
+
};
|
|
167
|
+
const composerSignalMatchesTarget = () => {
|
|
168
|
+
const signal = readComposerModelSignal();
|
|
169
|
+
if (!signal) {
|
|
170
|
+
return COMPOSER_SIGNAL_ALLOW_BLANK;
|
|
171
|
+
}
|
|
172
|
+
if (COMPOSER_SIGNAL_EXCLUDES.some((token) => token && signal.includes(token))) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
if (COMPOSER_SIGNAL_INCLUDES.length === 0) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
return COMPOSER_SIGNAL_INCLUDES.some((token) => token && signal.includes(token));
|
|
179
|
+
};
|
|
180
|
+
const activeSelectionMatchesTarget = () => {
|
|
181
|
+
if (buttonMatchesTarget()) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
if (!buttonHasGenericLabel()) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
return composerSignalMatchesTarget();
|
|
188
|
+
};
|
|
189
|
+
const selectionStateChanged = (previousButtonLabel, previousComposerSignal) => {
|
|
190
|
+
const currentButtonLabel = normalizeText(getButtonLabel());
|
|
191
|
+
const currentComposerSignal = readComposerModelSignal();
|
|
192
|
+
if (
|
|
193
|
+
currentButtonLabel &&
|
|
194
|
+
currentButtonLabel !== previousButtonLabel &&
|
|
195
|
+
!buttonHasGenericLabel()
|
|
196
|
+
) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
return currentComposerSignal !== previousComposerSignal;
|
|
200
|
+
};
|
|
134
201
|
|
|
135
|
-
if (
|
|
136
|
-
return { status: 'already-selected', label:
|
|
202
|
+
if (activeSelectionMatchesTarget()) {
|
|
203
|
+
return { status: 'already-selected', label: getResolvedLabel(PRIMARY_LABEL) };
|
|
137
204
|
}
|
|
138
205
|
|
|
139
206
|
let lastPointerClick = 0;
|
|
@@ -160,7 +227,7 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
160
227
|
if (dataSelected === 'true' || selectedStates.includes(dataState)) {
|
|
161
228
|
return true;
|
|
162
229
|
}
|
|
163
|
-
if (node.querySelector('[data-testid*="check"], [role="img"][data-icon="check"], svg[data-icon="check"]')) {
|
|
230
|
+
if (node.querySelector('[data-testid*="check"], [role="img"][data-icon="check"], svg[data-icon="check"], .trailing svg')) {
|
|
164
231
|
return true;
|
|
165
232
|
}
|
|
166
233
|
return false;
|
|
@@ -182,6 +249,12 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
182
249
|
normalizedTestId.includes('gpt-5-2') ||
|
|
183
250
|
normalizedTestId.includes('gpt-5.2') ||
|
|
184
251
|
normalizedTestId.includes('gpt52');
|
|
252
|
+
const has55 =
|
|
253
|
+
normalizedTestId.includes('5-5') ||
|
|
254
|
+
normalizedTestId.includes('5.5') ||
|
|
255
|
+
normalizedTestId.includes('gpt-5-5') ||
|
|
256
|
+
normalizedTestId.includes('gpt-5.5') ||
|
|
257
|
+
normalizedTestId.includes('gpt55');
|
|
185
258
|
const has54 =
|
|
186
259
|
normalizedTestId.includes('5-4') ||
|
|
187
260
|
normalizedTestId.includes('5.4') ||
|
|
@@ -200,7 +273,7 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
200
273
|
normalizedTestId.includes('gpt-5-0') ||
|
|
201
274
|
normalizedTestId.includes('gpt-5.0') ||
|
|
202
275
|
normalizedTestId.includes('gpt50');
|
|
203
|
-
const candidateVersion = has54 ? '5-4' : has52 ? '5-2' : has51 ? '5-1' : has50 ? '5-0' : null;
|
|
276
|
+
const candidateVersion = has55 ? '5-5' : has54 ? '5-4' : has52 ? '5-2' : has51 ? '5-1' : has50 ? '5-0' : null;
|
|
204
277
|
// If a candidate advertises a different version, ignore it entirely.
|
|
205
278
|
if (candidateVersion && candidateVersion !== desiredVersion) {
|
|
206
279
|
return 0;
|
|
@@ -227,6 +300,20 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
227
300
|
}
|
|
228
301
|
}
|
|
229
302
|
}
|
|
303
|
+
const candidateGpt55VisibleAlias = isTargetGpt55VisibleAlias(normalizedText);
|
|
304
|
+
if (desiredVersion === '5-5' && normalizedText && !candidateGpt55VisibleAlias) {
|
|
305
|
+
const candidateHasVersion =
|
|
306
|
+
normalizedText.includes('5 5') ||
|
|
307
|
+
normalizedText.includes('gpt55') ||
|
|
308
|
+
normalizedText.includes('gpt 5 5');
|
|
309
|
+
const versionLikeLabel = /(?:^|\\s)5\\s+[0-9](?:\\s|$)/.test(normalizedText) || normalizedText.includes('gpt');
|
|
310
|
+
if (versionLikeLabel && !candidateHasVersion) {
|
|
311
|
+
return 0;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (candidateGpt55VisibleAlias) {
|
|
315
|
+
score += 900;
|
|
316
|
+
}
|
|
230
317
|
if (normalizedText && normalizedTarget) {
|
|
231
318
|
if (normalizedText === normalizedTarget) {
|
|
232
319
|
score += 500;
|
|
@@ -301,6 +388,24 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
301
388
|
}
|
|
302
389
|
return bestMatch;
|
|
303
390
|
};
|
|
391
|
+
const waitForTargetSelection = (previousButtonLabel, previousComposerSignal) => new Promise((resolve) => {
|
|
392
|
+
const waitStart = performance.now();
|
|
393
|
+
const check = () => {
|
|
394
|
+
if (
|
|
395
|
+
activeSelectionMatchesTarget() ||
|
|
396
|
+
selectionStateChanged(previousButtonLabel, previousComposerSignal)
|
|
397
|
+
) {
|
|
398
|
+
resolve(true);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (performance.now() - waitStart > SETTLE_WAIT_MS) {
|
|
402
|
+
resolve(false);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
setTimeout(check, 100);
|
|
406
|
+
};
|
|
407
|
+
check();
|
|
408
|
+
});
|
|
304
409
|
|
|
305
410
|
return new Promise((resolve) => {
|
|
306
411
|
const start = performance.now();
|
|
@@ -345,11 +450,13 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
345
450
|
ensureMenuOpen();
|
|
346
451
|
const match = findBestOption();
|
|
347
452
|
if (match) {
|
|
348
|
-
if (optionIsSelected(match.node)) {
|
|
453
|
+
if (optionIsSelected(match.node) || activeSelectionMatchesTarget()) {
|
|
349
454
|
closeMenu();
|
|
350
|
-
resolve({ status: 'already-selected', label:
|
|
455
|
+
resolve({ status: 'already-selected', label: getResolvedLabel(match.label) });
|
|
351
456
|
return;
|
|
352
457
|
}
|
|
458
|
+
const previousButtonLabel = normalizeText(getButtonLabel());
|
|
459
|
+
const previousComposerSignal = readComposerModelSignal();
|
|
353
460
|
dispatchClickSequence(match.node);
|
|
354
461
|
// Submenus (e.g. "Legacy models") need a second pass to pick the actual model option.
|
|
355
462
|
// Keep scanning once the submenu opens instead of treating the submenu click as a final switch.
|
|
@@ -358,15 +465,15 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
358
465
|
setTimeout(attempt, REOPEN_INTERVAL_MS / 2);
|
|
359
466
|
return;
|
|
360
467
|
}
|
|
361
|
-
// Wait for the
|
|
362
|
-
|
|
363
|
-
if (
|
|
468
|
+
// Wait for the selected model signal to settle before reopening the picker.
|
|
469
|
+
waitForTargetSelection(previousButtonLabel, previousComposerSignal).then((selectionSettled) => {
|
|
470
|
+
if (selectionSettled) {
|
|
364
471
|
closeMenu();
|
|
365
|
-
resolve({ status: 'switched', label:
|
|
472
|
+
resolve({ status: 'switched', label: getResolvedLabel(match.label) });
|
|
366
473
|
return;
|
|
367
474
|
}
|
|
368
475
|
attempt();
|
|
369
|
-
}
|
|
476
|
+
});
|
|
370
477
|
return;
|
|
371
478
|
}
|
|
372
479
|
if (performance.now() - start > MAX_WAIT_MS) {
|
|
@@ -385,6 +492,27 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
385
492
|
export function buildModelMatchersLiteralForTest(targetModel) {
|
|
386
493
|
return buildModelMatchersLiteral(targetModel);
|
|
387
494
|
}
|
|
495
|
+
function buildComposerSignalMatchers(targetModel) {
|
|
496
|
+
const normalized = targetModel
|
|
497
|
+
.trim()
|
|
498
|
+
.toLowerCase()
|
|
499
|
+
.replace(/[^a-z0-9]+/g, " ")
|
|
500
|
+
.replace(/\s+/g, " ")
|
|
501
|
+
.trim();
|
|
502
|
+
if (normalized.includes("pro")) {
|
|
503
|
+
return { includesAny: ["pro"], excludesAny: ["thinking"], allowBlank: false };
|
|
504
|
+
}
|
|
505
|
+
if (normalized.includes("thinking")) {
|
|
506
|
+
return { includesAny: ["thinking"], excludesAny: ["pro"], allowBlank: false };
|
|
507
|
+
}
|
|
508
|
+
if (normalized.includes("instant")) {
|
|
509
|
+
return { includesAny: [], excludesAny: ["thinking", "pro"], allowBlank: true };
|
|
510
|
+
}
|
|
511
|
+
return { includesAny: [], excludesAny: ["thinking", "pro"], allowBlank: true };
|
|
512
|
+
}
|
|
513
|
+
export function buildComposerSignalMatchersForTest(targetModel) {
|
|
514
|
+
return buildComposerSignalMatchers(targetModel);
|
|
515
|
+
}
|
|
388
516
|
function buildModelMatchersLiteral(targetModel) {
|
|
389
517
|
const base = targetModel.trim().toLowerCase();
|
|
390
518
|
const labelTokens = new Set();
|
|
@@ -396,115 +524,145 @@ function buildModelMatchersLiteral(targetModel) {
|
|
|
396
524
|
}
|
|
397
525
|
};
|
|
398
526
|
push(base, labelTokens);
|
|
399
|
-
push(base.replace(/\s+/g,
|
|
400
|
-
const collapsed = base.replace(/\s+/g,
|
|
527
|
+
push(base.replace(/\s+/g, " "), labelTokens);
|
|
528
|
+
const collapsed = base.replace(/\s+/g, "");
|
|
401
529
|
push(collapsed, labelTokens);
|
|
402
|
-
const dotless = base.replace(/[.]/g,
|
|
530
|
+
const dotless = base.replace(/[.]/g, "");
|
|
403
531
|
push(dotless, labelTokens);
|
|
404
532
|
push(`chatgpt ${base}`, labelTokens);
|
|
405
533
|
push(`chatgpt ${dotless}`, labelTokens);
|
|
406
534
|
push(`gpt ${base}`, labelTokens);
|
|
407
535
|
push(`gpt ${dotless}`, labelTokens);
|
|
536
|
+
// Numeric variations (5.5 <-> 55 <-> gpt-5-5)
|
|
537
|
+
if (base.includes("5.5") || base.includes("5-5") || base.includes("55")) {
|
|
538
|
+
push("5.5", labelTokens);
|
|
539
|
+
push("gpt-5.5", labelTokens);
|
|
540
|
+
push("gpt5.5", labelTokens);
|
|
541
|
+
push("gpt-5-5", labelTokens);
|
|
542
|
+
push("gpt5-5", labelTokens);
|
|
543
|
+
push("gpt55", labelTokens);
|
|
544
|
+
push("chatgpt 5.5", labelTokens);
|
|
545
|
+
if (base.includes("thinking")) {
|
|
546
|
+
push("thinking heavy", labelTokens);
|
|
547
|
+
push("heavy thinking", labelTokens);
|
|
548
|
+
testIdTokens.add("model-switcher-gpt-5-5-thinking");
|
|
549
|
+
testIdTokens.add("gpt-5-5-thinking");
|
|
550
|
+
testIdTokens.add("gpt-5.5-thinking");
|
|
551
|
+
}
|
|
552
|
+
if (!base.includes("pro") && !base.includes("thinking")) {
|
|
553
|
+
testIdTokens.add("model-switcher-gpt-5-5");
|
|
554
|
+
}
|
|
555
|
+
testIdTokens.add("gpt-5-5");
|
|
556
|
+
testIdTokens.add("gpt5-5");
|
|
557
|
+
testIdTokens.add("gpt55");
|
|
558
|
+
}
|
|
408
559
|
// Numeric variations (5.4 ↔ 54 ↔ gpt-5-4)
|
|
409
|
-
if (base.includes(
|
|
410
|
-
push(
|
|
411
|
-
push(
|
|
412
|
-
push(
|
|
413
|
-
push(
|
|
414
|
-
push(
|
|
415
|
-
push(
|
|
416
|
-
push(
|
|
417
|
-
if (!base.includes(
|
|
418
|
-
testIdTokens.add(
|
|
419
|
-
}
|
|
420
|
-
testIdTokens.add(
|
|
421
|
-
testIdTokens.add(
|
|
422
|
-
testIdTokens.add(
|
|
560
|
+
if (base.includes("5.4") || base.includes("5-4") || base.includes("54")) {
|
|
561
|
+
push("5.4", labelTokens);
|
|
562
|
+
push("gpt-5.4", labelTokens);
|
|
563
|
+
push("gpt5.4", labelTokens);
|
|
564
|
+
push("gpt-5-4", labelTokens);
|
|
565
|
+
push("gpt5-4", labelTokens);
|
|
566
|
+
push("gpt54", labelTokens);
|
|
567
|
+
push("chatgpt 5.4", labelTokens);
|
|
568
|
+
if (!base.includes("pro")) {
|
|
569
|
+
testIdTokens.add("model-switcher-gpt-5-4");
|
|
570
|
+
}
|
|
571
|
+
testIdTokens.add("gpt-5-4");
|
|
572
|
+
testIdTokens.add("gpt5-4");
|
|
573
|
+
testIdTokens.add("gpt54");
|
|
423
574
|
}
|
|
424
575
|
// Numeric variations (5.1 ↔ 51 ↔ gpt-5-1)
|
|
425
|
-
if (base.includes(
|
|
426
|
-
push(
|
|
427
|
-
push(
|
|
428
|
-
push(
|
|
429
|
-
push(
|
|
430
|
-
push(
|
|
431
|
-
push(
|
|
432
|
-
push(
|
|
433
|
-
testIdTokens.add(
|
|
434
|
-
testIdTokens.add(
|
|
435
|
-
testIdTokens.add(
|
|
576
|
+
if (base.includes("5.1") || base.includes("5-1") || base.includes("51")) {
|
|
577
|
+
push("5.1", labelTokens);
|
|
578
|
+
push("gpt-5.1", labelTokens);
|
|
579
|
+
push("gpt5.1", labelTokens);
|
|
580
|
+
push("gpt-5-1", labelTokens);
|
|
581
|
+
push("gpt5-1", labelTokens);
|
|
582
|
+
push("gpt51", labelTokens);
|
|
583
|
+
push("chatgpt 5.1", labelTokens);
|
|
584
|
+
testIdTokens.add("gpt-5-1");
|
|
585
|
+
testIdTokens.add("gpt5-1");
|
|
586
|
+
testIdTokens.add("gpt51");
|
|
436
587
|
}
|
|
437
588
|
// Numeric variations (5.0 ↔ 50 ↔ gpt-5-0)
|
|
438
|
-
if (base.includes(
|
|
439
|
-
push(
|
|
440
|
-
push(
|
|
441
|
-
push(
|
|
442
|
-
push(
|
|
443
|
-
push(
|
|
444
|
-
push(
|
|
445
|
-
push(
|
|
446
|
-
testIdTokens.add(
|
|
447
|
-
testIdTokens.add(
|
|
448
|
-
testIdTokens.add(
|
|
589
|
+
if (base.includes("5.0") || base.includes("5-0") || base.includes("50")) {
|
|
590
|
+
push("5.0", labelTokens);
|
|
591
|
+
push("gpt-5.0", labelTokens);
|
|
592
|
+
push("gpt5.0", labelTokens);
|
|
593
|
+
push("gpt-5-0", labelTokens);
|
|
594
|
+
push("gpt5-0", labelTokens);
|
|
595
|
+
push("gpt50", labelTokens);
|
|
596
|
+
push("chatgpt 5.0", labelTokens);
|
|
597
|
+
testIdTokens.add("gpt-5-0");
|
|
598
|
+
testIdTokens.add("gpt5-0");
|
|
599
|
+
testIdTokens.add("gpt50");
|
|
449
600
|
}
|
|
450
601
|
// Numeric variations (5.2 ↔ 52 ↔ gpt-5-2)
|
|
451
|
-
if (base.includes(
|
|
452
|
-
push(
|
|
453
|
-
push(
|
|
454
|
-
push(
|
|
455
|
-
push(
|
|
456
|
-
push(
|
|
457
|
-
push(
|
|
458
|
-
push(
|
|
602
|
+
if (base.includes("5.2") || base.includes("5-2") || base.includes("52")) {
|
|
603
|
+
push("5.2", labelTokens);
|
|
604
|
+
push("gpt-5.2", labelTokens);
|
|
605
|
+
push("gpt5.2", labelTokens);
|
|
606
|
+
push("gpt-5-2", labelTokens);
|
|
607
|
+
push("gpt5-2", labelTokens);
|
|
608
|
+
push("gpt52", labelTokens);
|
|
609
|
+
push("chatgpt 5.2", labelTokens);
|
|
459
610
|
// Thinking variant: explicit testid for "Thinking" picker option
|
|
460
|
-
if (base.includes(
|
|
461
|
-
push(
|
|
462
|
-
testIdTokens.add(
|
|
463
|
-
testIdTokens.add(
|
|
464
|
-
testIdTokens.add(
|
|
611
|
+
if (base.includes("thinking")) {
|
|
612
|
+
push("thinking", labelTokens);
|
|
613
|
+
testIdTokens.add("model-switcher-gpt-5-2-thinking");
|
|
614
|
+
testIdTokens.add("gpt-5-2-thinking");
|
|
615
|
+
testIdTokens.add("gpt-5.2-thinking");
|
|
465
616
|
}
|
|
466
617
|
// Instant variant: explicit testid for "Instant" picker option
|
|
467
|
-
if (base.includes(
|
|
468
|
-
push(
|
|
469
|
-
testIdTokens.add(
|
|
470
|
-
testIdTokens.add(
|
|
471
|
-
testIdTokens.add(
|
|
618
|
+
if (base.includes("instant")) {
|
|
619
|
+
push("instant", labelTokens);
|
|
620
|
+
testIdTokens.add("model-switcher-gpt-5-2-instant");
|
|
621
|
+
testIdTokens.add("gpt-5-2-instant");
|
|
622
|
+
testIdTokens.add("gpt-5.2-instant");
|
|
472
623
|
}
|
|
473
624
|
// Base 5.2 testids (for "Auto" mode when no suffix specified)
|
|
474
|
-
if (!base.includes(
|
|
475
|
-
testIdTokens.add(
|
|
625
|
+
if (!base.includes("thinking") && !base.includes("instant") && !base.includes("pro")) {
|
|
626
|
+
testIdTokens.add("model-switcher-gpt-5-2");
|
|
476
627
|
}
|
|
477
|
-
testIdTokens.add(
|
|
478
|
-
testIdTokens.add(
|
|
479
|
-
testIdTokens.add(
|
|
628
|
+
testIdTokens.add("gpt-5-2");
|
|
629
|
+
testIdTokens.add("gpt5-2");
|
|
630
|
+
testIdTokens.add("gpt52");
|
|
480
631
|
}
|
|
481
632
|
// Pro / research variants
|
|
482
|
-
if (base.includes(
|
|
483
|
-
push(
|
|
484
|
-
push(
|
|
485
|
-
push(
|
|
486
|
-
if (base.includes(
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
testIdTokens.add(
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
testIdTokens.add(
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
testIdTokens.add(
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
testIdTokens.add(
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
633
|
+
if (base.includes("pro")) {
|
|
634
|
+
push("proresearch", labelTokens);
|
|
635
|
+
push("research grade", labelTokens);
|
|
636
|
+
push("advanced reasoning", labelTokens);
|
|
637
|
+
if (base.includes("5.5") || base.includes("5-5") || base.includes("55")) {
|
|
638
|
+
push("pro extended", labelTokens);
|
|
639
|
+
push("extended pro", labelTokens);
|
|
640
|
+
testIdTokens.add("gpt-5.5-pro");
|
|
641
|
+
testIdTokens.add("gpt-5-5-pro");
|
|
642
|
+
testIdTokens.add("gpt55pro");
|
|
643
|
+
}
|
|
644
|
+
if (base.includes("5.4") || base.includes("5-4") || base.includes("54")) {
|
|
645
|
+
testIdTokens.add("gpt-5.4-pro");
|
|
646
|
+
testIdTokens.add("gpt-5-4-pro");
|
|
647
|
+
testIdTokens.add("gpt54pro");
|
|
648
|
+
}
|
|
649
|
+
if (base.includes("5.1") || base.includes("5-1") || base.includes("51")) {
|
|
650
|
+
testIdTokens.add("gpt-5.1-pro");
|
|
651
|
+
testIdTokens.add("gpt-5-1-pro");
|
|
652
|
+
testIdTokens.add("gpt51pro");
|
|
653
|
+
}
|
|
654
|
+
if (base.includes("5.0") || base.includes("5-0") || base.includes("50")) {
|
|
655
|
+
testIdTokens.add("gpt-5.0-pro");
|
|
656
|
+
testIdTokens.add("gpt-5-0-pro");
|
|
657
|
+
testIdTokens.add("gpt50pro");
|
|
658
|
+
}
|
|
659
|
+
if (base.includes("5.2") || base.includes("5-2") || base.includes("52")) {
|
|
660
|
+
testIdTokens.add("gpt-5.2-pro");
|
|
661
|
+
testIdTokens.add("gpt-5-2-pro");
|
|
662
|
+
testIdTokens.add("gpt52pro");
|
|
663
|
+
}
|
|
664
|
+
testIdTokens.add("pro");
|
|
665
|
+
testIdTokens.add("proresearch");
|
|
508
666
|
}
|
|
509
667
|
base
|
|
510
668
|
.split(/\s+/)
|
|
@@ -513,7 +671,7 @@ function buildModelMatchersLiteral(targetModel) {
|
|
|
513
671
|
.forEach((token) => {
|
|
514
672
|
push(token, labelTokens);
|
|
515
673
|
});
|
|
516
|
-
const hyphenated = base.replace(/\s+/g,
|
|
674
|
+
const hyphenated = base.replace(/\s+/g, "-");
|
|
517
675
|
push(hyphenated, testIdTokens);
|
|
518
676
|
push(collapsed, testIdTokens);
|
|
519
677
|
push(dotless, testIdTokens);
|
|
@@ -525,7 +683,7 @@ function buildModelMatchersLiteral(targetModel) {
|
|
|
525
683
|
labelTokens.add(base);
|
|
526
684
|
}
|
|
527
685
|
if (!testIdTokens.size) {
|
|
528
|
-
testIdTokens.add(base.replace(/\s+/g,
|
|
686
|
+
testIdTokens.add(base.replace(/\s+/g, "-"));
|
|
529
687
|
}
|
|
530
688
|
return {
|
|
531
689
|
labelTokens: Array.from(labelTokens).filter(Boolean),
|
|
@@ -533,5 +691,5 @@ function buildModelMatchersLiteral(targetModel) {
|
|
|
533
691
|
};
|
|
534
692
|
}
|
|
535
693
|
export function buildModelSelectionExpressionForTest(targetModel) {
|
|
536
|
-
return buildModelSelectionExpression(targetModel,
|
|
694
|
+
return buildModelSelectionExpression(targetModel, "select");
|
|
537
695
|
}
|