@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.
Files changed (194) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +107 -49
  3. package/dist/bin/oracle-cli.js +551 -410
  4. package/dist/bin/oracle-mcp.js +2 -2
  5. package/dist/bin/oracle.js +165 -279
  6. package/dist/scripts/agent-send.js +31 -31
  7. package/dist/scripts/check.js +6 -6
  8. package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
  9. package/dist/scripts/docs-list.js +30 -30
  10. package/dist/scripts/git-policy.js +25 -23
  11. package/dist/scripts/run-cli.js +8 -8
  12. package/dist/scripts/runner.js +203 -195
  13. package/dist/scripts/test-browser.js +21 -18
  14. package/dist/scripts/test-remote-chrome.js +20 -20
  15. package/dist/src/bridge/connection.js +18 -18
  16. package/dist/src/bridge/userConfigFile.js +7 -7
  17. package/dist/src/browser/actions/archiveConversation.js +224 -0
  18. package/dist/src/browser/actions/assistantResponse.js +175 -101
  19. package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
  20. package/dist/src/browser/actions/attachments.js +246 -150
  21. package/dist/src/browser/actions/deepResearch.js +662 -0
  22. package/dist/src/browser/actions/domEvents.js +2 -2
  23. package/dist/src/browser/actions/modelSelection.js +342 -119
  24. package/dist/src/browser/actions/navigation.js +183 -137
  25. package/dist/src/browser/actions/projectSources.js +491 -0
  26. package/dist/src/browser/actions/promptComposer.js +152 -91
  27. package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
  28. package/dist/src/browser/actions/thinkingStatus.js +391 -0
  29. package/dist/src/browser/actions/thinkingTime.js +207 -110
  30. package/dist/src/browser/artifacts.js +150 -0
  31. package/dist/src/browser/attachRunning.js +31 -0
  32. package/dist/src/browser/chatgptImages.js +315 -0
  33. package/dist/src/browser/chromeLifecycle.js +276 -63
  34. package/dist/src/browser/config.js +59 -16
  35. package/dist/src/browser/constants.js +25 -12
  36. package/dist/src/browser/controlPlan.js +81 -0
  37. package/dist/src/browser/cookies.js +19 -19
  38. package/dist/src/browser/detect.js +250 -77
  39. package/dist/src/browser/domDebug.js +50 -1
  40. package/dist/src/browser/index.js +1559 -692
  41. package/dist/src/browser/liveTabs.js +434 -0
  42. package/dist/src/browser/modelStrategy.js +1 -1
  43. package/dist/src/browser/pageActions.js +5 -5
  44. package/dist/src/browser/policies.js +16 -13
  45. package/dist/src/browser/profileState.js +127 -42
  46. package/dist/src/browser/projectSourcesRunner.js +366 -0
  47. package/dist/src/browser/prompt.js +72 -42
  48. package/dist/src/browser/promptSummary.js +5 -5
  49. package/dist/src/browser/providerDomFlow.js +1 -1
  50. package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
  51. package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
  52. package/dist/src/browser/providers/index.js +2 -2
  53. package/dist/src/browser/reattach.js +178 -73
  54. package/dist/src/browser/reattachHelpers.js +32 -27
  55. package/dist/src/browser/sessionRunner.js +89 -25
  56. package/dist/src/browser/tabLeaseRegistry.js +182 -0
  57. package/dist/src/browser/utils.js +9 -9
  58. package/dist/src/browserMode.js +1 -1
  59. package/dist/src/cli/bridge/claudeConfig.js +24 -20
  60. package/dist/src/cli/bridge/client.js +28 -20
  61. package/dist/src/cli/bridge/codexConfig.js +16 -16
  62. package/dist/src/cli/bridge/doctor.js +47 -39
  63. package/dist/src/cli/bridge/host.js +58 -56
  64. package/dist/src/cli/browserConfig.js +102 -48
  65. package/dist/src/cli/browserDefaults.js +51 -26
  66. package/dist/src/cli/browserTabs.js +228 -0
  67. package/dist/src/cli/bundleWarnings.js +1 -1
  68. package/dist/src/cli/clipboard.js +11 -2
  69. package/dist/src/cli/detach.js +2 -2
  70. package/dist/src/cli/dryRun.js +62 -26
  71. package/dist/src/cli/duplicatePromptGuard.js +12 -4
  72. package/dist/src/cli/engine.js +9 -9
  73. package/dist/src/cli/errorUtils.js +1 -1
  74. package/dist/src/cli/fileSize.js +3 -3
  75. package/dist/src/cli/format.js +2 -2
  76. package/dist/src/cli/help.js +28 -28
  77. package/dist/src/cli/hiddenAliases.js +3 -3
  78. package/dist/src/cli/markdownBundle.js +7 -7
  79. package/dist/src/cli/markdownRenderer.js +15 -15
  80. package/dist/src/cli/notifier.js +77 -67
  81. package/dist/src/cli/options.js +131 -106
  82. package/dist/src/cli/oscUtils.js +1 -1
  83. package/dist/src/cli/projectSources.js +116 -0
  84. package/dist/src/cli/promptRequirement.js +2 -2
  85. package/dist/src/cli/renderOutput.js +1 -1
  86. package/dist/src/cli/rootAlias.js +1 -1
  87. package/dist/src/cli/runOptions.js +32 -28
  88. package/dist/src/cli/sessionCommand.js +82 -21
  89. package/dist/src/cli/sessionDisplay.js +213 -87
  90. package/dist/src/cli/sessionLineage.js +6 -2
  91. package/dist/src/cli/sessionRunner.js +149 -95
  92. package/dist/src/cli/sessionTable.js +26 -23
  93. package/dist/src/cli/stdin.js +22 -0
  94. package/dist/src/cli/tagline.js +121 -124
  95. package/dist/src/cli/tui/index.js +139 -128
  96. package/dist/src/cli/writeOutputPath.js +5 -5
  97. package/dist/src/config.js +7 -7
  98. package/dist/src/gemini-web/browserSessionManager.js +19 -15
  99. package/dist/src/gemini-web/client.js +76 -70
  100. package/dist/src/gemini-web/executionMode.js +6 -8
  101. package/dist/src/gemini-web/executor.js +98 -93
  102. package/dist/src/gemini-web/index.js +1 -1
  103. package/dist/src/mcp/consultPresets.js +19 -0
  104. package/dist/src/mcp/server.js +18 -12
  105. package/dist/src/mcp/tools/consult.js +246 -67
  106. package/dist/src/mcp/tools/projectSources.js +123 -0
  107. package/dist/src/mcp/tools/sessionResources.js +12 -12
  108. package/dist/src/mcp/tools/sessions.js +26 -17
  109. package/dist/src/mcp/types.js +12 -5
  110. package/dist/src/mcp/utils.js +21 -8
  111. package/dist/src/oracle/background.js +15 -15
  112. package/dist/src/oracle/claude.js +53 -25
  113. package/dist/src/oracle/client.js +50 -41
  114. package/dist/src/oracle/config.js +96 -66
  115. package/dist/src/oracle/errors.js +38 -38
  116. package/dist/src/oracle/files.js +55 -46
  117. package/dist/src/oracle/finishLine.js +10 -8
  118. package/dist/src/oracle/format.js +3 -3
  119. package/dist/src/oracle/gemini.js +37 -33
  120. package/dist/src/oracle/logging.js +7 -7
  121. package/dist/src/oracle/markdown.js +28 -28
  122. package/dist/src/oracle/modelResolver.js +16 -16
  123. package/dist/src/oracle/multiModelRunner.js +12 -12
  124. package/dist/src/oracle/oscProgress.js +8 -8
  125. package/dist/src/oracle/promptAssembly.js +6 -3
  126. package/dist/src/oracle/request.js +16 -13
  127. package/dist/src/oracle/run.js +160 -135
  128. package/dist/src/oracle/runUtils.js +8 -5
  129. package/dist/src/oracle/tokenEstimate.js +6 -6
  130. package/dist/src/oracle/tokenStats.js +5 -5
  131. package/dist/src/oracle/tokenStringifier.js +5 -5
  132. package/dist/src/oracle.js +12 -12
  133. package/dist/src/oracleHome.js +3 -3
  134. package/dist/src/projectSources/plan.js +27 -0
  135. package/dist/src/projectSources/url.js +23 -0
  136. package/dist/src/remote/client.js +25 -25
  137. package/dist/src/remote/health.js +20 -20
  138. package/dist/src/remote/remoteServiceConfig.js +9 -9
  139. package/dist/src/remote/server.js +129 -118
  140. package/dist/src/sessionManager.js +78 -75
  141. package/dist/src/sessionStore.js +3 -3
  142. package/dist/src/version.js +10 -10
  143. package/dist/vendor/oracle-notifier/README.md +2 -0
  144. package/package.json +67 -62
  145. package/vendor/oracle-notifier/README.md +2 -0
  146. package/dist/markdansi/types/index.js +0 -4
  147. package/dist/oracle/bin/oracle-cli.js +0 -472
  148. package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
  149. package/dist/oracle/src/browser/actions/attachments.js +0 -82
  150. package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
  151. package/dist/oracle/src/browser/actions/navigation.js +0 -75
  152. package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
  153. package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
  154. package/dist/oracle/src/browser/config.js +0 -33
  155. package/dist/oracle/src/browser/constants.js +0 -40
  156. package/dist/oracle/src/browser/cookies.js +0 -210
  157. package/dist/oracle/src/browser/domDebug.js +0 -36
  158. package/dist/oracle/src/browser/index.js +0 -331
  159. package/dist/oracle/src/browser/pageActions.js +0 -5
  160. package/dist/oracle/src/browser/prompt.js +0 -88
  161. package/dist/oracle/src/browser/promptSummary.js +0 -20
  162. package/dist/oracle/src/browser/sessionRunner.js +0 -80
  163. package/dist/oracle/src/browser/utils.js +0 -62
  164. package/dist/oracle/src/browserMode.js +0 -1
  165. package/dist/oracle/src/cli/browserConfig.js +0 -44
  166. package/dist/oracle/src/cli/dryRun.js +0 -59
  167. package/dist/oracle/src/cli/engine.js +0 -17
  168. package/dist/oracle/src/cli/errorUtils.js +0 -9
  169. package/dist/oracle/src/cli/help.js +0 -70
  170. package/dist/oracle/src/cli/markdownRenderer.js +0 -15
  171. package/dist/oracle/src/cli/options.js +0 -103
  172. package/dist/oracle/src/cli/promptRequirement.js +0 -14
  173. package/dist/oracle/src/cli/rootAlias.js +0 -30
  174. package/dist/oracle/src/cli/sessionCommand.js +0 -77
  175. package/dist/oracle/src/cli/sessionDisplay.js +0 -270
  176. package/dist/oracle/src/cli/sessionRunner.js +0 -94
  177. package/dist/oracle/src/heartbeat.js +0 -43
  178. package/dist/oracle/src/oracle/client.js +0 -48
  179. package/dist/oracle/src/oracle/config.js +0 -29
  180. package/dist/oracle/src/oracle/errors.js +0 -101
  181. package/dist/oracle/src/oracle/files.js +0 -220
  182. package/dist/oracle/src/oracle/format.js +0 -33
  183. package/dist/oracle/src/oracle/fsAdapter.js +0 -7
  184. package/dist/oracle/src/oracle/oscProgress.js +0 -60
  185. package/dist/oracle/src/oracle/request.js +0 -48
  186. package/dist/oracle/src/oracle/run.js +0 -444
  187. package/dist/oracle/src/oracle/tokenStats.js +0 -39
  188. package/dist/oracle/src/oracle/types.js +0 -1
  189. package/dist/oracle/src/oracle.js +0 -9
  190. package/dist/oracle/src/sessionManager.js +0 -205
  191. package/dist/oracle/src/version.js +0 -39
  192. package/dist/scripts/chrome/browser-tools.js +0 -295
  193. package/dist/src/browser/profileSync.js +0 -141
  194. /package/dist/{oracle/src/browser → src/projectSources}/types.js +0 -0
