@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
|
@@ -1,16 +1,34 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import os from
|
|
3
|
-
import path from
|
|
4
|
-
import { readFiles, createFileSections, MODEL_CONFIGS, TOKENIZER_OPTIONS, formatFileSection, } from
|
|
5
|
-
import { isKnownModel } from
|
|
6
|
-
import { buildPromptMarkdown } from
|
|
7
|
-
import { buildAttachmentPlan } from
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { readFiles, createFileSections, MODEL_CONFIGS, TOKENIZER_OPTIONS, formatFileSection, } from "../oracle.js";
|
|
5
|
+
import { isKnownModel } from "../oracle/modelResolver.js";
|
|
6
|
+
import { buildPromptMarkdown } from "../oracle/promptAssembly.js";
|
|
7
|
+
import { buildAttachmentPlan } from "./policies.js";
|
|
8
8
|
const DEFAULT_BROWSER_INLINE_CHAR_BUDGET = 60_000;
|
|
9
9
|
const MEDIA_EXTENSIONS = new Set([
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
".mp4",
|
|
11
|
+
".mov",
|
|
12
|
+
".avi",
|
|
13
|
+
".mkv",
|
|
14
|
+
".webm",
|
|
15
|
+
".m4v",
|
|
16
|
+
".mp3",
|
|
17
|
+
".wav",
|
|
18
|
+
".aac",
|
|
19
|
+
".flac",
|
|
20
|
+
".ogg",
|
|
21
|
+
".m4a",
|
|
22
|
+
".jpg",
|
|
23
|
+
".jpeg",
|
|
24
|
+
".png",
|
|
25
|
+
".gif",
|
|
26
|
+
".webp",
|
|
27
|
+
".bmp",
|
|
28
|
+
".svg",
|
|
29
|
+
".heic",
|
|
30
|
+
".heif",
|
|
31
|
+
".pdf",
|
|
14
32
|
]);
|
|
15
33
|
export function isMediaFile(filePath) {
|
|
16
34
|
const ext = path.extname(filePath).toLowerCase();
|
|
@@ -32,14 +50,14 @@ export async function assembleBrowserPrompt(runOptions, deps = {}) {
|
|
|
32
50
|
};
|
|
33
51
|
}));
|
|
34
52
|
const files = await readFilesFn(textFilePaths, { cwd });
|
|
35
|
-
const basePrompt = (runOptions.prompt ??
|
|
53
|
+
const basePrompt = (runOptions.prompt ?? "").trim();
|
|
36
54
|
const userPrompt = basePrompt;
|
|
37
|
-
const systemPrompt = runOptions.system?.trim() ||
|
|
55
|
+
const systemPrompt = runOptions.system?.trim() || "";
|
|
38
56
|
const sections = createFileSections(files, cwd);
|
|
39
57
|
const markdown = buildPromptMarkdown(systemPrompt, userPrompt, sections);
|
|
40
58
|
const attachmentsPolicy = runOptions.browserInlineFiles
|
|
41
|
-
?
|
|
42
|
-
: runOptions.browserAttachments ??
|
|
59
|
+
? "never"
|
|
60
|
+
: (runOptions.browserAttachments ?? "auto");
|
|
43
61
|
const bundleRequested = Boolean(runOptions.browserBundleFiles);
|
|
44
62
|
const inlinePlan = buildAttachmentPlan(sections, { inlineFiles: true, bundleRequested });
|
|
45
63
|
const uploadPlan = buildAttachmentPlan(sections, { inlineFiles: false, bundleRequested });
|
|
@@ -48,10 +66,13 @@ export async function assembleBrowserPrompt(runOptions, deps = {}) {
|
|
|
48
66
|
baseComposerSections.push(systemPrompt);
|
|
49
67
|
if (userPrompt)
|
|
50
68
|
baseComposerSections.push(userPrompt);
|
|
51
|
-
const inlineComposerText = [...baseComposerSections, inlinePlan.inlineBlock]
|
|
52
|
-
|
|
69
|
+
const inlineComposerText = [...baseComposerSections, inlinePlan.inlineBlock]
|
|
70
|
+
.filter(Boolean)
|
|
71
|
+
.join("\n\n")
|
|
72
|
+
.trim();
|
|
73
|
+
const selectedPlan = attachmentsPolicy === "always"
|
|
53
74
|
? uploadPlan
|
|
54
|
-
: attachmentsPolicy ===
|
|
75
|
+
: attachmentsPolicy === "never"
|
|
55
76
|
? inlinePlan
|
|
56
77
|
: inlineComposerText.length <= DEFAULT_BROWSER_INLINE_CHAR_BUDGET || sections.length === 0
|
|
57
78
|
? inlinePlan
|
|
@@ -60,73 +81,82 @@ export async function assembleBrowserPrompt(runOptions, deps = {}) {
|
|
|
60
81
|
? [...baseComposerSections, selectedPlan.inlineBlock]
|
|
61
82
|
: baseComposerSections)
|
|
62
83
|
.filter(Boolean)
|
|
63
|
-
.join(
|
|
84
|
+
.join("\n\n")
|
|
64
85
|
.trim();
|
|
65
86
|
const attachments = [...selectedPlan.attachments, ...mediaAttachments];
|
|
66
87
|
const shouldBundle = selectedPlan.shouldBundle;
|
|
67
88
|
let bundleText = null;
|
|
68
89
|
let bundled = null;
|
|
69
90
|
if (shouldBundle) {
|
|
70
|
-
const bundleDir = await fs.mkdtemp(path.join(os.tmpdir(),
|
|
71
|
-
const bundlePath = path.join(bundleDir,
|
|
91
|
+
const bundleDir = await fs.mkdtemp(path.join(os.tmpdir(), "oracle-browser-bundle-"));
|
|
92
|
+
const bundlePath = path.join(bundleDir, "attachments-bundle.txt");
|
|
72
93
|
const bundleLines = [];
|
|
73
94
|
sections.forEach((section) => {
|
|
74
95
|
bundleLines.push(formatFileSection(section.displayPath, section.content).trimEnd());
|
|
75
|
-
bundleLines.push(
|
|
96
|
+
bundleLines.push("");
|
|
76
97
|
});
|
|
77
|
-
bundleText = `${bundleLines
|
|
78
|
-
|
|
98
|
+
bundleText = `${bundleLines
|
|
99
|
+
.join("\n")
|
|
100
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
101
|
+
.trimEnd()}\n`;
|
|
102
|
+
await fs.writeFile(bundlePath, bundleText, "utf8");
|
|
79
103
|
attachments.length = 0;
|
|
80
104
|
attachments.push({
|
|
81
105
|
path: bundlePath,
|
|
82
106
|
displayPath: bundlePath,
|
|
83
|
-
sizeBytes: Buffer.byteLength(bundleText,
|
|
107
|
+
sizeBytes: Buffer.byteLength(bundleText, "utf8"),
|
|
84
108
|
});
|
|
85
109
|
attachments.push(...mediaAttachments);
|
|
86
110
|
bundled = { originalCount: sections.length, bundlePath };
|
|
87
111
|
}
|
|
88
112
|
const inlineFileCount = selectedPlan.inlineFileCount;
|
|
89
|
-
const modelConfig = isKnownModel(runOptions.model)
|
|
113
|
+
const modelConfig = isKnownModel(runOptions.model)
|
|
114
|
+
? MODEL_CONFIGS[runOptions.model]
|
|
115
|
+
: MODEL_CONFIGS["gpt-5.1"];
|
|
90
116
|
const tokenizer = deps.tokenizeImpl ?? modelConfig.tokenizer;
|
|
91
117
|
const tokenizerUserContent = inlineFileCount > 0 && selectedPlan.inlineBlock
|
|
92
|
-
? [userPrompt, selectedPlan.inlineBlock]
|
|
118
|
+
? [userPrompt, selectedPlan.inlineBlock]
|
|
119
|
+
.filter((value) => Boolean(value?.trim()))
|
|
120
|
+
.join("\n\n")
|
|
121
|
+
.trim()
|
|
93
122
|
: userPrompt;
|
|
94
123
|
const tokenizerMessages = [
|
|
95
|
-
systemPrompt ? { role:
|
|
96
|
-
tokenizerUserContent ? { role:
|
|
124
|
+
systemPrompt ? { role: "system", content: systemPrompt } : null,
|
|
125
|
+
tokenizerUserContent ? { role: "user", content: tokenizerUserContent } : null,
|
|
97
126
|
].filter(Boolean);
|
|
98
|
-
let estimatedInputTokens = tokenizer(tokenizerMessages.length > 0
|
|
99
|
-
? tokenizerMessages
|
|
100
|
-
: [{ role: 'user', content: '' }], TOKENIZER_OPTIONS);
|
|
127
|
+
let estimatedInputTokens = tokenizer(tokenizerMessages.length > 0 ? tokenizerMessages : [{ role: "user", content: "" }], TOKENIZER_OPTIONS);
|
|
101
128
|
const tokenEstimateIncludesInlineFiles = inlineFileCount > 0 && Boolean(selectedPlan.inlineBlock);
|
|
102
129
|
if (!tokenEstimateIncludesInlineFiles && sections.length > 0) {
|
|
103
130
|
const attachmentText = bundleText ??
|
|
104
131
|
sections
|
|
105
132
|
.map((section) => formatFileSection(section.displayPath, section.content).trimEnd())
|
|
106
|
-
.join(
|
|
107
|
-
const attachmentTokens = tokenizer([{ role:
|
|
133
|
+
.join("\n\n");
|
|
134
|
+
const attachmentTokens = tokenizer([{ role: "user", content: attachmentText }], TOKENIZER_OPTIONS);
|
|
108
135
|
estimatedInputTokens += attachmentTokens;
|
|
109
136
|
}
|
|
110
137
|
let fallback = null;
|
|
111
|
-
if (attachmentsPolicy ===
|
|
112
|
-
const fallbackComposerText = baseComposerSections.join(
|
|
138
|
+
if (attachmentsPolicy === "auto" && selectedPlan.mode === "inline" && sections.length > 0) {
|
|
139
|
+
const fallbackComposerText = baseComposerSections.join("\n\n").trim();
|
|
113
140
|
const fallbackAttachments = [...uploadPlan.attachments, ...mediaAttachments];
|
|
114
141
|
let fallbackBundled = null;
|
|
115
142
|
if (uploadPlan.shouldBundle) {
|
|
116
|
-
const bundleDir = await fs.mkdtemp(path.join(os.tmpdir(),
|
|
117
|
-
const bundlePath = path.join(bundleDir,
|
|
143
|
+
const bundleDir = await fs.mkdtemp(path.join(os.tmpdir(), "oracle-browser-bundle-"));
|
|
144
|
+
const bundlePath = path.join(bundleDir, "attachments-bundle.txt");
|
|
118
145
|
const bundleLines = [];
|
|
119
146
|
sections.forEach((section) => {
|
|
120
147
|
bundleLines.push(formatFileSection(section.displayPath, section.content).trimEnd());
|
|
121
|
-
bundleLines.push(
|
|
148
|
+
bundleLines.push("");
|
|
122
149
|
});
|
|
123
|
-
const fallbackBundleText = `${bundleLines
|
|
124
|
-
|
|
150
|
+
const fallbackBundleText = `${bundleLines
|
|
151
|
+
.join("\n")
|
|
152
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
153
|
+
.trimEnd()}\n`;
|
|
154
|
+
await fs.writeFile(bundlePath, fallbackBundleText, "utf8");
|
|
125
155
|
fallbackAttachments.length = 0;
|
|
126
156
|
fallbackAttachments.push({
|
|
127
157
|
path: bundlePath,
|
|
128
158
|
displayPath: bundlePath,
|
|
129
|
-
sizeBytes: Buffer.byteLength(fallbackBundleText,
|
|
159
|
+
sizeBytes: Buffer.byteLength(fallbackBundleText, "utf8"),
|
|
130
160
|
});
|
|
131
161
|
fallbackAttachments.push(...mediaAttachments);
|
|
132
162
|
fallbackBundled = { originalCount: sections.length, bundlePath };
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { formatBytes } from
|
|
1
|
+
import { formatBytes } from "./utils.js";
|
|
2
2
|
export function buildTokenEstimateSuffix(artifacts) {
|
|
3
3
|
if (artifacts.tokenEstimateIncludesInlineFiles && artifacts.inlineFileCount > 0) {
|
|
4
4
|
const count = artifacts.inlineFileCount;
|
|
5
|
-
const plural = count === 1 ?
|
|
5
|
+
const plural = count === 1 ? "" : "s";
|
|
6
6
|
return ` (includes ${count} inline file${plural})`;
|
|
7
7
|
}
|
|
8
8
|
if (artifacts.attachments.length > 0) {
|
|
9
9
|
const count = artifacts.attachments.length;
|
|
10
|
-
const plural = count === 1 ?
|
|
10
|
+
const plural = count === 1 ? "" : "s";
|
|
11
11
|
return ` (prompt only; ${count} attachment${plural} excluded)`;
|
|
12
12
|
}
|
|
13
|
-
return
|
|
13
|
+
return "";
|
|
14
14
|
}
|
|
15
15
|
export function formatAttachmentLabel(attachment) {
|
|
16
|
-
if (typeof attachment.sizeBytes !==
|
|
16
|
+
if (typeof attachment.sizeBytes !== "number" || Number.isNaN(attachment.sizeBytes)) {
|
|
17
17
|
return attachment.displayPath;
|
|
18
18
|
}
|
|
19
19
|
return `${attachment.displayPath} (${formatBytes(attachment.sizeBytes)})`;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ensurePromptReady } from
|
|
2
|
-
import { submitPrompt } from
|
|
3
|
-
import { waitForAssistantResponse } from
|
|
1
|
+
import { ensurePromptReady } from "../actions/navigation.js";
|
|
2
|
+
import { submitPrompt } from "../actions/promptComposer.js";
|
|
3
|
+
import { waitForAssistantResponse } from "../actions/assistantResponse.js";
|
|
4
4
|
function requireState(ctx) {
|
|
5
5
|
const state = ctx.state;
|
|
6
6
|
if (!state?.runtime || !state?.input || !state?.logger) {
|
|
7
|
-
throw new Error(
|
|
7
|
+
throw new Error("chatgptDomProvider requires runtime/input/logger in context.state.");
|
|
8
8
|
}
|
|
9
9
|
return state;
|
|
10
10
|
}
|
|
@@ -24,10 +24,10 @@ async function submitPromptViaAdapter(ctx) {
|
|
|
24
24
|
baselineTurns: state.baselineTurns ?? undefined,
|
|
25
25
|
inputTimeoutMs: state.inputTimeoutMs ?? undefined,
|
|
26
26
|
}, ctx.prompt, state.logger);
|
|
27
|
-
state.committedTurns =
|
|
28
|
-
? committedTurns
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
state.committedTurns =
|
|
28
|
+
typeof committedTurns === "number" && Number.isFinite(committedTurns) ? committedTurns : null;
|
|
29
|
+
if (state.committedTurns != null &&
|
|
30
|
+
(state.baselineTurns == null || state.committedTurns > state.baselineTurns)) {
|
|
31
31
|
state.baselineTurns = Math.max(0, state.committedTurns - 1);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -41,7 +41,7 @@ async function waitForResponse(ctx) {
|
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
export const chatgptDomProvider = {
|
|
44
|
-
providerName:
|
|
44
|
+
providerName: "chatgpt-web",
|
|
45
45
|
waitForUi,
|
|
46
46
|
typePrompt,
|
|
47
47
|
submitPrompt: submitPromptViaAdapter,
|
|
@@ -1,43 +1,50 @@
|
|
|
1
|
-
import { joinSelectors } from
|
|
1
|
+
import { joinSelectors } from "../providerDomFlow.js";
|
|
2
2
|
const UI_TIMEOUT_MS = 60_000;
|
|
3
3
|
const RESPONSE_TIMEOUT_MS = 10 * 60_000;
|
|
4
4
|
export const GEMINI_DEEP_THINK_SELECTORS = {
|
|
5
|
-
input: [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
input: [
|
|
6
|
+
"rich-textarea .ql-editor",
|
|
7
|
+
'[role="textbox"][aria-label*="prompt" i]',
|
|
8
|
+
'div[contenteditable="true"]',
|
|
9
|
+
],
|
|
10
|
+
sendButton: ["button.send-button", 'button[aria-label="Send message"]'],
|
|
11
|
+
toolsButton: ["button.toolbox-drawer-button", 'button[aria-label="Tools"]'],
|
|
12
|
+
toolsMenuItem: ['[role="menuitemcheckbox"]', ".toolbox-drawer-item-list-button"],
|
|
13
|
+
deepThinkActive: [
|
|
14
|
+
".toolbox-drawer-item-deselect-button",
|
|
15
|
+
'button[aria-label*="Deselect Deep Think"]',
|
|
16
|
+
],
|
|
17
|
+
uploadButton: ['button[aria-label="Open upload file menu"]', ".upload-card-button"],
|
|
11
18
|
uploadMenuItem: ['[role="menuitem"]'],
|
|
12
|
-
uploadTrigger: [
|
|
13
|
-
uploaderContainer: [
|
|
14
|
-
uploaderElement: [
|
|
15
|
-
userTurnAttachment: [
|
|
16
|
-
responseTurn: [
|
|
17
|
-
responseText: [
|
|
18
|
-
responseComplete: [
|
|
19
|
-
userQuery: [
|
|
20
|
-
userQueryText: [
|
|
19
|
+
uploadTrigger: [".hidden-local-file-upload-button", ".hidden-local-upload-button"],
|
|
20
|
+
uploaderContainer: [".uploader-button-container", ".file-uploader"],
|
|
21
|
+
uploaderElement: ["uploader.upload-button"],
|
|
22
|
+
userTurnAttachment: [".file-preview-container"],
|
|
23
|
+
responseTurn: ["model-response"],
|
|
24
|
+
responseText: ["message-content", ".model-response-text message-content"],
|
|
25
|
+
responseComplete: [".response-footer.complete"],
|
|
26
|
+
userQuery: ["user-query"],
|
|
27
|
+
userQueryText: ["user-query-content", ".query-text"],
|
|
21
28
|
spinner: ['[role="progressbar"]'],
|
|
22
|
-
thoughtsToggle: [
|
|
23
|
-
thoughtsContent: [
|
|
24
|
-
hasThoughts: [
|
|
29
|
+
thoughtsToggle: [".thoughts-header-button", '[data-test-id="thoughts-header-button"]'],
|
|
30
|
+
thoughtsContent: ["model-thoughts", '[data-test-id="model-thoughts"]'],
|
|
31
|
+
hasThoughts: [".has-thoughts"],
|
|
25
32
|
};
|
|
26
33
|
function asSelectorLiteral(selectors) {
|
|
27
34
|
return JSON.stringify(joinSelectors(selectors));
|
|
28
35
|
}
|
|
29
36
|
function readTimeouts(ctx) {
|
|
30
37
|
const state = ctx.state;
|
|
31
|
-
const uiTimeoutMs = typeof state?.inputTimeoutMs ===
|
|
38
|
+
const uiTimeoutMs = typeof state?.inputTimeoutMs === "number" && Number.isFinite(state.inputTimeoutMs)
|
|
32
39
|
? Math.max(1_000, state.inputTimeoutMs)
|
|
33
40
|
: UI_TIMEOUT_MS;
|
|
34
|
-
const responseTimeoutMs = typeof state?.timeoutMs ===
|
|
41
|
+
const responseTimeoutMs = typeof state?.timeoutMs === "number" && Number.isFinite(state.timeoutMs)
|
|
35
42
|
? Math.max(1_000, state.timeoutMs)
|
|
36
43
|
: RESPONSE_TIMEOUT_MS;
|
|
37
44
|
return { uiTimeoutMs, responseTimeoutMs };
|
|
38
45
|
}
|
|
39
46
|
async function waitForUi(ctx) {
|
|
40
|
-
ctx.log?.(
|
|
47
|
+
ctx.log?.("[gemini-web] Waiting for Gemini UI to load...");
|
|
41
48
|
const inputSelector = asSelectorLiteral(GEMINI_DEEP_THINK_SELECTORS.input);
|
|
42
49
|
const { uiTimeoutMs } = readTimeouts(ctx);
|
|
43
50
|
const uiDeadline = Date.now() + uiTimeoutMs;
|
|
@@ -64,9 +71,9 @@ async function waitForUi(ctx) {
|
|
|
64
71
|
}
|
|
65
72
|
if (!uiReady) {
|
|
66
73
|
if (sawLoginRedirect) {
|
|
67
|
-
throw new Error(
|
|
74
|
+
throw new Error("Gemini is showing a sign-in flow. Please sign in in Chrome and retry.");
|
|
68
75
|
}
|
|
69
|
-
throw new Error(
|
|
76
|
+
throw new Error("Timed out waiting for Gemini UI prompt input to become ready.");
|
|
70
77
|
}
|
|
71
78
|
}
|
|
72
79
|
async function selectMode(ctx) {
|
|
@@ -79,8 +86,8 @@ async function selectMode(ctx) {
|
|
|
79
86
|
}
|
|
80
87
|
return 'not-found';
|
|
81
88
|
})()`);
|
|
82
|
-
if (toolsClickResult !==
|
|
83
|
-
throw new Error(
|
|
89
|
+
if (toolsClickResult !== "clicked") {
|
|
90
|
+
throw new Error("Unable to open Gemini tools menu; Deep Think toggle is not accessible.");
|
|
84
91
|
}
|
|
85
92
|
await ctx.delay(1_000);
|
|
86
93
|
const deepThinkItemSelectors = asSelectorLiteral(GEMINI_DEEP_THINK_SELECTORS.toolsMenuItem);
|
|
@@ -94,7 +101,7 @@ async function selectMode(ctx) {
|
|
|
94
101
|
}
|
|
95
102
|
return 'not-found';
|
|
96
103
|
})()`);
|
|
97
|
-
if (deepThinkClickResult !==
|
|
104
|
+
if (deepThinkClickResult !== "clicked") {
|
|
98
105
|
throw new Error('Unable to select "Deep Think" from Gemini tools menu.');
|
|
99
106
|
}
|
|
100
107
|
await ctx.delay(1_500);
|
|
@@ -107,11 +114,11 @@ async function selectMode(ctx) {
|
|
|
107
114
|
return label.includes('deep think') || text.includes('deep think');
|
|
108
115
|
})()`);
|
|
109
116
|
if (!deepThinkActive) {
|
|
110
|
-
throw new Error(
|
|
117
|
+
throw new Error("Deep Think did not appear selected after clicking the tools menu item.");
|
|
111
118
|
}
|
|
112
119
|
}
|
|
113
120
|
async function typePrompt(ctx) {
|
|
114
|
-
ctx.log?.(
|
|
121
|
+
ctx.log?.("[gemini-web] Typing prompt...");
|
|
115
122
|
const inputSelector = asSelectorLiteral(GEMINI_DEEP_THINK_SELECTORS.input);
|
|
116
123
|
const typeResult = await ctx.evaluate(`(() => {
|
|
117
124
|
const editor = document.querySelector(${inputSelector});
|
|
@@ -127,13 +134,13 @@ async function typePrompt(ctx) {
|
|
|
127
134
|
const typed = (editor.textContent || '').trim().length > 0;
|
|
128
135
|
return typed ? 'typed' : 'empty';
|
|
129
136
|
})()`);
|
|
130
|
-
if (typeResult !==
|
|
131
|
-
throw new Error(`Failed to type Gemini prompt (status=${typeResult ??
|
|
137
|
+
if (typeResult !== "typed") {
|
|
138
|
+
throw new Error(`Failed to type Gemini prompt (status=${typeResult ?? "unknown"}).`);
|
|
132
139
|
}
|
|
133
140
|
await ctx.delay(500);
|
|
134
141
|
}
|
|
135
142
|
async function submitPrompt(ctx) {
|
|
136
|
-
ctx.log?.(
|
|
143
|
+
ctx.log?.("[gemini-web] Sending prompt...");
|
|
137
144
|
const inputSelector = asSelectorLiteral(GEMINI_DEEP_THINK_SELECTORS.input);
|
|
138
145
|
const sendButtonSelectors = asSelectorLiteral(GEMINI_DEEP_THINK_SELECTORS.sendButton);
|
|
139
146
|
const sendResult = await ctx.evaluate(`(() => {
|
|
@@ -150,12 +157,12 @@ async function submitPrompt(ctx) {
|
|
|
150
157
|
}
|
|
151
158
|
return 'not-found';
|
|
152
159
|
})()`);
|
|
153
|
-
if (sendResult !==
|
|
154
|
-
throw new Error(
|
|
160
|
+
if (sendResult !== "clicked" && sendResult !== "enter") {
|
|
161
|
+
throw new Error("Failed to submit prompt in Gemini Deep Think mode (send control not found).");
|
|
155
162
|
}
|
|
156
163
|
}
|
|
157
164
|
async function waitForResponse(ctx) {
|
|
158
|
-
ctx.log?.(
|
|
165
|
+
ctx.log?.("[gemini-web] Waiting for Deep Think response (this may take a while)...");
|
|
159
166
|
const responseTurnSel = asSelectorLiteral(GEMINI_DEEP_THINK_SELECTORS.responseTurn);
|
|
160
167
|
const responseTextSel = asSelectorLiteral(GEMINI_DEEP_THINK_SELECTORS.responseText);
|
|
161
168
|
const responseCompleteSel = asSelectorLiteral(GEMINI_DEEP_THINK_SELECTORS.responseComplete);
|
|
@@ -163,7 +170,7 @@ async function waitForResponse(ctx) {
|
|
|
163
170
|
const { responseTimeoutMs } = readTimeouts(ctx);
|
|
164
171
|
const responseDeadline = Date.now() + responseTimeoutMs;
|
|
165
172
|
let lastLog = 0;
|
|
166
|
-
let responseText =
|
|
173
|
+
let responseText = "";
|
|
167
174
|
while (Date.now() < responseDeadline) {
|
|
168
175
|
const payload = await ctx.evaluate(`(() => {
|
|
169
176
|
const turns = document.querySelectorAll(${responseTurnSel});
|
|
@@ -187,14 +194,14 @@ async function waitForResponse(ctx) {
|
|
|
187
194
|
return JSON.stringify({ status: 'generating' });
|
|
188
195
|
})()`);
|
|
189
196
|
try {
|
|
190
|
-
const parsed = JSON.parse(payload ??
|
|
191
|
-
if (parsed.status ===
|
|
197
|
+
const parsed = JSON.parse(payload ?? "{}");
|
|
198
|
+
if (parsed.status === "done" && typeof parsed.text === "string" && parsed.text.length > 0) {
|
|
192
199
|
responseText = parsed.text;
|
|
193
200
|
break;
|
|
194
201
|
}
|
|
195
202
|
const now = Date.now();
|
|
196
203
|
if (now - lastLog > 10_000) {
|
|
197
|
-
ctx.log?.(`[gemini-web] Deep Think still generating... (${parsed.status ??
|
|
204
|
+
ctx.log?.(`[gemini-web] Deep Think still generating... (${parsed.status ?? "unknown"})`);
|
|
198
205
|
lastLog = now;
|
|
199
206
|
}
|
|
200
207
|
}
|
|
@@ -217,7 +224,7 @@ async function extractThoughts(ctx) {
|
|
|
217
224
|
toggle.click();
|
|
218
225
|
return 'clicked';
|
|
219
226
|
})()`);
|
|
220
|
-
if (thinkResult !==
|
|
227
|
+
if (thinkResult !== "clicked") {
|
|
221
228
|
return null;
|
|
222
229
|
}
|
|
223
230
|
await ctx.delay(1_500);
|
|
@@ -232,10 +239,12 @@ async function extractThoughts(ctx) {
|
|
|
232
239
|
}
|
|
233
240
|
return full;
|
|
234
241
|
})()`);
|
|
235
|
-
return typeof extractedThoughts ===
|
|
242
|
+
return typeof extractedThoughts === "string" && extractedThoughts.length > 0
|
|
243
|
+
? extractedThoughts
|
|
244
|
+
: null;
|
|
236
245
|
}
|
|
237
246
|
export const geminiDeepThinkDomProvider = {
|
|
238
|
-
providerName:
|
|
247
|
+
providerName: "gemini-web",
|
|
239
248
|
waitForUi,
|
|
240
249
|
selectMode,
|
|
241
250
|
typePrompt,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { chatgptDomProvider } from
|
|
2
|
-
export { geminiDeepThinkDomProvider, GEMINI_DEEP_THINK_SELECTORS, } from
|
|
1
|
+
export { chatgptDomProvider } from "./chatgptDomProvider.js";
|
|
2
|
+
export { geminiDeepThinkDomProvider, GEMINI_DEEP_THINK_SELECTORS, } from "./geminiDeepThinkDomProvider.js";
|