@steipete/oracle 0.8.6 → 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 +130 -45
- package/dist/bin/oracle-cli.js +613 -379
- 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 +314 -104
- package/dist/src/browser/actions/navigation.js +161 -136
- 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 +452 -303
- 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 +17 -0
- package/dist/src/browser/providers/chatgptDomProvider.js +49 -0
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +254 -0
- package/dist/src/browser/providers/index.js +2 -0
- 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 +65 -45
- 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 +7 -4
- 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 +11 -0
- 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 +12 -8
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +145 -87
- 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 +37 -25
- package/dist/src/cli/sessionCommand.js +31 -21
- package/dist/src/cli/sessionDisplay.js +182 -79
- package/dist/src/cli/sessionLineage.js +60 -0
- package/dist/src/cli/sessionRunner.js +118 -90
- package/dist/src/cli/sessionTable.js +28 -24
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +140 -127
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +80 -0
- package/dist/src/gemini-web/client.js +81 -64
- package/dist/src/gemini-web/executionMode.js +16 -0
- package/dist/src/gemini-web/executor.js +327 -169
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/server.js +16 -12
- package/dist/src/mcp/tools/consult.js +81 -64
- 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 +84 -46
- package/dist/src/oracle/config.js +124 -58
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +69 -45
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -30
- 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 +23 -15
- package/dist/src/oracle/run.js +172 -140
- 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 +81 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +69 -65
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- 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/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
- /package/dist/{oracle/src/browser/types.js → src/gemini-web/executionClients.js} +0 -0
|
@@ -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 '';
|
|
@@ -71,30 +81,72 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
71
81
|
.map((token) => normalizeText(token))
|
|
72
82
|
.filter(Boolean);
|
|
73
83
|
const targetWords = normalizedTarget.split(' ').filter(Boolean);
|
|
74
|
-
const desiredVersion = normalizedTarget.includes('5
|
|
75
|
-
? '5-
|
|
76
|
-
: normalizedTarget.includes('5
|
|
77
|
-
? '5-
|
|
78
|
-
: normalizedTarget.includes('5
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
const desiredVersion = normalizedTarget.includes('5 4')
|
|
85
|
+
? '5-4'
|
|
86
|
+
: normalizedTarget.includes('5 5')
|
|
87
|
+
? '5-5'
|
|
88
|
+
: normalizedTarget.includes('5 2')
|
|
89
|
+
? '5-2'
|
|
90
|
+
: normalizedTarget.includes('5 1')
|
|
91
|
+
? '5-1'
|
|
92
|
+
: normalizedTarget.includes('5 0')
|
|
93
|
+
? '5-0'
|
|
94
|
+
: null;
|
|
81
95
|
const wantsPro = normalizedTarget.includes(' pro') || normalizedTarget.endsWith(' pro') || normalizedTokens.includes('pro');
|
|
82
96
|
const wantsInstant = normalizedTarget.includes('instant');
|
|
83
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
|
+
};
|
|
84
109
|
|
|
85
110
|
const button = document.querySelector(BUTTON_SELECTOR);
|
|
86
111
|
if (!button) {
|
|
87
112
|
return { status: 'button-missing' };
|
|
88
113
|
}
|
|
89
114
|
|
|
115
|
+
const closeMenu = () => {
|
|
116
|
+
try {
|
|
117
|
+
if (dispatchClickSequence(button)) {
|
|
118
|
+
lastPointerClick = performance.now();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
} catch {}
|
|
122
|
+
try {
|
|
123
|
+
document.dispatchEvent(
|
|
124
|
+
new KeyboardEvent('keydown', {
|
|
125
|
+
key: 'Escape',
|
|
126
|
+
code: 'Escape',
|
|
127
|
+
keyCode: 27,
|
|
128
|
+
which: 27,
|
|
129
|
+
bubbles: true,
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
} catch {}
|
|
133
|
+
};
|
|
134
|
+
|
|
90
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;
|
|
91
140
|
if (MODEL_STRATEGY === 'current') {
|
|
92
|
-
return { status: 'already-selected', label:
|
|
141
|
+
return { status: 'already-selected', label: getResolvedLabel(PRIMARY_LABEL) };
|
|
93
142
|
}
|
|
94
143
|
const buttonMatchesTarget = () => {
|
|
95
144
|
const normalizedLabel = normalizeText(getButtonLabel());
|
|
96
145
|
if (!normalizedLabel) return false;
|
|
146
|
+
if (isTargetGpt55VisibleAlias(normalizedLabel)) return true;
|
|
97
147
|
if (desiredVersion) {
|
|
148
|
+
if (desiredVersion === '5-5' && !normalizedLabel.includes('5 5')) return false;
|
|
149
|
+
if (desiredVersion === '5-4' && !normalizedLabel.includes('5 4')) return false;
|
|
98
150
|
if (desiredVersion === '5-2' && !normalizedLabel.includes('5 2')) return false;
|
|
99
151
|
if (desiredVersion === '5-1' && !normalizedLabel.includes('5 1')) return false;
|
|
100
152
|
if (desiredVersion === '5-0' && !normalizedLabel.includes('5 0')) return false;
|
|
@@ -108,9 +160,47 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
108
160
|
if (!wantsThinking && normalizedLabel.includes('thinking')) return false;
|
|
109
161
|
return true;
|
|
110
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
|
+
};
|
|
111
201
|
|
|
112
|
-
if (
|
|
113
|
-
return { status: 'already-selected', label:
|
|
202
|
+
if (activeSelectionMatchesTarget()) {
|
|
203
|
+
return { status: 'already-selected', label: getResolvedLabel(PRIMARY_LABEL) };
|
|
114
204
|
}
|
|
115
205
|
|
|
116
206
|
let lastPointerClick = 0;
|
|
@@ -137,7 +227,7 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
137
227
|
if (dataSelected === 'true' || selectedStates.includes(dataState)) {
|
|
138
228
|
return true;
|
|
139
229
|
}
|
|
140
|
-
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')) {
|
|
141
231
|
return true;
|
|
142
232
|
}
|
|
143
233
|
return false;
|
|
@@ -159,6 +249,18 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
159
249
|
normalizedTestId.includes('gpt-5-2') ||
|
|
160
250
|
normalizedTestId.includes('gpt-5.2') ||
|
|
161
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');
|
|
258
|
+
const has54 =
|
|
259
|
+
normalizedTestId.includes('5-4') ||
|
|
260
|
+
normalizedTestId.includes('5.4') ||
|
|
261
|
+
normalizedTestId.includes('gpt-5-4') ||
|
|
262
|
+
normalizedTestId.includes('gpt-5.4') ||
|
|
263
|
+
normalizedTestId.includes('gpt54');
|
|
162
264
|
const has51 =
|
|
163
265
|
normalizedTestId.includes('5-1') ||
|
|
164
266
|
normalizedTestId.includes('5.1') ||
|
|
@@ -171,7 +273,7 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
171
273
|
normalizedTestId.includes('gpt-5-0') ||
|
|
172
274
|
normalizedTestId.includes('gpt-5.0') ||
|
|
173
275
|
normalizedTestId.includes('gpt50');
|
|
174
|
-
const candidateVersion = 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;
|
|
175
277
|
// If a candidate advertises a different version, ignore it entirely.
|
|
176
278
|
if (candidateVersion && candidateVersion !== desiredVersion) {
|
|
177
279
|
return 0;
|
|
@@ -198,6 +300,20 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
198
300
|
}
|
|
199
301
|
}
|
|
200
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
|
+
}
|
|
201
317
|
if (normalizedText && normalizedTarget) {
|
|
202
318
|
if (normalizedText === normalizedTarget) {
|
|
203
319
|
score += 500;
|
|
@@ -272,6 +388,24 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
272
388
|
}
|
|
273
389
|
return bestMatch;
|
|
274
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
|
+
});
|
|
275
409
|
|
|
276
410
|
return new Promise((resolve) => {
|
|
277
411
|
const start = performance.now();
|
|
@@ -316,10 +450,13 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
316
450
|
ensureMenuOpen();
|
|
317
451
|
const match = findBestOption();
|
|
318
452
|
if (match) {
|
|
319
|
-
if (optionIsSelected(match.node)) {
|
|
320
|
-
|
|
453
|
+
if (optionIsSelected(match.node) || activeSelectionMatchesTarget()) {
|
|
454
|
+
closeMenu();
|
|
455
|
+
resolve({ status: 'already-selected', label: getResolvedLabel(match.label) });
|
|
321
456
|
return;
|
|
322
457
|
}
|
|
458
|
+
const previousButtonLabel = normalizeText(getButtonLabel());
|
|
459
|
+
const previousComposerSignal = readComposerModelSignal();
|
|
323
460
|
dispatchClickSequence(match.node);
|
|
324
461
|
// Submenus (e.g. "Legacy models") need a second pass to pick the actual model option.
|
|
325
462
|
// Keep scanning once the submenu opens instead of treating the submenu click as a final switch.
|
|
@@ -328,14 +465,15 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
328
465
|
setTimeout(attempt, REOPEN_INTERVAL_MS / 2);
|
|
329
466
|
return;
|
|
330
467
|
}
|
|
331
|
-
// Wait for the
|
|
332
|
-
|
|
333
|
-
if (
|
|
334
|
-
|
|
468
|
+
// Wait for the selected model signal to settle before reopening the picker.
|
|
469
|
+
waitForTargetSelection(previousButtonLabel, previousComposerSignal).then((selectionSettled) => {
|
|
470
|
+
if (selectionSettled) {
|
|
471
|
+
closeMenu();
|
|
472
|
+
resolve({ status: 'switched', label: getResolvedLabel(match.label) });
|
|
335
473
|
return;
|
|
336
474
|
}
|
|
337
475
|
attempt();
|
|
338
|
-
}
|
|
476
|
+
});
|
|
339
477
|
return;
|
|
340
478
|
}
|
|
341
479
|
if (performance.now() - start > MAX_WAIT_MS) {
|
|
@@ -354,6 +492,27 @@ function buildModelSelectionExpression(targetModel, strategy) {
|
|
|
354
492
|
export function buildModelMatchersLiteralForTest(targetModel) {
|
|
355
493
|
return buildModelMatchersLiteral(targetModel);
|
|
356
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
|
+
}
|
|
357
516
|
function buildModelMatchersLiteral(targetModel) {
|
|
358
517
|
const base = targetModel.trim().toLowerCase();
|
|
359
518
|
const labelTokens = new Set();
|
|
@@ -365,94 +524,145 @@ function buildModelMatchersLiteral(targetModel) {
|
|
|
365
524
|
}
|
|
366
525
|
};
|
|
367
526
|
push(base, labelTokens);
|
|
368
|
-
push(base.replace(/\s+/g,
|
|
369
|
-
const collapsed = base.replace(/\s+/g,
|
|
527
|
+
push(base.replace(/\s+/g, " "), labelTokens);
|
|
528
|
+
const collapsed = base.replace(/\s+/g, "");
|
|
370
529
|
push(collapsed, labelTokens);
|
|
371
|
-
const dotless = base.replace(/[.]/g,
|
|
530
|
+
const dotless = base.replace(/[.]/g, "");
|
|
372
531
|
push(dotless, labelTokens);
|
|
373
532
|
push(`chatgpt ${base}`, labelTokens);
|
|
374
533
|
push(`chatgpt ${dotless}`, labelTokens);
|
|
375
534
|
push(`gpt ${base}`, labelTokens);
|
|
376
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
|
+
}
|
|
559
|
+
// Numeric variations (5.4 ↔ 54 ↔ gpt-5-4)
|
|
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");
|
|
574
|
+
}
|
|
377
575
|
// Numeric variations (5.1 ↔ 51 ↔ gpt-5-1)
|
|
378
|
-
if (base.includes(
|
|
379
|
-
push(
|
|
380
|
-
push(
|
|
381
|
-
push(
|
|
382
|
-
push(
|
|
383
|
-
push(
|
|
384
|
-
push(
|
|
385
|
-
push(
|
|
386
|
-
testIdTokens.add(
|
|
387
|
-
testIdTokens.add(
|
|
388
|
-
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");
|
|
389
587
|
}
|
|
390
588
|
// Numeric variations (5.0 ↔ 50 ↔ gpt-5-0)
|
|
391
|
-
if (base.includes(
|
|
392
|
-
push(
|
|
393
|
-
push(
|
|
394
|
-
push(
|
|
395
|
-
push(
|
|
396
|
-
push(
|
|
397
|
-
push(
|
|
398
|
-
push(
|
|
399
|
-
testIdTokens.add(
|
|
400
|
-
testIdTokens.add(
|
|
401
|
-
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");
|
|
402
600
|
}
|
|
403
601
|
// Numeric variations (5.2 ↔ 52 ↔ gpt-5-2)
|
|
404
|
-
if (base.includes(
|
|
405
|
-
push(
|
|
406
|
-
push(
|
|
407
|
-
push(
|
|
408
|
-
push(
|
|
409
|
-
push(
|
|
410
|
-
push(
|
|
411
|
-
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);
|
|
412
610
|
// Thinking variant: explicit testid for "Thinking" picker option
|
|
413
|
-
if (base.includes(
|
|
414
|
-
push(
|
|
415
|
-
testIdTokens.add(
|
|
416
|
-
testIdTokens.add(
|
|
417
|
-
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");
|
|
418
616
|
}
|
|
419
617
|
// Instant variant: explicit testid for "Instant" picker option
|
|
420
|
-
if (base.includes(
|
|
421
|
-
push(
|
|
422
|
-
testIdTokens.add(
|
|
423
|
-
testIdTokens.add(
|
|
424
|
-
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");
|
|
425
623
|
}
|
|
426
624
|
// Base 5.2 testids (for "Auto" mode when no suffix specified)
|
|
427
|
-
if (!base.includes(
|
|
428
|
-
testIdTokens.add(
|
|
625
|
+
if (!base.includes("thinking") && !base.includes("instant") && !base.includes("pro")) {
|
|
626
|
+
testIdTokens.add("model-switcher-gpt-5-2");
|
|
429
627
|
}
|
|
430
|
-
testIdTokens.add(
|
|
431
|
-
testIdTokens.add(
|
|
432
|
-
testIdTokens.add(
|
|
628
|
+
testIdTokens.add("gpt-5-2");
|
|
629
|
+
testIdTokens.add("gpt5-2");
|
|
630
|
+
testIdTokens.add("gpt52");
|
|
433
631
|
}
|
|
434
632
|
// Pro / research variants
|
|
435
|
-
if (base.includes(
|
|
436
|
-
push(
|
|
437
|
-
push(
|
|
438
|
-
push(
|
|
439
|
-
if (base.includes(
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
testIdTokens.add(
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
testIdTokens.add(
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
testIdTokens.add(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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");
|
|
456
666
|
}
|
|
457
667
|
base
|
|
458
668
|
.split(/\s+/)
|
|
@@ -461,7 +671,7 @@ function buildModelMatchersLiteral(targetModel) {
|
|
|
461
671
|
.forEach((token) => {
|
|
462
672
|
push(token, labelTokens);
|
|
463
673
|
});
|
|
464
|
-
const hyphenated = base.replace(/\s+/g,
|
|
674
|
+
const hyphenated = base.replace(/\s+/g, "-");
|
|
465
675
|
push(hyphenated, testIdTokens);
|
|
466
676
|
push(collapsed, testIdTokens);
|
|
467
677
|
push(dotless, testIdTokens);
|
|
@@ -473,7 +683,7 @@ function buildModelMatchersLiteral(targetModel) {
|
|
|
473
683
|
labelTokens.add(base);
|
|
474
684
|
}
|
|
475
685
|
if (!testIdTokens.size) {
|
|
476
|
-
testIdTokens.add(base.replace(/\s+/g,
|
|
686
|
+
testIdTokens.add(base.replace(/\s+/g, "-"));
|
|
477
687
|
}
|
|
478
688
|
return {
|
|
479
689
|
labelTokens: Array.from(labelTokens).filter(Boolean),
|
|
@@ -481,5 +691,5 @@ function buildModelMatchersLiteral(targetModel) {
|
|
|
481
691
|
};
|
|
482
692
|
}
|
|
483
693
|
export function buildModelSelectionExpressionForTest(targetModel) {
|
|
484
|
-
return buildModelSelectionExpression(targetModel,
|
|
694
|
+
return buildModelSelectionExpression(targetModel, "select");
|
|
485
695
|
}
|