@steipete/oracle 0.9.0 → 0.11.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 +107 -49
- package/dist/bin/oracle-cli.js +551 -410
- 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/archiveConversation.js +224 -0
- package/dist/src/browser/actions/assistantResponse.js +175 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/deepResearch.js +662 -0
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +342 -119
- package/dist/src/browser/actions/navigation.js +183 -137
- package/dist/src/browser/actions/projectSources.js +491 -0
- package/dist/src/browser/actions/promptComposer.js +152 -91
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingStatus.js +391 -0
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/artifacts.js +150 -0
- package/dist/src/browser/attachRunning.js +31 -0
- package/dist/src/browser/chatgptImages.js +315 -0
- package/dist/src/browser/chromeLifecycle.js +276 -63
- package/dist/src/browser/config.js +59 -16
- package/dist/src/browser/constants.js +25 -12
- package/dist/src/browser/controlPlan.js +81 -0
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +250 -77
- package/dist/src/browser/domDebug.js +50 -1
- package/dist/src/browser/index.js +1559 -692
- package/dist/src/browser/liveTabs.js +434 -0
- 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 +127 -42
- package/dist/src/browser/projectSourcesRunner.js +366 -0
- 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 +178 -73
- package/dist/src/browser/reattachHelpers.js +32 -27
- package/dist/src/browser/sessionRunner.js +89 -25
- package/dist/src/browser/tabLeaseRegistry.js +182 -0
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +24 -20
- 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 +102 -48
- package/dist/src/cli/browserDefaults.js +51 -26
- package/dist/src/cli/browserTabs.js +228 -0
- 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 +62 -26
- package/dist/src/cli/duplicatePromptGuard.js +12 -4
- 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 +131 -106
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/projectSources.js +116 -0
- 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 +82 -21
- package/dist/src/cli/sessionDisplay.js +213 -87
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +149 -95
- 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/consultPresets.js +19 -0
- package/dist/src/mcp/server.js +18 -12
- package/dist/src/mcp/tools/consult.js +246 -67
- package/dist/src/mcp/tools/projectSources.js +123 -0
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +12 -5
- package/dist/src/mcp/utils.js +21 -8
- 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 +160 -135
- 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/projectSources/plan.js +27 -0
- package/dist/src/projectSources/url.js +23 -0
- 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 +78 -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 +67 -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/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 → src/projectSources}/types.js +0 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { formatElapsed } from "../../oracle/format.js";
|
|
2
|
+
import { ASSISTANT_ROLE_SELECTOR, CONVERSATION_TURN_SELECTOR } from "../constants.js";
|
|
3
|
+
const THINKING_STALE_HINT_MS = 10 * 60_000;
|
|
4
|
+
export function startThinkingStatusMonitor(Runtime, logger, options = {}) {
|
|
5
|
+
const intervalMs = resolveThinkingStatusInterval(options.intervalMs);
|
|
6
|
+
if (!intervalMs) {
|
|
7
|
+
return () => { };
|
|
8
|
+
}
|
|
9
|
+
const now = options.now ?? Date.now;
|
|
10
|
+
let stopped = false;
|
|
11
|
+
let pending = false;
|
|
12
|
+
let lastFingerprint = null;
|
|
13
|
+
let lastChangedAt = now();
|
|
14
|
+
const startedAt = now();
|
|
15
|
+
const interval = setInterval(async () => {
|
|
16
|
+
// stop flag flips asynchronously
|
|
17
|
+
if (stopped || pending) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
pending = true;
|
|
21
|
+
try {
|
|
22
|
+
const snapshot = await readThinkingStatus(Runtime);
|
|
23
|
+
if (stopped) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const tickAt = now();
|
|
27
|
+
if (snapshot) {
|
|
28
|
+
const fingerprint = buildThinkingStatusFingerprint(snapshot);
|
|
29
|
+
if (fingerprint !== lastFingerprint) {
|
|
30
|
+
lastFingerprint = fingerprint;
|
|
31
|
+
lastChangedAt = tickAt;
|
|
32
|
+
}
|
|
33
|
+
if (stopped) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
logger(formatThinkingLog(startedAt, tickAt, snapshot, "", tickAt - lastChangedAt));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
logger(formatThinkingWaitingLog(startedAt, tickAt));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// ignore DOM polling errors
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
pending = false;
|
|
47
|
+
}
|
|
48
|
+
}, intervalMs);
|
|
49
|
+
interval.unref?.();
|
|
50
|
+
return () => {
|
|
51
|
+
// multiple callers may race to stop
|
|
52
|
+
if (stopped) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
stopped = true;
|
|
56
|
+
clearInterval(interval);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function formatThinkingLog(startedAt, now, status, locatorSuffix, unchangedMs = 0) {
|
|
60
|
+
const elapsedMs = now - startedAt;
|
|
61
|
+
const elapsedText = formatElapsed(elapsedMs);
|
|
62
|
+
const snapshot = typeof status === "string"
|
|
63
|
+
? { message: sanitizeThinkingText(status) || "active", source: "inline" }
|
|
64
|
+
: status;
|
|
65
|
+
const progress = typeof snapshot.progressPercent === "number" && Number.isFinite(snapshot.progressPercent)
|
|
66
|
+
? `${Math.max(0, Math.min(100, Math.round(snapshot.progressPercent)))}% UI progress`
|
|
67
|
+
: null;
|
|
68
|
+
const prefix = progress
|
|
69
|
+
? `[browser] ChatGPT thinking - ${progress}, ${elapsedText} elapsed`
|
|
70
|
+
: `[browser] ChatGPT thinking - ${elapsedText} elapsed`;
|
|
71
|
+
const statusLabel = snapshot.message ? `; status=${snapshot.message}` : "";
|
|
72
|
+
const changeLabel = unchangedMs > 0 ? `; last change ${formatElapsed(unchangedMs)} ago` : "";
|
|
73
|
+
const staleLabel = unchangedMs >= THINKING_STALE_HINT_MS ? "; stale-hint=no UI progress change" : "";
|
|
74
|
+
const sourceLabel = snapshot.source ? `; source=${snapshot.source}` : "";
|
|
75
|
+
return `${prefix}${statusLabel}${changeLabel}${staleLabel}${sourceLabel}${locatorSuffix}`;
|
|
76
|
+
}
|
|
77
|
+
export function formatThinkingWaitingLog(startedAt, now) {
|
|
78
|
+
return `[browser] Waiting for ChatGPT response - ${formatElapsed(now - startedAt)} elapsed; no thinking status detected yet.`;
|
|
79
|
+
}
|
|
80
|
+
function resolveThinkingStatusInterval(intervalMs) {
|
|
81
|
+
if (intervalMs === 0) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
if (typeof intervalMs === "number" && Number.isFinite(intervalMs) && intervalMs > 0) {
|
|
85
|
+
return Math.max(1000, Math.floor(intervalMs));
|
|
86
|
+
}
|
|
87
|
+
return 30_000;
|
|
88
|
+
}
|
|
89
|
+
function buildThinkingStatusFingerprint(snapshot) {
|
|
90
|
+
return [
|
|
91
|
+
snapshot.source,
|
|
92
|
+
snapshot.message,
|
|
93
|
+
snapshot.progressPercent == null ? "" : Math.round(snapshot.progressPercent),
|
|
94
|
+
snapshot.panelVisible ? "panel" : "",
|
|
95
|
+
].join(":");
|
|
96
|
+
}
|
|
97
|
+
async function readThinkingStatus(Runtime) {
|
|
98
|
+
const expression = buildThinkingStatusExpression();
|
|
99
|
+
const { result } = await Runtime.evaluate({
|
|
100
|
+
expression,
|
|
101
|
+
awaitPromise: true,
|
|
102
|
+
returnByValue: true,
|
|
103
|
+
});
|
|
104
|
+
const value = result.value;
|
|
105
|
+
if (typeof value === "string") {
|
|
106
|
+
const sanitized = sanitizeThinkingText(value);
|
|
107
|
+
return sanitized ? { message: sanitized, source: "inline" } : null;
|
|
108
|
+
}
|
|
109
|
+
if (!value || typeof value !== "object") {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const source = value.source === "sidecar" ? "sidecar" : "inline";
|
|
113
|
+
const message = sanitizeThinkingText(value.message ?? "");
|
|
114
|
+
const progressPercent = typeof value.progressPercent === "number" && Number.isFinite(value.progressPercent)
|
|
115
|
+
? Math.max(0, Math.min(100, value.progressPercent))
|
|
116
|
+
: undefined;
|
|
117
|
+
if (!message && progressPercent == null) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
message: message || "active",
|
|
122
|
+
source,
|
|
123
|
+
progressPercent,
|
|
124
|
+
panelOpened: value.panelOpened === true,
|
|
125
|
+
panelVisible: value.panelVisible === true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const SAFE_THINKING_STATUS_MESSAGES = new Set([
|
|
129
|
+
"active",
|
|
130
|
+
"thinking sidecar active",
|
|
131
|
+
"thinking sidecar opened",
|
|
132
|
+
]);
|
|
133
|
+
export function sanitizeThinkingText(raw) {
|
|
134
|
+
if (!raw) {
|
|
135
|
+
return "";
|
|
136
|
+
}
|
|
137
|
+
const trimmed = raw.replace(/\s+/g, " ").trim();
|
|
138
|
+
const prefixPattern = /^(pro thinking)\s*[•:\-–—]*\s*/i;
|
|
139
|
+
const normalized = prefixPattern.test(trimmed)
|
|
140
|
+
? trimmed.replace(prefixPattern, "").trim()
|
|
141
|
+
: trimmed;
|
|
142
|
+
if (!normalized) {
|
|
143
|
+
return "";
|
|
144
|
+
}
|
|
145
|
+
const normalizedKey = normalized.toLowerCase();
|
|
146
|
+
return SAFE_THINKING_STATUS_MESSAGES.has(normalizedKey) ? normalizedKey : "active";
|
|
147
|
+
}
|
|
148
|
+
function buildThinkingStatusExpression() {
|
|
149
|
+
const conversationLiteral = JSON.stringify(CONVERSATION_TURN_SELECTOR);
|
|
150
|
+
const assistantLiteral = JSON.stringify(ASSISTANT_ROLE_SELECTOR);
|
|
151
|
+
const selectors = [
|
|
152
|
+
"span.loading-shimmer",
|
|
153
|
+
"span.flex.items-center.gap-1.truncate.text-start.align-middle.text-token-text-tertiary",
|
|
154
|
+
'[data-testid*="thinking"]',
|
|
155
|
+
'[data-testid*="reasoning"]',
|
|
156
|
+
'[role="status"]',
|
|
157
|
+
'[aria-live="polite"]',
|
|
158
|
+
];
|
|
159
|
+
const keywords = ["pro thinking", "thinking", "reasoning"];
|
|
160
|
+
const selectorLiteral = JSON.stringify(selectors);
|
|
161
|
+
const keywordsLiteral = JSON.stringify(keywords);
|
|
162
|
+
return `(async () => {
|
|
163
|
+
const CONVERSATION_SELECTOR = ${conversationLiteral};
|
|
164
|
+
const ASSISTANT_SELECTOR = ${assistantLiteral};
|
|
165
|
+
const selectors = ${selectorLiteral};
|
|
166
|
+
const keywords = ${keywordsLiteral};
|
|
167
|
+
const normalize = (value) =>
|
|
168
|
+
String(value || '')
|
|
169
|
+
.normalize('NFD')
|
|
170
|
+
.replace(/[\\u0300-\\u036f]/g, '')
|
|
171
|
+
.toLowerCase()
|
|
172
|
+
.replace(/\\s+/g, ' ')
|
|
173
|
+
.trim();
|
|
174
|
+
const isVisible = (node) => {
|
|
175
|
+
if (!(node instanceof HTMLElement)) return false;
|
|
176
|
+
const rect = node.getBoundingClientRect();
|
|
177
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return false;
|
|
178
|
+
const style = window.getComputedStyle(node);
|
|
179
|
+
if (
|
|
180
|
+
style.display === 'none' ||
|
|
181
|
+
style.visibility === 'hidden' ||
|
|
182
|
+
(style.opacity !== '' && Number(style.opacity) === 0)
|
|
183
|
+
) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
return rect.bottom >= 0 && rect.right >= 0 && rect.top <= window.innerHeight && rect.left <= window.innerWidth;
|
|
187
|
+
};
|
|
188
|
+
const labelFor = (node) =>
|
|
189
|
+
normalize([
|
|
190
|
+
node.textContent,
|
|
191
|
+
node.getAttribute?.('aria-label'),
|
|
192
|
+
node.getAttribute?.('title'),
|
|
193
|
+
node.getAttribute?.('data-testid'),
|
|
194
|
+
].filter(Boolean).join(' '));
|
|
195
|
+
const looksLikeThinking = (node) => {
|
|
196
|
+
const label = labelFor(node);
|
|
197
|
+
return (
|
|
198
|
+
label.includes('thinking') ||
|
|
199
|
+
label.includes('reasoning') ||
|
|
200
|
+
label.includes('pro thinking') ||
|
|
201
|
+
label.includes('myslen') ||
|
|
202
|
+
label.includes('mysl') ||
|
|
203
|
+
label.includes('rozumow')
|
|
204
|
+
);
|
|
205
|
+
};
|
|
206
|
+
const isComposerAdjacent = (node) =>
|
|
207
|
+
Boolean(node.closest?.('[contenteditable="true"], textarea, [data-testid*="composer"], [id*="composer"]'));
|
|
208
|
+
const isAssistantTurn = (node) => {
|
|
209
|
+
if (!(node instanceof HTMLElement)) return false;
|
|
210
|
+
const turnAttr = (node.getAttribute('data-turn') || node.dataset?.turn || '').toLowerCase();
|
|
211
|
+
if (turnAttr === 'assistant') return true;
|
|
212
|
+
const role = (node.getAttribute('data-message-author-role') || node.dataset?.messageAuthorRole || '').toLowerCase();
|
|
213
|
+
if (role === 'assistant') return true;
|
|
214
|
+
const testId = (node.getAttribute('data-testid') || '').toLowerCase();
|
|
215
|
+
if (testId.includes('assistant')) return true;
|
|
216
|
+
return Boolean(node.querySelector(ASSISTANT_SELECTOR) || node.querySelector('[data-testid*="assistant"]'));
|
|
217
|
+
};
|
|
218
|
+
const latestAssistantTurn = () => {
|
|
219
|
+
const turns = Array.from(document.querySelectorAll(CONVERSATION_SELECTOR));
|
|
220
|
+
for (let index = turns.length - 1; index >= 0; index -= 1) {
|
|
221
|
+
if (isAssistantTurn(turns[index])) {
|
|
222
|
+
return turns[index];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
};
|
|
227
|
+
const findThinkingDisclosure = (scope) => {
|
|
228
|
+
const candidates = Array.from(
|
|
229
|
+
scope.querySelectorAll(
|
|
230
|
+
[
|
|
231
|
+
'button',
|
|
232
|
+
'[role="button"]',
|
|
233
|
+
'[aria-expanded]',
|
|
234
|
+
'[data-testid*="thinking"]',
|
|
235
|
+
'[data-testid*="reasoning"]',
|
|
236
|
+
].join(','),
|
|
237
|
+
),
|
|
238
|
+
);
|
|
239
|
+
for (const node of candidates) {
|
|
240
|
+
if (!(node instanceof HTMLElement)) continue;
|
|
241
|
+
if (!isVisible(node) || isComposerAdjacent(node) || !looksLikeThinking(node)) continue;
|
|
242
|
+
if (node.getAttribute('aria-haspopup') === 'menu') continue;
|
|
243
|
+
if (node.dataset?.oracleThinkingProbed === 'true') continue;
|
|
244
|
+
const expanded = normalize(node.getAttribute('aria-expanded'));
|
|
245
|
+
if (expanded !== 'false') {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
return node;
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
};
|
|
252
|
+
const findProgressPercent = (scope) => {
|
|
253
|
+
const progressNodes = Array.from(
|
|
254
|
+
scope.querySelectorAll('progress, [role="progressbar"], [aria-valuenow], [data-testid*="progress"], [class*="progress"]'),
|
|
255
|
+
);
|
|
256
|
+
const readNumeric = (raw) => {
|
|
257
|
+
if (raw == null || raw === '') return null;
|
|
258
|
+
const value = Number(String(raw).replace('%', '').trim());
|
|
259
|
+
return Number.isFinite(value) ? value : null;
|
|
260
|
+
};
|
|
261
|
+
const readStylePercent = (node) => {
|
|
262
|
+
const style = node instanceof HTMLElement ? window.getComputedStyle(node) : null;
|
|
263
|
+
if (!style) return null;
|
|
264
|
+
const widthMatch = String(node.style?.width || style.width || '').match(
|
|
265
|
+
/([0-9]+(?:\\.[0-9]+)?)%/,
|
|
266
|
+
);
|
|
267
|
+
if (widthMatch) return readNumeric(widthMatch[1]);
|
|
268
|
+
const transform = String(style.transform || '');
|
|
269
|
+
const scaleMatch = transform.match(/scaleX\\(([0-9.]+)\\)/);
|
|
270
|
+
if (scaleMatch) {
|
|
271
|
+
const scale = readNumeric(scaleMatch[1]);
|
|
272
|
+
return scale == null ? null : scale * 100;
|
|
273
|
+
}
|
|
274
|
+
const matrixMatch = transform.match(/matrix\\(([0-9.\\-]+),/);
|
|
275
|
+
if (matrixMatch) {
|
|
276
|
+
const scale = readNumeric(matrixMatch[1]);
|
|
277
|
+
return scale == null ? null : scale * 100;
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
};
|
|
281
|
+
for (const node of progressNodes) {
|
|
282
|
+
if (!(node instanceof HTMLElement) || !isVisible(node)) continue;
|
|
283
|
+
const ariaNow = readNumeric(node.getAttribute('aria-valuenow'));
|
|
284
|
+
if (ariaNow != null) {
|
|
285
|
+
const ariaMin = readNumeric(node.getAttribute('aria-valuemin')) ?? 0;
|
|
286
|
+
const ariaMax = readNumeric(node.getAttribute('aria-valuemax')) ?? 100;
|
|
287
|
+
const span = Math.max(ariaMax - ariaMin, 1);
|
|
288
|
+
return Math.max(0, Math.min(100, ((ariaNow - ariaMin) / span) * 100));
|
|
289
|
+
}
|
|
290
|
+
if (node instanceof HTMLProgressElement && Number.isFinite(node.value) && Number.isFinite(node.max) && node.max > 0) {
|
|
291
|
+
return Math.max(0, Math.min(100, (node.value / node.max) * 100));
|
|
292
|
+
}
|
|
293
|
+
const stylePercent = readStylePercent(node);
|
|
294
|
+
if (stylePercent != null) {
|
|
295
|
+
return Math.max(0, Math.min(100, stylePercent));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
};
|
|
300
|
+
const findThinkingPanel = () => {
|
|
301
|
+
const candidates = Array.from(
|
|
302
|
+
document.querySelectorAll(
|
|
303
|
+
[
|
|
304
|
+
'aside',
|
|
305
|
+
'[role="complementary"]',
|
|
306
|
+
'[role="dialog"]',
|
|
307
|
+
'[data-testid*="thinking"]',
|
|
308
|
+
'[data-testid*="reasoning"]',
|
|
309
|
+
'[data-testid*="sidebar"]',
|
|
310
|
+
'[class*="sidecar"]',
|
|
311
|
+
'[class*="sidebar"]',
|
|
312
|
+
].join(','),
|
|
313
|
+
),
|
|
314
|
+
);
|
|
315
|
+
for (const node of candidates) {
|
|
316
|
+
if (!(node instanceof HTMLElement) || !isVisible(node) || isComposerAdjacent(node)) continue;
|
|
317
|
+
const rect = node.getBoundingClientRect();
|
|
318
|
+
const rightSidePanel = rect.left >= window.innerWidth * 0.35 && rect.width >= 180 && rect.height >= 120;
|
|
319
|
+
const hasProgress = findProgressPercent(node) != null;
|
|
320
|
+
if (hasProgress || (rightSidePanel && looksLikeThinking(node))) {
|
|
321
|
+
return node;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return null;
|
|
325
|
+
};
|
|
326
|
+
const existingPanel = findThinkingPanel();
|
|
327
|
+
if (existingPanel) {
|
|
328
|
+
return {
|
|
329
|
+
message: 'thinking sidecar active',
|
|
330
|
+
source: 'sidecar',
|
|
331
|
+
progressPercent: findProgressPercent(existingPanel),
|
|
332
|
+
panelOpened: false,
|
|
333
|
+
panelVisible: true,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
let panelOpened = false;
|
|
337
|
+
const currentTurn = latestAssistantTurn();
|
|
338
|
+
const disclosure = currentTurn ? findThinkingDisclosure(currentTurn) : null;
|
|
339
|
+
if (disclosure) {
|
|
340
|
+
try {
|
|
341
|
+
disclosure.dataset.oracleThinkingProbed = 'true';
|
|
342
|
+
disclosure.click();
|
|
343
|
+
panelOpened = true;
|
|
344
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
345
|
+
} catch {
|
|
346
|
+
// non-fatal; fall through to passive status detection
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const panel = findThinkingPanel();
|
|
350
|
+
if (panel) {
|
|
351
|
+
const progressPercent = findProgressPercent(panel);
|
|
352
|
+
return {
|
|
353
|
+
message: panelOpened ? 'thinking sidecar opened' : 'thinking sidecar active',
|
|
354
|
+
source: 'sidecar',
|
|
355
|
+
progressPercent,
|
|
356
|
+
panelOpened,
|
|
357
|
+
panelVisible: true,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
const nodes = new Set();
|
|
361
|
+
for (const selector of selectors) {
|
|
362
|
+
document.querySelectorAll(selector).forEach((node) => nodes.add(node));
|
|
363
|
+
}
|
|
364
|
+
for (const node of nodes) {
|
|
365
|
+
if (!(node instanceof HTMLElement)) {
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const text = node.textContent?.trim();
|
|
369
|
+
if (!text) {
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const classLabel = String(node.className || '').toLowerCase();
|
|
373
|
+
const dataLabel = ((node.getAttribute('data-testid') || '') + ' ' + (node.getAttribute('aria-label') || ''))
|
|
374
|
+
.toLowerCase();
|
|
375
|
+
const normalizedText = text.toLowerCase();
|
|
376
|
+
const matches = keywords.some((keyword) =>
|
|
377
|
+
normalizedText.includes(keyword) || classLabel.includes(keyword) || dataLabel.includes(keyword)
|
|
378
|
+
);
|
|
379
|
+
if (matches) {
|
|
380
|
+
return {
|
|
381
|
+
message: 'active',
|
|
382
|
+
source: 'inline',
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return null;
|
|
387
|
+
})()`;
|
|
388
|
+
}
|
|
389
|
+
export const startThinkingStatusMonitorForTest = startThinkingStatusMonitor;
|
|
390
|
+
export const readThinkingStatusForTest = readThinkingStatus;
|
|
391
|
+
export const buildThinkingStatusExpressionForTest = buildThinkingStatusExpression;
|