@steipete/oracle 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -11
- package/dist/bin/oracle-cli.js +71 -71
- package/dist/src/browser/actions/assistantResponse.js +93 -10
- package/dist/src/browser/actions/deepResearch.js +180 -52
- package/dist/src/browser/actions/modelSelection.js +357 -40
- package/dist/src/browser/actions/navigation.js +294 -65
- package/dist/src/browser/actions/promptComposer.js +50 -4
- package/dist/src/browser/actions/thinkingTime.js +517 -105
- package/dist/src/browser/artifacts.js +19 -0
- package/dist/src/browser/chatgptFiles.js +797 -0
- package/dist/src/browser/chatgptImages.js +256 -27
- package/dist/src/browser/chromeLifecycle.js +4 -0
- package/dist/src/browser/config.js +2 -0
- package/dist/src/browser/index.js +404 -41
- package/dist/src/browser/pageActions.js +1 -1
- package/dist/src/browser/policies.js +2 -6
- package/dist/src/browser/projectSourcesRunner.js +16 -3
- package/dist/src/browser/prompt.js +161 -36
- package/dist/src/browser/reattach.js +6 -2
- package/dist/src/browser/reattachability.js +22 -10
- package/dist/src/browser/recoverConversation.js +73 -0
- package/dist/src/browser/sessionRunner.js +4 -1
- package/dist/src/cli/browserConfig.js +6 -2
- package/dist/src/cli/browserDefaults.js +2 -1
- package/dist/src/cli/browserTabs.js +129 -54
- package/dist/src/cli/followup.js +72 -0
- package/dist/src/cli/help.js +1 -1
- package/dist/src/cli/options.js +24 -0
- package/dist/src/cli/reattachGuidance.js +8 -0
- package/dist/src/cli/runOptions.js +2 -12
- package/dist/src/cli/sessionCommand.js +4 -0
- package/dist/src/cli/sessionDisplay.js +17 -3
- package/dist/src/cli/sessionLineage.js +7 -4
- package/dist/src/cli/sessionRunner.js +16 -1
- package/dist/src/gemini-web/client.js +5 -10
- package/dist/src/gemini-web/executor.js +1 -24
- package/dist/src/gemini-web/models.js +83 -0
- package/dist/src/mcp/server.js +2 -0
- package/dist/src/mcp/tools/chatgptImage.js +132 -0
- package/dist/src/mcp/tools/consult.js +270 -175
- package/dist/src/mcp/types.js +13 -2
- package/dist/src/mcp/utils.js +87 -1
- package/dist/src/oracle/config.js +27 -1
- package/dist/src/oracle/files.js +4 -6
- package/dist/src/oracle/geminiModels.js +2 -0
- package/dist/src/oracle/markdown.js +36 -3
- package/dist/src/oracle/modelResolver.js +21 -13
- package/dist/src/oracle/promptAssembly.js +2 -4
- package/dist/src/oracle/request.js +3 -5
- package/dist/src/oracle/thinkingTime.js +40 -0
- package/dist/src/oracle/tokenStats.js +5 -1
- package/dist/src/oracle.js +1 -1
- package/dist/src/sessionManager.js +1 -0
- package/dist/vendor/oracle-notifier/build-notifier.sh +0 -0
- package/package.json +47 -58
- package/vendor/oracle-notifier/build-notifier.sh +0 -0
- package/dist/bin/oracle.js +0 -569
- package/dist/src/browser/chromeCookies.js +0 -312
- package/dist/src/browser/keytarShim.js +0 -56
- package/dist/src/browser/windowsCookies.js +0 -219
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/Info.plist +0 -20
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +0 -128
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/Info.plist +0 -20
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +0 -128
- package/vendor/oracle-notifier/README.md +0 -26
|
@@ -107,13 +107,16 @@ export async function waitForResearchPlanAutoConfirm(Runtime, logger, autoConfir
|
|
|
107
107
|
* Polls for Deep Research completion over 5-30+ minutes.
|
|
108
108
|
* Returns the full response text, optional HTML, and turn metadata.
|
|
109
109
|
*/
|
|
110
|
-
export async function waitForDeepResearchCompletion(Runtime, logger, timeoutMs = DEEP_RESEARCH_DEFAULT_TIMEOUT_MS, minTurnIndex, Page, client) {
|
|
110
|
+
export async function waitForDeepResearchCompletion(Runtime, logger, timeoutMs = DEEP_RESEARCH_DEFAULT_TIMEOUT_MS, minTurnIndex, Page, client, options) {
|
|
111
111
|
const start = Date.now();
|
|
112
112
|
let lastLogTime = start;
|
|
113
113
|
let lastTextLength = 0;
|
|
114
114
|
const minTurnLiteral = typeof minTurnIndex === "number" && Number.isFinite(minTurnIndex) && minTurnIndex >= 0
|
|
115
115
|
? Math.floor(minTurnIndex)
|
|
116
116
|
: -1;
|
|
117
|
+
const scopedToNewTurns = minTurnLiteral >= 0;
|
|
118
|
+
const ignoredTargetKeys = new Set(options?.ignoredTargetKeys ?? []);
|
|
119
|
+
const requireScopedTargetOwner = options?.requireScopedTargetOwner === true;
|
|
117
120
|
logger(`Monitoring Deep Research (timeout: ${Math.round(timeoutMs / 60_000)}min)...`);
|
|
118
121
|
while (Date.now() - start < timeoutMs) {
|
|
119
122
|
const { result } = await Runtime.evaluate({
|
|
@@ -124,19 +127,37 @@ export async function waitForDeepResearchCompletion(Runtime, logger, timeoutMs =
|
|
|
124
127
|
if (val?.accountBlocked) {
|
|
125
128
|
throw new BrowserAutomationError("ChatGPT account security block detected during Deep Research. Open chatgpt.com in Chrome, secure the account, then rerun Oracle.", { stage: "chatgpt-account-blocked", code: "chatgpt-account-blocked" });
|
|
126
129
|
}
|
|
127
|
-
const
|
|
130
|
+
const activeScopedResearch = Boolean(val?.hasActiveScopedResearch);
|
|
131
|
+
// ChatGPT renders the Deep Research report inside an out-of-process,
|
|
132
|
+
// sandboxed iframe (connector_openai_deep_research.*.oaiusercontent.com),
|
|
133
|
+
// doubly nested and same-origin. That OOPIF does NOT appear in the main
|
|
134
|
+
// page's frame tree, so the in-page isolated-world path
|
|
135
|
+
// (readDeepResearchFrameResult) can never see it. The target-attach path
|
|
136
|
+
// (readDeepResearchTargetResult) attaches to the iframe's own CDP target and
|
|
137
|
+
// walks its nested frames, so it CAN read the report. Prefer the target path
|
|
138
|
+
// and fall back to the in-page frame path for legacy/inline rendering.
|
|
139
|
+
const targetResult = client
|
|
140
|
+
? ((await readDeepResearchTargetResult(client, ignoredTargetKeys, requireScopedTargetOwner ? minTurnLiteral : -1).catch(() => null))?.read ?? null)
|
|
141
|
+
: null;
|
|
142
|
+
// A completed target read is authoritative. If the target read is missing or
|
|
143
|
+
// only in-progress, still try the in-page frame path so an incomplete target
|
|
144
|
+
// read does not suppress a completed report there (legacy/inline rendering).
|
|
145
|
+
const inPageResult = !targetResult?.completed && Page
|
|
128
146
|
? await readDeepResearchFrameResult(Runtime, Page).catch(() => null)
|
|
129
|
-
:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
: null;
|
|
148
|
+
const read = pickPreferredDeepResearchRead(targetResult, inPageResult);
|
|
149
|
+
// A target-confirmed completion read the live connector iframe directly, so
|
|
150
|
+
// it is authoritative even when the main DOM exposes no assistant turn (the
|
|
151
|
+
// report lives entirely in the OOPIF). The main-DOM hasActiveScopedResearch
|
|
152
|
+
// heuristic no longer holds in that case, so don't gate on it.
|
|
153
|
+
const completedFromTarget = Boolean(targetResult?.completed);
|
|
154
|
+
if (read?.completed &&
|
|
155
|
+
read.text &&
|
|
156
|
+
(completedFromTarget || !scopedToNewTurns || activeScopedResearch)) {
|
|
136
157
|
logger(`Deep Research completed (${Math.round((Date.now() - start) / 1000)}s elapsed)`);
|
|
137
158
|
return {
|
|
138
|
-
text:
|
|
139
|
-
html:
|
|
159
|
+
text: read.text,
|
|
160
|
+
html: read.html,
|
|
140
161
|
meta: { turnId: null, messageId: null },
|
|
141
162
|
};
|
|
142
163
|
}
|
|
@@ -149,8 +170,8 @@ export async function waitForDeepResearchCompletion(Runtime, logger, timeoutMs =
|
|
|
149
170
|
const now = Date.now();
|
|
150
171
|
if (now - lastLogTime >= 60_000) {
|
|
151
172
|
const elapsed = Math.round((now - start) / 1000);
|
|
152
|
-
const chars = Math.max(val?.textLength ?? 0,
|
|
153
|
-
const phase =
|
|
173
|
+
const chars = Math.max(val?.textLength ?? 0, read?.textLength ?? 0);
|
|
174
|
+
const phase = read?.inProgress || val?.hasIframe
|
|
154
175
|
? "researching"
|
|
155
176
|
: val?.stopVisible
|
|
156
177
|
? "generating"
|
|
@@ -158,7 +179,7 @@ export async function waitForDeepResearchCompletion(Runtime, logger, timeoutMs =
|
|
|
158
179
|
logger(`Deep Research ${phase}... ${elapsed}s elapsed, ~${chars} chars`);
|
|
159
180
|
lastLogTime = now;
|
|
160
181
|
}
|
|
161
|
-
lastTextLength = Math.max(val?.textLength ?? 0,
|
|
182
|
+
lastTextLength = Math.max(val?.textLength ?? 0, read?.textLength ?? 0, lastTextLength);
|
|
162
183
|
await delay(DEEP_RESEARCH_POLL_INTERVAL_MS);
|
|
163
184
|
}
|
|
164
185
|
// Timeout — throw with metadata for potential reattach
|
|
@@ -202,6 +223,26 @@ function isDeepResearchPlaceholderText(text) {
|
|
|
202
223
|
export function isDeepResearchPlaceholderTextForTest(text) {
|
|
203
224
|
return isDeepResearchPlaceholderText(text);
|
|
204
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Choose the authoritative Deep Research read between the target-attach result
|
|
228
|
+
* and the in-page frame result. A completed read wins (target preferred, since
|
|
229
|
+
* it reads the live OOPIF directly); otherwise the best in-progress/text-bearing
|
|
230
|
+
* read is kept so progress logging still advances. This preserves the legacy
|
|
231
|
+
* Page-first inline behaviour: when the target read is missing or incomplete,
|
|
232
|
+
* a completed in-page result is still returned.
|
|
233
|
+
*/
|
|
234
|
+
function pickPreferredDeepResearchRead(targetResult, inPageResult) {
|
|
235
|
+
if (targetResult?.completed) {
|
|
236
|
+
return targetResult;
|
|
237
|
+
}
|
|
238
|
+
if (inPageResult?.completed) {
|
|
239
|
+
return inPageResult;
|
|
240
|
+
}
|
|
241
|
+
return targetResult ?? inPageResult;
|
|
242
|
+
}
|
|
243
|
+
export function pickPreferredDeepResearchReadForTest(targetResult, inPageResult) {
|
|
244
|
+
return pickPreferredDeepResearchRead(targetResult, inPageResult);
|
|
245
|
+
}
|
|
205
246
|
async function readDeepResearchFrameResult(Runtime, Page) {
|
|
206
247
|
const pageWithFrames = Page;
|
|
207
248
|
if (typeof pageWithFrames.getFrameTree !== "function" ||
|
|
@@ -228,58 +269,97 @@ async function readDeepResearchFrameResult(Runtime, Page) {
|
|
|
228
269
|
});
|
|
229
270
|
return result?.value ?? null;
|
|
230
271
|
}
|
|
231
|
-
async function readDeepResearchTargetResult(client) {
|
|
272
|
+
async function readDeepResearchTargetResult(client, ignoredTargetKeys = new Set(), minTurnIndex = -1) {
|
|
232
273
|
const rawClient = client;
|
|
233
274
|
if (typeof rawClient.send !== "function") {
|
|
234
275
|
return null;
|
|
235
276
|
}
|
|
236
|
-
|
|
277
|
+
// On the browser-WSEndpoint path, `client` is a session-bound wrapper whose
|
|
278
|
+
// domain methods target the page session but whose raw `send` is the
|
|
279
|
+
// browser-level send. We must therefore pass the page session id explicitly so
|
|
280
|
+
// Target.setAutoAttach binds to THIS page (not the whole browser). For a direct
|
|
281
|
+
// tab client this is undefined and `send` already defaults to the page session.
|
|
282
|
+
const pageSessionId = rawClient.oraclePageSessionId;
|
|
283
|
+
const sessions = new Map();
|
|
237
284
|
const ownedSessionIds = new Set();
|
|
238
|
-
const onAttached = (params,
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
285
|
+
const onAttached = (params, parentSessionId) => {
|
|
286
|
+
// chrome-remote-interface emits flattened target events both on the
|
|
287
|
+
// session-specific event name and on the shared base event. The second
|
|
288
|
+
// callback argument identifies the parent page session; ignore events from
|
|
289
|
+
// other tabs when this client wraps a shared browser WebSocket.
|
|
290
|
+
if (pageSessionId && parentSessionId !== pageSessionId) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const targetInfo = params?.targetInfo;
|
|
294
|
+
const eventSessionId = params?.sessionId ?? parentSessionId;
|
|
242
295
|
const url = targetInfo?.url ?? "";
|
|
243
296
|
const type = targetInfo?.type ?? "";
|
|
244
297
|
if (eventSessionId && isDeepResearchTarget(url, type)) {
|
|
245
|
-
|
|
298
|
+
sessions.set(eventSessionId, { targetId: targetInfo?.targetId, url });
|
|
246
299
|
ownedSessionIds.add(eventSessionId);
|
|
247
300
|
}
|
|
248
301
|
};
|
|
249
302
|
client.on?.("Target.attachedToTarget", onAttached);
|
|
250
303
|
try {
|
|
251
|
-
|
|
304
|
+
// Scope discovery to the current Oracle-controlled page. `client` is
|
|
305
|
+
// connected to the conversation page target, so enabling auto-attach on this
|
|
306
|
+
// session only attaches THIS page's related targets (its Deep Research OOPIF
|
|
307
|
+
// subframe) and emits Target.attachedToTarget for them.
|
|
308
|
+
//
|
|
309
|
+
// We deliberately do NOT enumerate Target.getTargets / attachToTarget here:
|
|
310
|
+
// that scan is browser-wide, and in a shared/persistent Chrome profile it
|
|
311
|
+
// would surface another tab's completed Deep Research report and let it be
|
|
312
|
+
// saved into the current session (cross-tab leak). Only auto-attached,
|
|
313
|
+
// page-scoped sessions are treated as belonging to this run.
|
|
252
314
|
await rawClient
|
|
253
315
|
.send("Target.setAutoAttach", {
|
|
254
316
|
autoAttach: true,
|
|
255
317
|
waitForDebuggerOnStart: false,
|
|
256
318
|
flatten: true,
|
|
257
|
-
})
|
|
319
|
+
}, pageSessionId)
|
|
258
320
|
.catch(() => undefined);
|
|
259
321
|
await delay(100);
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
322
|
+
if (minTurnIndex >= 0) {
|
|
323
|
+
await rawClient.send("DOM.enable", {}, pageSessionId).catch(() => undefined);
|
|
324
|
+
await rawClient.send("Runtime.enable", {}, pageSessionId).catch(() => undefined);
|
|
325
|
+
}
|
|
326
|
+
// Baseline targets and owner turns before the submitted prompt are removed
|
|
327
|
+
// first. Among remaining targets, a completed report is authoritative;
|
|
328
|
+
// otherwise retain the newest meaningful progress read for status logging.
|
|
329
|
+
let completed = null;
|
|
330
|
+
let latestProgress = null;
|
|
331
|
+
const targetKeys = [];
|
|
332
|
+
for (const [sessionId, target] of sessions) {
|
|
333
|
+
const sessionResult = await readDeepResearchTargetSession(rawClient, sessionId, target.url);
|
|
334
|
+
if (!sessionResult.confirmed) {
|
|
263
335
|
continue;
|
|
264
336
|
}
|
|
265
|
-
|
|
266
|
-
.
|
|
267
|
-
.catch(() => null));
|
|
268
|
-
if (attached?.sessionId) {
|
|
269
|
-
sessionIds.add(attached.sessionId);
|
|
270
|
-
ownedSessionIds.add(attached.sessionId);
|
|
337
|
+
if (target.targetId) {
|
|
338
|
+
targetKeys.push(target.targetId);
|
|
271
339
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
340
|
+
if (target.targetId && ignoredTargetKeys.has(target.targetId)) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (minTurnIndex >= 0) {
|
|
344
|
+
const ownerTurnIndex = sessionResult.frameId
|
|
345
|
+
? await readDeepResearchTargetOwnerTurnIndex(rawClient, sessionResult.frameId, pageSessionId)
|
|
346
|
+
: null;
|
|
347
|
+
if (ownerTurnIndex === null || ownerTurnIndex < minTurnIndex) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const value = sessionResult.read;
|
|
275
352
|
if (value?.completed) {
|
|
276
|
-
|
|
353
|
+
completed = value;
|
|
277
354
|
}
|
|
278
|
-
if (value
|
|
279
|
-
|
|
355
|
+
else if (value && (value.inProgress || value.textLength > 0)) {
|
|
356
|
+
latestProgress = value;
|
|
280
357
|
}
|
|
281
358
|
}
|
|
282
|
-
return
|
|
359
|
+
return {
|
|
360
|
+
read: completed ?? latestProgress,
|
|
361
|
+
targetKeys,
|
|
362
|
+
};
|
|
283
363
|
}
|
|
284
364
|
finally {
|
|
285
365
|
await rawClient
|
|
@@ -287,18 +367,62 @@ async function readDeepResearchTargetResult(client) {
|
|
|
287
367
|
autoAttach: false,
|
|
288
368
|
waitForDebuggerOnStart: false,
|
|
289
369
|
flatten: true,
|
|
290
|
-
})
|
|
370
|
+
}, pageSessionId)
|
|
291
371
|
.catch(() => undefined);
|
|
292
372
|
await Promise.all(Array.from(ownedSessionIds, (sessionId) => rawClient.send("Target.detachFromTarget", { sessionId }).catch(() => undefined)));
|
|
293
373
|
client.removeListener?.("Target.attachedToTarget", onAttached);
|
|
294
374
|
}
|
|
295
375
|
}
|
|
296
|
-
async function
|
|
376
|
+
export async function captureDeepResearchTargetKeys(client) {
|
|
377
|
+
return (await readDeepResearchTargetResult(client))?.targetKeys ?? [];
|
|
378
|
+
}
|
|
379
|
+
async function readDeepResearchTargetOwnerTurnIndex(rawClient, frameId, pageSessionId) {
|
|
380
|
+
const owner = (await rawClient
|
|
381
|
+
.send("DOM.getFrameOwner", { frameId }, pageSessionId)
|
|
382
|
+
.catch(() => null));
|
|
383
|
+
if (typeof owner?.backendNodeId !== "number") {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
const resolved = (await rawClient
|
|
387
|
+
.send("DOM.resolveNode", { backendNodeId: owner.backendNodeId }, pageSessionId)
|
|
388
|
+
.catch(() => null));
|
|
389
|
+
const objectId = resolved?.object?.objectId;
|
|
390
|
+
if (!objectId) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
try {
|
|
394
|
+
const response = (await rawClient
|
|
395
|
+
.send("Runtime.callFunctionOn", {
|
|
396
|
+
objectId,
|
|
397
|
+
functionDeclaration: `function() {
|
|
398
|
+
const selector = ${JSON.stringify(CONVERSATION_TURN_SELECTOR)};
|
|
399
|
+
const turn = this.closest(selector);
|
|
400
|
+
return turn ? Array.from(document.querySelectorAll(selector)).indexOf(turn) : null;
|
|
401
|
+
}`,
|
|
402
|
+
returnByValue: true,
|
|
403
|
+
}, pageSessionId)
|
|
404
|
+
.catch(() => null));
|
|
405
|
+
const value = response?.result?.value;
|
|
406
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0
|
|
407
|
+
? Math.floor(value)
|
|
408
|
+
: null;
|
|
409
|
+
}
|
|
410
|
+
finally {
|
|
411
|
+
await rawClient
|
|
412
|
+
.send("Runtime.releaseObject", { objectId }, pageSessionId)
|
|
413
|
+
.catch(() => undefined);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async function readDeepResearchTargetSession(rawClient, sessionId, targetUrl) {
|
|
297
417
|
await rawClient.send("Runtime.enable", {}, sessionId).catch(() => undefined);
|
|
298
418
|
await rawClient.send("Page.enable", {}, sessionId).catch(() => undefined);
|
|
299
419
|
const frameTree = (await rawClient
|
|
300
420
|
.send("Page.getFrameTree", {}, sessionId)
|
|
301
421
|
.catch(() => null));
|
|
422
|
+
const frameId = frameTree?.frameTree?.frame?.id;
|
|
423
|
+
if (!isConfirmedDeepResearchTarget(targetUrl, frameTree?.frameTree)) {
|
|
424
|
+
return { confirmed: false, read: null };
|
|
425
|
+
}
|
|
302
426
|
const frameIds = collectDeepResearchFrameIds(frameTree?.frameTree);
|
|
303
427
|
let best = null;
|
|
304
428
|
for (const frameId of frameIds) {
|
|
@@ -314,7 +438,7 @@ async function readDeepResearchTargetSession(rawClient, sessionId) {
|
|
|
314
438
|
}
|
|
315
439
|
const value = await evaluateDeepResearchFrameStatus(rawClient, sessionId, world.executionContextId);
|
|
316
440
|
if (value?.completed) {
|
|
317
|
-
return value;
|
|
441
|
+
return { confirmed: true, read: value, frameId };
|
|
318
442
|
}
|
|
319
443
|
if ((value?.textLength ?? 0) > (best?.textLength ?? 0) || value?.inProgress) {
|
|
320
444
|
best = value;
|
|
@@ -322,12 +446,12 @@ async function readDeepResearchTargetSession(rawClient, sessionId) {
|
|
|
322
446
|
}
|
|
323
447
|
const topFrameValue = await evaluateDeepResearchFrameStatus(rawClient, sessionId);
|
|
324
448
|
if (topFrameValue?.completed) {
|
|
325
|
-
return topFrameValue;
|
|
449
|
+
return { confirmed: true, read: topFrameValue, frameId };
|
|
326
450
|
}
|
|
327
451
|
if ((topFrameValue?.textLength ?? 0) > (best?.textLength ?? 0) || topFrameValue?.inProgress) {
|
|
328
452
|
best = topFrameValue;
|
|
329
453
|
}
|
|
330
|
-
return best;
|
|
454
|
+
return { confirmed: true, read: best, frameId };
|
|
331
455
|
}
|
|
332
456
|
async function evaluateDeepResearchFrameStatus(rawClient, sessionId, contextId) {
|
|
333
457
|
const response = (await rawClient
|
|
@@ -340,11 +464,14 @@ async function evaluateDeepResearchFrameStatus(rawClient, sessionId, contextId)
|
|
|
340
464
|
return response?.result?.value ?? null;
|
|
341
465
|
}
|
|
342
466
|
function isDeepResearchTarget(url, type) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
467
|
+
return type.toLowerCase() === "iframe" || isDeepResearchFrameDescriptor(url);
|
|
468
|
+
}
|
|
469
|
+
function isConfirmedDeepResearchTarget(targetUrl, tree) {
|
|
470
|
+
return isDeepResearchFrameDescriptor(targetUrl) || Boolean(findDeepResearchFrameId(tree));
|
|
471
|
+
}
|
|
472
|
+
function isDeepResearchFrameDescriptor(url, name = "") {
|
|
473
|
+
const descriptor = `${url}\n${name}`.toLowerCase();
|
|
474
|
+
return (descriptor.includes("connector_openai_deep_research") || descriptor.includes("deep-research"));
|
|
348
475
|
}
|
|
349
476
|
function findDeepResearchFrameId(tree) {
|
|
350
477
|
if (!tree?.frame) {
|
|
@@ -352,9 +479,7 @@ function findDeepResearchFrameId(tree) {
|
|
|
352
479
|
}
|
|
353
480
|
const url = tree.frame.url ?? "";
|
|
354
481
|
const name = tree.frame.name ?? "";
|
|
355
|
-
if (url
|
|
356
|
-
url.includes("deep-research") ||
|
|
357
|
-
name.includes("deep-research")) {
|
|
482
|
+
if (isDeepResearchFrameDescriptor(url, name)) {
|
|
358
483
|
return tree.frame.id ?? null;
|
|
359
484
|
}
|
|
360
485
|
for (const child of tree.childFrames ?? []) {
|
|
@@ -439,6 +564,9 @@ function buildDeepResearchFrameStatusExpression() {
|
|
|
439
564
|
export function findDeepResearchFrameIdForTest(tree) {
|
|
440
565
|
return findDeepResearchFrameId(tree);
|
|
441
566
|
}
|
|
567
|
+
export function isConfirmedDeepResearchTargetForTest(targetUrl, tree) {
|
|
568
|
+
return isConfirmedDeepResearchTarget(targetUrl, tree);
|
|
569
|
+
}
|
|
442
570
|
export function buildDeepResearchFrameStatusExpressionForTest() {
|
|
443
571
|
return buildDeepResearchFrameStatusExpression();
|
|
444
572
|
}
|