@steipete/oracle 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -9
- package/dist/bin/oracle-cli.js +63 -63
- package/dist/docs-site/.nojekyll +0 -0
- package/dist/docs-site/CNAME +1 -0
- package/dist/docs-site/RELEASING.html +410 -0
- package/dist/docs-site/agents.html +374 -0
- package/dist/docs-site/anthropic.html +368 -0
- package/dist/docs-site/bridge.html +400 -0
- package/dist/docs-site/browser-mode.html +593 -0
- package/dist/docs-site/chromium-forks.html +347 -0
- package/dist/docs-site/cli-reference.html +346 -0
- package/dist/docs-site/configuration.html +452 -0
- package/dist/docs-site/favicon.svg +14 -0
- package/dist/docs-site/followup.html +375 -0
- package/dist/docs-site/gemini.html +383 -0
- package/dist/docs-site/grok.html +325 -0
- package/dist/docs-site/index.html +360 -0
- package/dist/docs-site/install.html +335 -0
- package/dist/docs-site/linux.html +321 -0
- package/dist/docs-site/llms.txt +43 -0
- package/dist/docs-site/manual-tests.html +596 -0
- package/dist/docs-site/mcp.html +391 -0
- package/dist/docs-site/multimodel.html +364 -0
- package/dist/docs-site/mythical-pro-agents.html +360 -0
- package/dist/docs-site/notifier.html +338 -0
- package/dist/docs-site/openai-endpoints.html +387 -0
- package/dist/docs-site/openrouter.html +344 -0
- package/dist/docs-site/quickstart.html +369 -0
- package/dist/docs-site/refactor/ux.html +532 -0
- package/dist/docs-site/sessions.html +388 -0
- package/dist/docs-site/social-card.png +0 -0
- package/dist/docs-site/social-card.svg +79 -0
- package/dist/docs-site/spec.html +363 -0
- package/dist/docs-site/testing.html +320 -0
- package/dist/docs-site/tui-debug.html +326 -0
- package/dist/docs-site/windows-work.html +323 -0
- package/dist/docs-site/windows.html +320 -0
- package/dist/src/browser/actions/assistantResponse.js +91 -9
- package/dist/src/browser/actions/deepResearch.js +180 -52
- package/dist/src/browser/actions/modelSelection.js +13 -11
- 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 +308 -89
- package/dist/src/browser/artifacts.js +19 -0
- package/dist/src/browser/chatgptFiles.js +797 -0
- package/dist/src/browser/chatgptImages.js +130 -3
- package/dist/src/browser/chromeLifecycle.js +4 -0
- package/dist/src/browser/config.js +2 -0
- package/dist/src/browser/index.js +347 -39
- package/dist/src/browser/pageActions.js +1 -1
- package/dist/src/browser/policies.js +2 -6
- package/dist/src/browser/projectSourcesRunner.js +14 -2
- package/dist/src/browser/prompt.js +11 -15
- 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 +4 -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 +16 -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 +127 -0
- package/dist/src/mcp/tools/consult.js +261 -167
- package/dist/src/mcp/types.js +2 -0
- 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/tokenStats.js +5 -1
- package/dist/src/oracle.js +1 -1
- package/dist/src/sessionManager.js +1 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/package.json +19 -28
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
|
@@ -5,12 +5,12 @@ import net from "node:net";
|
|
|
5
5
|
import { resolveBrowserConfig } from "./config.js";
|
|
6
6
|
import { launchChrome, registerTerminationHooks, hideChromeWindow, connectToRemoteChrome, connectWithNewTab, closeTab, closeRemoteChromeTarget, closeBlankChromeTabs, } from "./chromeLifecycle.js";
|
|
7
7
|
import { syncCookies } from "./cookies.js";
|
|
8
|
-
import { navigateToChatGPT, navigateToPromptReadyWithFallback, ensureNotBlocked, ensureLoggedIn, ensurePromptReady, installJavaScriptDialogAutoDismissal, ensureModelSelection, clearPromptComposer, waitForAssistantResponse, captureAssistantMarkdown, clearComposerAttachments, uploadAttachmentFile, waitForAttachmentCompletion, waitForUserTurnAttachments, readAssistantSnapshot, } from "./pageActions.js";
|
|
8
|
+
import { navigateToChatGPT, navigateToPromptReadyWithFallback, ensureNotBlocked, ensureLoggedIn, ensurePromptReady, waitForResumedConversationHydration, installJavaScriptDialogAutoDismissal, ensureModelSelection, clearPromptComposer, waitForAssistantResponse, captureAssistantMarkdown, clearComposerAttachments, uploadAttachmentFile, waitForAttachmentCompletion, waitForUserTurnAttachments, readAssistantSnapshot, } from "./pageActions.js";
|
|
9
9
|
import { INPUT_SELECTORS } from "./constants.js";
|
|
10
10
|
import { uploadAttachmentViaDataTransfer } from "./actions/remoteFileTransfer.js";
|
|
11
11
|
import { ensureThinkingTime } from "./actions/thinkingTime.js";
|
|
12
12
|
import { startThinkingStatusMonitor } from "./actions/thinkingStatus.js";
|
|
13
|
-
import { activateDeepResearch, waitForDeepResearchCompletion, waitForResearchPlanAutoConfirm, } from "./actions/deepResearch.js";
|
|
13
|
+
import { activateDeepResearch, captureDeepResearchTargetKeys, waitForDeepResearchCompletion, waitForResearchPlanAutoConfirm, } from "./actions/deepResearch.js";
|
|
14
14
|
import { estimateTokenCount, withRetries, delay } from "./utils.js";
|
|
15
15
|
import { formatElapsed } from "../oracle/format.js";
|
|
16
16
|
import { CHATGPT_URL, CONVERSATION_TURN_SELECTOR, DEFAULT_MODEL_STRATEGY } from "./constants.js";
|
|
@@ -20,6 +20,7 @@ import { cleanupStaleProfileState, acquireProfileRunLock, findRunningChromeDebug
|
|
|
20
20
|
import { acquireBrowserTabLease, hasOtherActiveBrowserTabLeases, } from "./tabLeaseRegistry.js";
|
|
21
21
|
import { appendArtifacts, saveBrowserTranscriptArtifact, saveDeepResearchReportArtifact, } from "./artifacts.js";
|
|
22
22
|
import { collectGeneratedImageArtifacts } from "./chatgptImages.js";
|
|
23
|
+
import { collectChatGptFileArtifacts } from "./chatgptFiles.js";
|
|
23
24
|
import { runProviderSubmissionFlow } from "./providerDomFlow.js";
|
|
24
25
|
import { chatgptDomProvider } from "./providers/index.js";
|
|
25
26
|
import { resolveAttachRunningConnection } from "./attachRunning.js";
|
|
@@ -71,12 +72,188 @@ export function shouldPreserveBrowserOnErrorForTest(error, headless) {
|
|
|
71
72
|
export function classifyPreservedBrowserErrorForTest(error, headless) {
|
|
72
73
|
return classifyPreservedBrowserError(error, headless);
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
const MAX_CHATGPT_UI_WARNING_CHARS = 300;
|
|
76
|
+
const MAX_CHATGPT_UI_WARNINGS = 3;
|
|
77
|
+
function classifyChatGptUiWarningText(text) {
|
|
78
|
+
const normalized = text.toLowerCase();
|
|
79
|
+
if (/\btoo many requests\b/.test(normalized) ||
|
|
80
|
+
/\bsending too many requests\b/.test(normalized) ||
|
|
81
|
+
/\btoo quickly\b/.test(normalized) ||
|
|
82
|
+
/\btemporarily limited access\b/.test(normalized) ||
|
|
83
|
+
/\bplease wait a few minutes\b/.test(normalized) ||
|
|
84
|
+
/\brate limit(?:ed)?\b/.test(normalized) ||
|
|
85
|
+
/\bslow down\b/.test(normalized)) {
|
|
86
|
+
return "rate_limit";
|
|
87
|
+
}
|
|
88
|
+
if (/\btemporarily unavailable\b/.test(normalized) ||
|
|
89
|
+
/\bsomething went wrong\b/.test(normalized) ||
|
|
90
|
+
/\bfailed to generate\b/.test(normalized) ||
|
|
91
|
+
/\btry again later\b/.test(normalized)) {
|
|
92
|
+
return "temporary_unavailable";
|
|
93
|
+
}
|
|
94
|
+
if (/\bverify you are human\b/.test(normalized) ||
|
|
95
|
+
/\bunusual activity\b/.test(normalized) ||
|
|
96
|
+
/\bcloudflare\b/.test(normalized) ||
|
|
97
|
+
/\bchallenge\b/.test(normalized) ||
|
|
98
|
+
/\blogin required\b/.test(normalized) ||
|
|
99
|
+
/\bsign in\b/.test(normalized)) {
|
|
100
|
+
return "auth_or_challenge";
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
function sanitizeChatGptUiWarningText(text) {
|
|
105
|
+
return text
|
|
106
|
+
.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[redacted-email]")
|
|
107
|
+
.replace(/\b((?:access|auth|session)[-_ ]?token|token)\s*[:=]\s*["']?[^\s"',;]+/gi, "$1=[redacted]")
|
|
108
|
+
.replace(/\b(?:sk-(?:ant-|or-)?|xai-)[A-Za-z0-9_-]{8,}\b/g, "[redacted-token]");
|
|
109
|
+
}
|
|
110
|
+
function normalizeUiWarningCandidate(value) {
|
|
111
|
+
if (!value || typeof value !== "object")
|
|
112
|
+
return null;
|
|
113
|
+
const candidate = value;
|
|
114
|
+
const text = typeof candidate.text === "string"
|
|
115
|
+
? sanitizeChatGptUiWarningText(candidate.text.replace(/\s+/g, " ").trim())
|
|
116
|
+
: "";
|
|
117
|
+
if (!text)
|
|
118
|
+
return null;
|
|
119
|
+
return {
|
|
120
|
+
text: text.slice(0, MAX_CHATGPT_UI_WARNING_CHARS),
|
|
121
|
+
source: typeof candidate.source === "string" ? candidate.source : null,
|
|
122
|
+
role: typeof candidate.role === "string" ? candidate.role : null,
|
|
123
|
+
ariaLive: typeof candidate.ariaLive === "string" ? candidate.ariaLive : null,
|
|
124
|
+
selector: typeof candidate.selector === "string" ? candidate.selector : null,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function collectChatGptUiWarnings(Runtime) {
|
|
128
|
+
try {
|
|
129
|
+
const { result } = await Runtime.evaluate({
|
|
130
|
+
awaitPromise: true,
|
|
131
|
+
returnByValue: true,
|
|
132
|
+
expression: `(() => {
|
|
133
|
+
const warningPattern = /too many requests|sending too many requests|too quickly|temporarily limited access|please wait a few minutes|rate limit|rate limited|slow down|try again later|temporarily unavailable|something went wrong|failed to generate|verify you are human|unusual activity|cloudflare|challenge|login required|sign in/i;
|
|
134
|
+
const selectors = [
|
|
135
|
+
'[role="alert"]',
|
|
136
|
+
'[role="status"]',
|
|
137
|
+
'[role="dialog"]',
|
|
138
|
+
'[aria-live]',
|
|
139
|
+
'[data-testid*="toast" i]',
|
|
140
|
+
'[data-testid*="banner" i]',
|
|
141
|
+
'[data-testid*="error" i]',
|
|
142
|
+
'[class*="toast" i]',
|
|
143
|
+
'[class*="banner" i]'
|
|
144
|
+
];
|
|
145
|
+
const isVisible = (element) => {
|
|
146
|
+
if (!(element instanceof HTMLElement)) return false;
|
|
147
|
+
let current = element;
|
|
148
|
+
while (current) {
|
|
149
|
+
const currentStyle = window.getComputedStyle(current);
|
|
150
|
+
if (
|
|
151
|
+
!currentStyle ||
|
|
152
|
+
currentStyle.display === 'none' ||
|
|
153
|
+
currentStyle.visibility === 'hidden' ||
|
|
154
|
+
currentStyle.visibility === 'collapse' ||
|
|
155
|
+
Number.parseFloat(currentStyle.opacity || '1') === 0
|
|
156
|
+
) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
current = current.parentElement;
|
|
160
|
+
}
|
|
161
|
+
const rect = element.getBoundingClientRect();
|
|
162
|
+
return rect.width > 0 && rect.height > 0;
|
|
163
|
+
};
|
|
164
|
+
const describe = (element, source, selector = null) => ({
|
|
165
|
+
text: (element.innerText || element.textContent || '').replace(/\\s+/g, ' ').trim().slice(0, 1000),
|
|
166
|
+
source,
|
|
167
|
+
selector,
|
|
168
|
+
role: element.getAttribute('role'),
|
|
169
|
+
ariaLive: element.getAttribute('aria-live')
|
|
170
|
+
});
|
|
171
|
+
const out = [];
|
|
172
|
+
const seen = new Set();
|
|
173
|
+
const warningContainers = [];
|
|
174
|
+
const overlapsWarningContainer = (element) => warningContainers.some((container) => (
|
|
175
|
+
container !== element && (container.contains(element) || element.contains(container))
|
|
176
|
+
));
|
|
177
|
+
const add = (element, entry) => {
|
|
178
|
+
if (!entry.text || !warningPattern.test(entry.text)) return;
|
|
179
|
+
const key = entry.text + '|' + (entry.role || '') + '|' + (entry.ariaLive || '');
|
|
180
|
+
if (seen.has(key)) return;
|
|
181
|
+
seen.add(key);
|
|
182
|
+
warningContainers.push(element);
|
|
183
|
+
out.push(entry);
|
|
184
|
+
};
|
|
185
|
+
for (const selector of selectors) {
|
|
186
|
+
if (out.length >= 5) break;
|
|
187
|
+
let elements = [];
|
|
188
|
+
try {
|
|
189
|
+
elements = Array.from(document.querySelectorAll(selector));
|
|
190
|
+
} catch {
|
|
191
|
+
elements = [];
|
|
192
|
+
}
|
|
193
|
+
for (const element of elements) {
|
|
194
|
+
if (out.length >= 5) break;
|
|
195
|
+
if (overlapsWarningContainer(element)) continue;
|
|
196
|
+
if (isVisible(element)) add(element, describe(element, 'selector', selector));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return out.slice(0, 5);
|
|
200
|
+
})()`,
|
|
201
|
+
});
|
|
202
|
+
const rawWarnings = Array.isArray(result?.value) ? result.value : [];
|
|
203
|
+
const warnings = [];
|
|
204
|
+
const seen = new Set();
|
|
205
|
+
for (const raw of rawWarnings) {
|
|
206
|
+
const candidate = normalizeUiWarningCandidate(raw);
|
|
207
|
+
if (!candidate)
|
|
208
|
+
continue;
|
|
209
|
+
const type = classifyChatGptUiWarningText(candidate.text);
|
|
210
|
+
if (!type)
|
|
211
|
+
continue;
|
|
212
|
+
const key = `${type}:${candidate.text}`;
|
|
213
|
+
if (seen.has(key))
|
|
214
|
+
continue;
|
|
215
|
+
seen.add(key);
|
|
216
|
+
warnings.push({
|
|
217
|
+
type,
|
|
218
|
+
message: candidate.text,
|
|
219
|
+
source: candidate.source,
|
|
220
|
+
role: candidate.role,
|
|
221
|
+
ariaLive: candidate.ariaLive,
|
|
222
|
+
selector: candidate.selector,
|
|
223
|
+
});
|
|
224
|
+
if (warnings.length >= MAX_CHATGPT_UI_WARNINGS)
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
return warnings;
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return [];
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function formatChatGptUiWarningType(type) {
|
|
234
|
+
switch (type) {
|
|
235
|
+
case "rate_limit":
|
|
236
|
+
return "rate-limit";
|
|
237
|
+
case "temporary_unavailable":
|
|
238
|
+
return "temporary-unavailable";
|
|
239
|
+
case "auth_or_challenge":
|
|
240
|
+
return "authentication/challenge";
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async function createAssistantTimeoutError(params) {
|
|
244
|
+
const [uiWarning] = await collectChatGptUiWarnings(params.Runtime);
|
|
245
|
+
if (!uiWarning) {
|
|
246
|
+
return new BrowserAutomationError("Assistant response timed out before completion; reattach later to capture the answer.", { stage: "assistant-timeout", runtime: params.runtime, diagnostics: params.diagnostics }, params.cause);
|
|
247
|
+
}
|
|
248
|
+
params.logger(`[browser] ChatGPT UI warning detected (${uiWarning.type}): ${uiWarning.message}`);
|
|
249
|
+
return new BrowserAutomationError(`ChatGPT displayed a ${formatChatGptUiWarningType(uiWarning.type)} warning while waiting for the assistant: ${uiWarning.message}`, {
|
|
250
|
+
stage: "assistant-timeout",
|
|
251
|
+
code: "chatgpt-ui-warning",
|
|
252
|
+
uiWarning,
|
|
253
|
+
runtime: params.runtime,
|
|
254
|
+
diagnostics: params.diagnostics,
|
|
255
|
+
}, params.cause);
|
|
256
|
+
}
|
|
80
257
|
function listIgnoredRemoteChromeFlags(config) {
|
|
81
258
|
return [
|
|
82
259
|
config.headless ? "--browser-headless" : null,
|
|
@@ -273,6 +450,19 @@ function resolveRemoteTabLeaseProfileDir(config) {
|
|
|
273
450
|
export function resolveRemoteTabLeaseProfileDirForTest(config) {
|
|
274
451
|
return resolveRemoteTabLeaseProfileDir(config);
|
|
275
452
|
}
|
|
453
|
+
function isLocalChromeHost(host) {
|
|
454
|
+
const normalized = host
|
|
455
|
+
.trim()
|
|
456
|
+
.toLowerCase()
|
|
457
|
+
.replace(/^\[|\]$/g, "");
|
|
458
|
+
if (normalized === "localhost" || normalized === "::1") {
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
return net.isIPv4(normalized) && normalized.startsWith("127.");
|
|
462
|
+
}
|
|
463
|
+
export function isLocalChromeHostForTest(host) {
|
|
464
|
+
return isLocalChromeHost(host);
|
|
465
|
+
}
|
|
276
466
|
async function closeRemoteConnectionAfterRun(options) {
|
|
277
467
|
if (options.connectionClosedUnexpectedly) {
|
|
278
468
|
return;
|
|
@@ -310,6 +500,7 @@ export async function runBrowserMode(options) {
|
|
|
310
500
|
const attachments = options.attachments ?? [];
|
|
311
501
|
const fallbackSubmission = options.fallbackSubmission;
|
|
312
502
|
let config = resolveBrowserConfig(options.config);
|
|
503
|
+
const isResumingConversation = Boolean(config.resumeConversationUrl);
|
|
313
504
|
const followUpPrompts = normalizeBrowserFollowUpPrompts(options.followUpPrompts);
|
|
314
505
|
if (config.researchMode === "deep" && followUpPrompts.length > 0) {
|
|
315
506
|
throw new BrowserAutomationError("Browser follow-ups are not supported with Deep Research mode. Put the full research plan into the initial prompt or run a normal browser consult for multi-turn review.", {
|
|
@@ -596,9 +787,18 @@ export async function runBrowserMode(options) {
|
|
|
596
787
|
});
|
|
597
788
|
}
|
|
598
789
|
if (config.browserTabRef) {
|
|
790
|
+
if (isResumingConversation) {
|
|
791
|
+
await raceWithDisconnect(navigateToChatGPT(Page, Runtime, config.resumeConversationUrl, logger));
|
|
792
|
+
}
|
|
599
793
|
await raceWithDisconnect(ensureNotBlocked(Runtime, config.headless, logger));
|
|
600
794
|
await raceWithDisconnect(ensureLoggedIn(Runtime, logger));
|
|
601
795
|
await raceWithDisconnect(ensurePromptReady(Runtime, config.inputTimeoutMs, logger));
|
|
796
|
+
if (isResumingConversation) {
|
|
797
|
+
await raceWithDisconnect(waitForResumedConversationHydration(Runtime, config.inputTimeoutMs, logger, {
|
|
798
|
+
requirePriorTurns: true,
|
|
799
|
+
expectedConversationUrl: config.resumeConversationUrl,
|
|
800
|
+
}));
|
|
801
|
+
}
|
|
602
802
|
}
|
|
603
803
|
else {
|
|
604
804
|
const baseUrl = CHATGPT_URL;
|
|
@@ -616,7 +816,12 @@ export async function runBrowserMode(options) {
|
|
|
616
816
|
profileDir: userDataDir,
|
|
617
817
|
keepBrowser: effectiveKeepBrowser,
|
|
618
818
|
}));
|
|
619
|
-
if (
|
|
819
|
+
if (isResumingConversation) {
|
|
820
|
+
await raceWithDisconnect(navigateToChatGPT(Page, Runtime, config.resumeConversationUrl, logger));
|
|
821
|
+
await raceWithDisconnect(ensureNotBlocked(Runtime, config.headless, logger));
|
|
822
|
+
await raceWithDisconnect(ensurePromptReady(Runtime, config.inputTimeoutMs, logger));
|
|
823
|
+
}
|
|
824
|
+
else if (config.url !== baseUrl) {
|
|
620
825
|
await raceWithDisconnect(navigateToPromptReadyWithFallback(Page, Runtime, {
|
|
621
826
|
url: config.url,
|
|
622
827
|
fallbackUrl: baseUrl,
|
|
@@ -628,6 +833,17 @@ export async function runBrowserMode(options) {
|
|
|
628
833
|
else {
|
|
629
834
|
await raceWithDisconnect(ensurePromptReady(Runtime, config.inputTimeoutMs, logger));
|
|
630
835
|
}
|
|
836
|
+
if (isResumingConversation) {
|
|
837
|
+
// A resumed thread loads its prior history after navigation; ChatGPT can reset the
|
|
838
|
+
// composer mid-hydration and wipe a freshly-typed prompt. Wait for hydration to settle
|
|
839
|
+
// and re-confirm the composer before the prompt is typed/submitted below. Wrapped in
|
|
840
|
+
// raceWithDisconnect so a dropped client aborts immediately instead of polling to the
|
|
841
|
+
// hydration deadline. Shared with the remote path via the same helper.
|
|
842
|
+
await raceWithDisconnect(waitForResumedConversationHydration(Runtime, config.inputTimeoutMs, logger, {
|
|
843
|
+
requirePriorTurns: true,
|
|
844
|
+
expectedConversationUrl: config.resumeConversationUrl,
|
|
845
|
+
}));
|
|
846
|
+
}
|
|
631
847
|
}
|
|
632
848
|
logger(`Prompt textarea ready (initial focus, ${promptText.length.toLocaleString()} chars queued)`);
|
|
633
849
|
const captureRuntimeSnapshot = async () => {
|
|
@@ -707,7 +923,7 @@ export async function runBrowserMode(options) {
|
|
|
707
923
|
};
|
|
708
924
|
await captureRuntimeSnapshot();
|
|
709
925
|
const modelStrategy = config.modelStrategy ?? DEFAULT_MODEL_STRATEGY;
|
|
710
|
-
if (config.desiredModel && modelStrategy !== "ignore") {
|
|
926
|
+
if (config.desiredModel && modelStrategy !== "ignore" && !isResumingConversation) {
|
|
711
927
|
modelSelectionEvidence = await raceWithDisconnect(withRetries(() => ensureModelSelection(Runtime, config.desiredModel, logger, modelStrategy), {
|
|
712
928
|
retries: 2,
|
|
713
929
|
delayMs: 300,
|
|
@@ -726,9 +942,11 @@ export async function runBrowserMode(options) {
|
|
|
726
942
|
await raceWithDisconnect(ensurePromptReady(Runtime, config.inputTimeoutMs, logger));
|
|
727
943
|
logger(`Prompt textarea ready (after model switch, ${promptText.length.toLocaleString()} chars queued)`);
|
|
728
944
|
}
|
|
729
|
-
else if (modelStrategy === "ignore") {
|
|
945
|
+
else if (modelStrategy === "ignore" || isResumingConversation) {
|
|
730
946
|
modelSelectionEvidence = buildSkippedModelSelectionEvidence(config.desiredModel, modelStrategy);
|
|
731
|
-
logger(
|
|
947
|
+
logger(isResumingConversation
|
|
948
|
+
? "Model picker: skipped (resumed conversation)"
|
|
949
|
+
: "Model picker: skipped (strategy=ignore)");
|
|
732
950
|
}
|
|
733
951
|
const deepResearch = config.researchMode === "deep";
|
|
734
952
|
// Handle thinking time selection if specified. Deep Research owns its own effort flow.
|
|
@@ -779,6 +997,10 @@ export async function runBrowserMode(options) {
|
|
|
779
997
|
const baselineSnapshot = await readAssistantSnapshot(Runtime).catch(() => null);
|
|
780
998
|
const baselineAssistantText = typeof baselineSnapshot?.text === "string" ? baselineSnapshot.text.trim() : "";
|
|
781
999
|
const attachmentNames = submissionAttachments.map((a) => path.basename(a.path));
|
|
1000
|
+
const attachmentExpectations = submissionAttachments.map((a) => ({
|
|
1001
|
+
name: path.basename(a.path),
|
|
1002
|
+
generatedBundle: a.generatedBundle === true,
|
|
1003
|
+
}));
|
|
782
1004
|
let inputOnlyAttachments = false;
|
|
783
1005
|
await raceWithDisconnect(clearPromptComposer(Runtime, logger));
|
|
784
1006
|
await raceWithDisconnect(ensurePromptReady(Runtime, config.inputTimeoutMs, logger));
|
|
@@ -814,9 +1036,12 @@ export async function runBrowserMode(options) {
|
|
|
814
1036
|
inputTimeoutMs: config.inputTimeoutMs ?? undefined,
|
|
815
1037
|
attachmentTimeoutMs: config.attachmentTimeoutMs ?? undefined,
|
|
816
1038
|
baselineTurns: baselineTurns ?? undefined,
|
|
817
|
-
attachmentNames,
|
|
1039
|
+
attachmentNames: attachmentExpectations,
|
|
818
1040
|
onPromptSubmitted: markPromptSubmitted,
|
|
819
1041
|
};
|
|
1042
|
+
const deepResearchTargetKeys = deepResearch && client
|
|
1043
|
+
? await captureDeepResearchTargetKeys(client).catch(() => [])
|
|
1044
|
+
: undefined;
|
|
820
1045
|
await runProviderSubmissionFlow(chatgptDomProvider, {
|
|
821
1046
|
prompt,
|
|
822
1047
|
evaluate: async () => undefined,
|
|
@@ -849,7 +1074,7 @@ export async function runBrowserMode(options) {
|
|
|
849
1074
|
}
|
|
850
1075
|
// Reattach needs a /c/ URL; ChatGPT can update it late, so poll in the background.
|
|
851
1076
|
scheduleConversationHint("post-submit", config.timeoutMs ?? 120_000);
|
|
852
|
-
return { baselineTurns, baselineAssistantText };
|
|
1077
|
+
return { baselineTurns, baselineAssistantText, deepResearchTargetKeys };
|
|
853
1078
|
};
|
|
854
1079
|
const reloadPromptComposer = async () => {
|
|
855
1080
|
logger("[browser] Composer became unresponsive; reloading page and retrying once.");
|
|
@@ -858,6 +1083,7 @@ export async function runBrowserMode(options) {
|
|
|
858
1083
|
};
|
|
859
1084
|
let baselineTurns = null;
|
|
860
1085
|
let baselineAssistantText = null;
|
|
1086
|
+
let deepResearchTargetKeys = [];
|
|
861
1087
|
await acquireProfileLockIfNeeded();
|
|
862
1088
|
try {
|
|
863
1089
|
const submission = await runSubmissionWithRecovery({
|
|
@@ -874,6 +1100,7 @@ export async function runBrowserMode(options) {
|
|
|
874
1100
|
});
|
|
875
1101
|
baselineTurns = submission.baselineTurns;
|
|
876
1102
|
baselineAssistantText = submission.baselineAssistantText;
|
|
1103
|
+
deepResearchTargetKeys = submission.deepResearchTargetKeys ?? [];
|
|
877
1104
|
}
|
|
878
1105
|
finally {
|
|
879
1106
|
await releaseProfileLockIfHeld();
|
|
@@ -881,7 +1108,7 @@ export async function runBrowserMode(options) {
|
|
|
881
1108
|
const imageArtifactMinTurnIndex = baselineTurns;
|
|
882
1109
|
if (deepResearch) {
|
|
883
1110
|
await raceWithDisconnect(waitForResearchPlanAutoConfirm(Runtime, logger));
|
|
884
|
-
const researchResult = await raceWithDisconnect(waitForDeepResearchCompletion(Runtime, logger, config.timeoutMs, baselineTurns, Page, client));
|
|
1111
|
+
const researchResult = await raceWithDisconnect(waitForDeepResearchCompletion(Runtime, logger, config.timeoutMs, baselineTurns, Page, client, { ignoredTargetKeys: deepResearchTargetKeys }));
|
|
885
1112
|
await updateConversationHint("post-deep-research", 15_000).catch(() => false);
|
|
886
1113
|
runStatus = "complete";
|
|
887
1114
|
const durationMs = Date.now() - startedAt;
|
|
@@ -1070,7 +1297,13 @@ export async function runBrowserMode(options) {
|
|
|
1070
1297
|
promptSubmitted,
|
|
1071
1298
|
controllerPid: process.pid,
|
|
1072
1299
|
};
|
|
1073
|
-
throw
|
|
1300
|
+
throw await createAssistantTimeoutError({
|
|
1301
|
+
Runtime,
|
|
1302
|
+
logger,
|
|
1303
|
+
runtime,
|
|
1304
|
+
diagnostics,
|
|
1305
|
+
cause: error,
|
|
1306
|
+
});
|
|
1074
1307
|
}
|
|
1075
1308
|
}
|
|
1076
1309
|
else {
|
|
@@ -1259,6 +1492,9 @@ export async function runBrowserMode(options) {
|
|
|
1259
1492
|
throw new Error("Chrome disconnected before completion");
|
|
1260
1493
|
}
|
|
1261
1494
|
const imageArtifacts = await collectGeneratedImageArtifacts({
|
|
1495
|
+
Browser: client.Browser,
|
|
1496
|
+
Client: client,
|
|
1497
|
+
Page,
|
|
1262
1498
|
Runtime,
|
|
1263
1499
|
Network,
|
|
1264
1500
|
logger,
|
|
@@ -1273,16 +1509,28 @@ export async function runBrowserMode(options) {
|
|
|
1273
1509
|
if (imageArtifacts.markdownSuffix) {
|
|
1274
1510
|
answerMarkdown += imageArtifacts.markdownSuffix;
|
|
1275
1511
|
}
|
|
1512
|
+
const fileArtifacts = await collectChatGptFileArtifacts({
|
|
1513
|
+
Browser: client.Browser,
|
|
1514
|
+
Client: client,
|
|
1515
|
+
Page,
|
|
1516
|
+
Runtime,
|
|
1517
|
+
Network,
|
|
1518
|
+
answerText: [answerText, answerMarkdown, answerHtml].filter(Boolean).join("\n"),
|
|
1519
|
+
logger,
|
|
1520
|
+
minTurnIndex: imageArtifactMinTurnIndex,
|
|
1521
|
+
sessionId: options.sessionId,
|
|
1522
|
+
});
|
|
1276
1523
|
const savedImageArtifacts = appendArtifacts(undefined, imageArtifacts.savedImages);
|
|
1524
|
+
const savedBrowserArtifacts = appendArtifacts(savedImageArtifacts, fileArtifacts.savedFiles);
|
|
1277
1525
|
const transcriptArtifact = await saveOptionalArtifact(() => saveBrowserTranscriptArtifact({
|
|
1278
1526
|
sessionId: options.sessionId,
|
|
1279
1527
|
prompt: promptText,
|
|
1280
1528
|
answerMarkdown,
|
|
1281
1529
|
conversationUrl: lastUrl,
|
|
1282
|
-
artifacts:
|
|
1530
|
+
artifacts: savedBrowserArtifacts,
|
|
1283
1531
|
logger,
|
|
1284
1532
|
}), logger);
|
|
1285
|
-
const savedArtifacts = appendArtifacts(
|
|
1533
|
+
const savedArtifacts = appendArtifacts(savedBrowserArtifacts, [transcriptArtifact]);
|
|
1286
1534
|
const archive = await maybeArchiveCompletedConversation({
|
|
1287
1535
|
Runtime,
|
|
1288
1536
|
logger,
|
|
@@ -1290,7 +1538,8 @@ export async function runBrowserMode(options) {
|
|
|
1290
1538
|
conversationUrl: lastUrl,
|
|
1291
1539
|
followUpCount: followUpPrompts.length,
|
|
1292
1540
|
requiredArtifactsSaved: Boolean(transcriptArtifact) &&
|
|
1293
|
-
imageArtifacts.savedImages.length === imageArtifacts.imageCount
|
|
1541
|
+
imageArtifacts.savedImages.length === imageArtifacts.imageCount &&
|
|
1542
|
+
fileArtifacts.savedFiles.length === fileArtifacts.fileCount,
|
|
1294
1543
|
});
|
|
1295
1544
|
runStatus = "complete";
|
|
1296
1545
|
const durationMs = Date.now() - startedAt;
|
|
@@ -1303,6 +1552,8 @@ export async function runBrowserMode(options) {
|
|
|
1303
1552
|
artifacts: savedArtifacts,
|
|
1304
1553
|
generatedImages: imageArtifacts.generatedImages,
|
|
1305
1554
|
savedImages: imageArtifacts.savedImages,
|
|
1555
|
+
downloadableFiles: fileArtifacts.files,
|
|
1556
|
+
savedFiles: fileArtifacts.savedFiles,
|
|
1306
1557
|
archive,
|
|
1307
1558
|
modelSelection: modelSelectionEvidence,
|
|
1308
1559
|
tookMs: durationMs,
|
|
@@ -1676,13 +1927,23 @@ async function maybeReuseRunningChrome(userDataDir, logger, options = {}) {
|
|
|
1676
1927
|
let pid = await readChromePid(userDataDir);
|
|
1677
1928
|
if (!port) {
|
|
1678
1929
|
const discovered = await findRunningChromeDebugTargetForProfile(userDataDir);
|
|
1679
|
-
if (!discovered)
|
|
1930
|
+
if (!discovered) {
|
|
1931
|
+
if (pid) {
|
|
1932
|
+
logger(`No reachable Chrome DevTools target found for ${userDataDir}; clearing stale profile state before launching new Chrome.`);
|
|
1933
|
+
await cleanupStaleProfileState(userDataDir, logger, {
|
|
1934
|
+
lockRemovalMode: "if_oracle_pid_dead",
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1680
1937
|
return null;
|
|
1938
|
+
}
|
|
1681
1939
|
const discoveredProbe = await (options.probe ?? verifyDevToolsReachable)({
|
|
1682
1940
|
port: discovered.port,
|
|
1683
1941
|
});
|
|
1684
1942
|
if (!discoveredProbe.ok) {
|
|
1685
1943
|
logger(`Discovered Chrome for ${userDataDir} on port ${discovered.port} but it was unreachable (${discoveredProbe.error}); launching new Chrome.`);
|
|
1944
|
+
await cleanupStaleProfileState(userDataDir, logger, {
|
|
1945
|
+
lockRemovalMode: "if_oracle_pid_dead",
|
|
1946
|
+
});
|
|
1686
1947
|
return null;
|
|
1687
1948
|
}
|
|
1688
1949
|
await writeDevToolsActivePort(userDataDir, discovered.port);
|
|
@@ -1830,16 +2091,20 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1830
2091
|
removeDialogHandler = installJavaScriptDialogAutoDismissal(Page, logger);
|
|
1831
2092
|
// Skip cookie sync for remote Chrome - it already has cookies
|
|
1832
2093
|
logger("Skipping cookie sync for remote Chrome (using existing session)");
|
|
1833
|
-
if (
|
|
2094
|
+
if (config.resumeConversationUrl) {
|
|
2095
|
+
await navigateToChatGPT(Page, Runtime, config.resumeConversationUrl, logger);
|
|
2096
|
+
}
|
|
2097
|
+
else if (!attachedExistingTab) {
|
|
1834
2098
|
await navigateToChatGPT(Page, Runtime, config.url, logger);
|
|
1835
|
-
await ensureNotBlocked(Runtime, config.headless, logger);
|
|
1836
|
-
await ensureLoggedIn(Runtime, logger, { remoteSession: true });
|
|
1837
|
-
await ensurePromptReady(Runtime, config.inputTimeoutMs, logger);
|
|
1838
2099
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
2100
|
+
await ensureNotBlocked(Runtime, config.headless, logger);
|
|
2101
|
+
await ensureLoggedIn(Runtime, logger, { remoteSession: true });
|
|
2102
|
+
await ensurePromptReady(Runtime, config.inputTimeoutMs, logger);
|
|
2103
|
+
if (config.resumeConversationUrl) {
|
|
2104
|
+
await waitForResumedConversationHydration(Runtime, config.inputTimeoutMs, logger, {
|
|
2105
|
+
requirePriorTurns: true,
|
|
2106
|
+
expectedConversationUrl: config.resumeConversationUrl,
|
|
2107
|
+
});
|
|
1843
2108
|
}
|
|
1844
2109
|
logger(`Prompt textarea ready (initial focus, ${promptText.length.toLocaleString()} chars queued)`);
|
|
1845
2110
|
try {
|
|
@@ -1856,7 +2121,7 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1856
2121
|
// ignore
|
|
1857
2122
|
}
|
|
1858
2123
|
const modelStrategy = config.modelStrategy ?? DEFAULT_MODEL_STRATEGY;
|
|
1859
|
-
if (config.desiredModel && modelStrategy !== "ignore") {
|
|
2124
|
+
if (config.desiredModel && modelStrategy !== "ignore" && !config.resumeConversationUrl) {
|
|
1860
2125
|
modelSelectionEvidence = await withRetries(() => ensureModelSelection(Runtime, config.desiredModel, logger, modelStrategy), {
|
|
1861
2126
|
retries: 2,
|
|
1862
2127
|
delayMs: 300,
|
|
@@ -1869,9 +2134,11 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1869
2134
|
await ensurePromptReady(Runtime, config.inputTimeoutMs, logger);
|
|
1870
2135
|
logger(`Prompt textarea ready (after model switch, ${promptText.length.toLocaleString()} chars queued)`);
|
|
1871
2136
|
}
|
|
1872
|
-
else if (modelStrategy === "ignore") {
|
|
2137
|
+
else if (modelStrategy === "ignore" || config.resumeConversationUrl) {
|
|
1873
2138
|
modelSelectionEvidence = buildSkippedModelSelectionEvidence(config.desiredModel, modelStrategy);
|
|
1874
|
-
logger(
|
|
2139
|
+
logger(config.resumeConversationUrl
|
|
2140
|
+
? "Model picker: skipped (resumed conversation)"
|
|
2141
|
+
: "Model picker: skipped (strategy=ignore)");
|
|
1875
2142
|
}
|
|
1876
2143
|
const deepResearch = config.researchMode === "deep";
|
|
1877
2144
|
// Handle thinking time selection if specified. Deep Research owns its own effort flow.
|
|
@@ -1905,6 +2172,10 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1905
2172
|
const baselineSnapshot = await readAssistantSnapshot(Runtime).catch(() => null);
|
|
1906
2173
|
const baselineAssistantText = typeof baselineSnapshot?.text === "string" ? baselineSnapshot.text.trim() : "";
|
|
1907
2174
|
const attachmentNames = submissionAttachments.map((a) => path.basename(a.path));
|
|
2175
|
+
const attachmentExpectations = submissionAttachments.map((a) => ({
|
|
2176
|
+
name: path.basename(a.path),
|
|
2177
|
+
generatedBundle: a.generatedBundle === true,
|
|
2178
|
+
}));
|
|
1908
2179
|
await clearPromptComposer(Runtime, logger);
|
|
1909
2180
|
await ensurePromptReady(Runtime, config.inputTimeoutMs, logger);
|
|
1910
2181
|
if (submissionAttachments.length > 0) {
|
|
@@ -1935,9 +2206,12 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1935
2206
|
inputTimeoutMs: config.inputTimeoutMs ?? undefined,
|
|
1936
2207
|
attachmentTimeoutMs: config.attachmentTimeoutMs ?? undefined,
|
|
1937
2208
|
baselineTurns: baselineTurns ?? undefined,
|
|
1938
|
-
attachmentNames,
|
|
2209
|
+
attachmentNames: attachmentExpectations,
|
|
1939
2210
|
onPromptSubmitted: markPromptSubmitted,
|
|
1940
2211
|
};
|
|
2212
|
+
const deepResearchTargetKeys = deepResearch && client
|
|
2213
|
+
? await captureDeepResearchTargetKeys(client).catch(() => [])
|
|
2214
|
+
: undefined;
|
|
1941
2215
|
await runProviderSubmissionFlow(chatgptDomProvider, {
|
|
1942
2216
|
prompt,
|
|
1943
2217
|
evaluate: async () => undefined,
|
|
@@ -1950,7 +2224,7 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1950
2224
|
if (typeof providerBaselineTurns === "number" && Number.isFinite(providerBaselineTurns)) {
|
|
1951
2225
|
baselineTurns = providerBaselineTurns;
|
|
1952
2226
|
}
|
|
1953
|
-
return { baselineTurns, baselineAssistantText };
|
|
2227
|
+
return { baselineTurns, baselineAssistantText, deepResearchTargetKeys };
|
|
1954
2228
|
};
|
|
1955
2229
|
const reloadPromptComposer = async () => {
|
|
1956
2230
|
logger("[browser] Composer became unresponsive; reloading page and retrying once.");
|
|
@@ -1959,6 +2233,7 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1959
2233
|
};
|
|
1960
2234
|
let baselineTurns = null;
|
|
1961
2235
|
let baselineAssistantText = null;
|
|
2236
|
+
let deepResearchTargetKeys = [];
|
|
1962
2237
|
const submission = await runSubmissionWithRecovery({
|
|
1963
2238
|
prompt: promptText,
|
|
1964
2239
|
attachments,
|
|
@@ -1973,10 +2248,11 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1973
2248
|
});
|
|
1974
2249
|
baselineTurns = submission.baselineTurns;
|
|
1975
2250
|
baselineAssistantText = submission.baselineAssistantText;
|
|
2251
|
+
deepResearchTargetKeys = submission.deepResearchTargetKeys ?? [];
|
|
1976
2252
|
const imageArtifactMinTurnIndex = baselineTurns;
|
|
1977
2253
|
if (deepResearch) {
|
|
1978
2254
|
await waitForResearchPlanAutoConfirm(Runtime, logger);
|
|
1979
|
-
const researchResult = await waitForDeepResearchCompletion(Runtime, logger, config.timeoutMs, baselineTurns, Page, client);
|
|
2255
|
+
const researchResult = await waitForDeepResearchCompletion(Runtime, logger, config.timeoutMs, baselineTurns, Page, client, { ignoredTargetKeys: deepResearchTargetKeys });
|
|
1980
2256
|
await emitRuntimeHint();
|
|
1981
2257
|
const durationMs = Date.now() - startedAt;
|
|
1982
2258
|
const tokens = estimateTokenCount(researchResult.text);
|
|
@@ -2175,7 +2451,13 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2175
2451
|
promptSubmitted,
|
|
2176
2452
|
controllerPid: process.pid,
|
|
2177
2453
|
};
|
|
2178
|
-
throw
|
|
2454
|
+
throw await createAssistantTimeoutError({
|
|
2455
|
+
Runtime,
|
|
2456
|
+
logger,
|
|
2457
|
+
runtime,
|
|
2458
|
+
diagnostics,
|
|
2459
|
+
cause: error,
|
|
2460
|
+
});
|
|
2179
2461
|
}
|
|
2180
2462
|
}
|
|
2181
2463
|
else {
|
|
@@ -2320,7 +2602,11 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2320
2602
|
answerMarkdown = formatted.answerMarkdown;
|
|
2321
2603
|
answerHtml = "";
|
|
2322
2604
|
}
|
|
2605
|
+
const canSaveBrowserDownloadsLocally = isLocalChromeHost(host);
|
|
2323
2606
|
const imageArtifacts = await collectGeneratedImageArtifacts({
|
|
2607
|
+
Browser: canSaveBrowserDownloadsLocally ? client.Browser : undefined,
|
|
2608
|
+
Client: canSaveBrowserDownloadsLocally ? client : undefined,
|
|
2609
|
+
Page: canSaveBrowserDownloadsLocally ? Page : undefined,
|
|
2324
2610
|
Runtime,
|
|
2325
2611
|
Network,
|
|
2326
2612
|
logger,
|
|
@@ -2335,16 +2621,28 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2335
2621
|
if (imageArtifacts.markdownSuffix) {
|
|
2336
2622
|
answerMarkdown += imageArtifacts.markdownSuffix;
|
|
2337
2623
|
}
|
|
2624
|
+
const fileArtifacts = await collectChatGptFileArtifacts({
|
|
2625
|
+
Browser: client.Browser,
|
|
2626
|
+
Client: client,
|
|
2627
|
+
Page,
|
|
2628
|
+
Runtime,
|
|
2629
|
+
Network,
|
|
2630
|
+
answerText: [answerText, answerMarkdown, answerHtml].filter(Boolean).join("\n"),
|
|
2631
|
+
logger,
|
|
2632
|
+
minTurnIndex: imageArtifactMinTurnIndex,
|
|
2633
|
+
sessionId: options.sessionId,
|
|
2634
|
+
});
|
|
2338
2635
|
const savedImageArtifacts = appendArtifacts(undefined, imageArtifacts.savedImages);
|
|
2636
|
+
const savedBrowserArtifacts = appendArtifacts(savedImageArtifacts, fileArtifacts.savedFiles);
|
|
2339
2637
|
const transcriptArtifact = await saveOptionalArtifact(() => saveBrowserTranscriptArtifact({
|
|
2340
2638
|
sessionId: options.sessionId,
|
|
2341
2639
|
prompt: promptText,
|
|
2342
2640
|
answerMarkdown,
|
|
2343
2641
|
conversationUrl: lastUrl,
|
|
2344
|
-
artifacts:
|
|
2642
|
+
artifacts: savedBrowserArtifacts,
|
|
2345
2643
|
logger,
|
|
2346
2644
|
}), logger);
|
|
2347
|
-
const savedArtifacts = appendArtifacts(
|
|
2645
|
+
const savedArtifacts = appendArtifacts(savedBrowserArtifacts, [transcriptArtifact]);
|
|
2348
2646
|
const archive = await maybeArchiveCompletedConversation({
|
|
2349
2647
|
Runtime,
|
|
2350
2648
|
logger,
|
|
@@ -2352,7 +2650,8 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2352
2650
|
conversationUrl: lastUrl,
|
|
2353
2651
|
followUpCount: followUpPrompts.length,
|
|
2354
2652
|
requiredArtifactsSaved: Boolean(transcriptArtifact) &&
|
|
2355
|
-
imageArtifacts.savedImages.length === imageArtifacts.imageCount
|
|
2653
|
+
imageArtifacts.savedImages.length === imageArtifacts.imageCount &&
|
|
2654
|
+
fileArtifacts.savedFiles.length === fileArtifacts.fileCount,
|
|
2356
2655
|
});
|
|
2357
2656
|
const durationMs = Date.now() - startedAt;
|
|
2358
2657
|
const answerChars = answerText.length;
|
|
@@ -2377,6 +2676,10 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
2377
2676
|
conversationId: lastUrl ? extractConversationIdFromUrl(lastUrl) : undefined,
|
|
2378
2677
|
promptSubmitted,
|
|
2379
2678
|
artifacts: savedArtifacts,
|
|
2679
|
+
generatedImages: imageArtifacts.generatedImages,
|
|
2680
|
+
savedImages: imageArtifacts.savedImages,
|
|
2681
|
+
downloadableFiles: fileArtifacts.files,
|
|
2682
|
+
savedFiles: fileArtifacts.savedFiles,
|
|
2380
2683
|
archive,
|
|
2381
2684
|
modelSelection: modelSelectionEvidence,
|
|
2382
2685
|
controllerPid: process.pid,
|
|
@@ -2443,8 +2746,12 @@ export { resolveBrowserConfig, DEFAULT_BROWSER_CONFIG } from "./config.js";
|
|
|
2443
2746
|
export const __test__ = {
|
|
2444
2747
|
assertManualLoginProfileReadyForRun,
|
|
2445
2748
|
closeRemoteConnectionAfterRun,
|
|
2749
|
+
classifyChatGptUiWarningText,
|
|
2750
|
+
collectChatGptUiWarnings,
|
|
2751
|
+
createAssistantTimeoutError,
|
|
2446
2752
|
detachKeptChromeProcess,
|
|
2447
2753
|
formatManualLoginSetupCommand,
|
|
2754
|
+
isAssistantResponseTimeoutError,
|
|
2448
2755
|
isManualLoginProfileInitialized,
|
|
2449
2756
|
isImageOnlyUiChromeText,
|
|
2450
2757
|
listIgnoredRemoteChromeFlags,
|
|
@@ -2500,7 +2807,8 @@ function isAssistantResponseTimeoutError(error) {
|
|
|
2500
2807
|
const message = error.message.toLowerCase();
|
|
2501
2808
|
if (!message)
|
|
2502
2809
|
return false;
|
|
2503
|
-
return (message
|
|
2810
|
+
return (message === "response timeout" ||
|
|
2811
|
+
message.includes("assistant-response") ||
|
|
2504
2812
|
message.includes("assistant response") ||
|
|
2505
2813
|
message.includes("watchdog") ||
|
|
2506
2814
|
message.includes("capture assistant response"));
|