@@ -1,16 +1,34 @@
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';
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
- '.mp4', '.mov', '.avi', '.mkv', '.webm', '.m4v',
11
- '.mp3', '.wav', '.aac', '.flac', '.ogg', '.m4a',
12
- '.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg', '.heic', '.heif',
13
- '.pdf',
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 ?? '').trim();
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
- ? 'never'
42
- : runOptions.browserAttachments ?? 'auto';
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].filter(Boolean).join('\n\n').trim();
52
- const selectedPlan = attachmentsPolicy === 'always'
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 === 'never'
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('\n\n')
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(), 'oracle-browser-bundle-'));
71
- const bundlePath = path.join(bundleDir, 'attachments-bundle.txt');
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.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd()}\n`;
78
- await fs.writeFile(bundlePath, bundleText, 'utf8');
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, 'utf8'),
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) ? MODEL_CONFIGS[runOptions.model] : MODEL_CONFIGS['gpt-5.1'];
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].filter((value) => Boolean(value?.trim())).join('\n\n').trim()
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: 'system', content: systemPrompt } : null,
96
- tokenizerUserContent ? { role: 'user', content: tokenizerUserContent } : null,
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('\n\n');
107
- const attachmentTokens = tokenizer([{ role: 'user', content: attachmentText }], TOKENIZER_OPTIONS);
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 === 'auto' && selectedPlan.mode === 'inline' && sections.length > 0) {
112
- const fallbackComposerText = baseComposerSections.join('\n\n').trim();
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(), 'oracle-browser-bundle-'));
117
- const bundlePath = path.join(bundleDir, 'attachments-bundle.txt');
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.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd()}\n`;
124
- await fs.writeFile(bundlePath, fallbackBundleText, 'utf8');
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, 'utf8'),
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 './utils.js';
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 ? '' : 's';
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 ? '' : 's';
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 !== 'number' || Number.isNaN(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)})`;
@@ -13,5 +13,5 @@ export async function runProviderDomFlow(adapter, ctx) {
13
13
  return { ...response, thoughts };
14
14
  }
15
15
  export function joinSelectors(selectors) {
16
- return selectors.join(', ');
16
+ return selectors.join(", ");
17
17
  }
@@ -1,10 +1,10 @@
1
- import { ensurePromptReady } from '../actions/navigation.js';
2
- import { submitPrompt } from '../actions/promptComposer.js';
3
- import { waitForAssistantResponse } from '../actions/assistantResponse.js';
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('chatgptDomProvider requires runtime/input/logger in context.state.');
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 = typeof committedTurns === 'number' && Number.isFinite(committedTurns)
28
- ? committedTurns
29
- : null;
30
- if (state.committedTurns != null && (state.baselineTurns == null || state.committedTurns > state.baselineTurns)) {
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: 'chatgpt-web',
44
+ providerName: "chatgpt-web",
45
45
  waitForUi,
46
46
  typePrompt,
47
47
  submitPrompt: submitPromptViaAdapter,
@@ -1,43 +1,50 @@
1
- import { joinSelectors } from '../providerDomFlow.js';
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: ['rich-textarea .ql-editor', '[role="textbox"][aria-label*="prompt" i]', 'div[contenteditable="true"]'],
6
- sendButton: ['button.send-button', 'button[aria-label="Send message"]'],
7
- toolsButton: ['button.toolbox-drawer-button', 'button[aria-label="Tools"]'],
8
- toolsMenuItem: ['[role="menuitemcheckbox"]', '.toolbox-drawer-item-list-button'],
9
- deepThinkActive: ['.toolbox-drawer-item-deselect-button', 'button[aria-label*="Deselect Deep Think"]'],
10
- uploadButton: ['button[aria-label="Open upload file menu"]', '.upload-card-button'],
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: ['.hidden-local-file-upload-button', '.hidden-local-upload-button'],
13
- uploaderContainer: ['.uploader-button-container', '.file-uploader'],
14
- uploaderElement: ['uploader.upload-button'],
15
- userTurnAttachment: ['.file-preview-container'],
16
- responseTurn: ['model-response'],
17
- responseText: ['message-content', '.model-response-text message-content'],
18
- responseComplete: ['.response-footer.complete'],
19
- userQuery: ['user-query'],
20
- userQueryText: ['user-query-content', '.query-text'],
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: ['.thoughts-header-button', '[data-test-id="thoughts-header-button"]'],
23
- thoughtsContent: ['model-thoughts', '[data-test-id="model-thoughts"]'],
24
- hasThoughts: ['.has-thoughts'],
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 === 'number' && Number.isFinite(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 === 'number' && Number.isFinite(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?.('[gemini-web] Waiting for Gemini UI to load...');
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('Gemini is showing a sign-in flow. Please sign in in Chrome and retry.');
74
+ throw new Error("Gemini is showing a sign-in flow. Please sign in in Chrome and retry.");
68
75
  }
69
- throw new Error('Timed out waiting for Gemini UI prompt input to become ready.');
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 !== 'clicked') {
83
- throw new Error('Unable to open Gemini tools menu; Deep Think toggle is not accessible.');
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 !== 'clicked') {
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('Deep Think did not appear selected after clicking the tools menu item.');
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?.('[gemini-web] Typing prompt...');
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 !== 'typed') {
131
- throw new Error(`Failed to type Gemini prompt (status=${typeResult ?? 'unknown'}).`);
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?.('[gemini-web] Sending prompt...');
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 !== 'clicked' && sendResult !== 'enter') {
154
- throw new Error('Failed to submit prompt in Gemini Deep Think mode (send control not found).');
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?.('[gemini-web] Waiting for Deep Think response (this may take a while)...');
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 === 'done' && typeof parsed.text === 'string' && parsed.text.length > 0) {
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 ?? 'unknown'})`);
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 !== 'clicked') {
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 === 'string' && extractedThoughts.length > 0 ? extractedThoughts : null;
242
+ return typeof extractedThoughts === "string" && extractedThoughts.length > 0
243
+ ? extractedThoughts
244
+ : null;
236
245
  }
237
246
  export const geminiDeepThinkDomProvider = {
238
- providerName: 'gemini-web',
247
+ providerName: "gemini-web",
239
248
  waitForUi,
240
249
  selectMode,
241
250
  typePrompt,
@@ -1,2 +1,2 @@
1
- export { chatgptDomProvider } from './chatgptDomProvider.js';
2
- export { geminiDeepThinkDomProvider, GEMINI_DEEP_THINK_SELECTORS, } from './geminiDeepThinkDomProvider.js';
1
+ export { chatgptDomProvider } from "./chatgptDomProvider.js";
2
+ export { geminiDeepThinkDomProvider, GEMINI_DEEP_THINK_SELECTORS, } from "./geminiDeepThinkDomProvider.js";