@steipete/oracle 0.8.6 → 0.10.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 (181) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +130 -45
  3. package/dist/bin/oracle-cli.js +613 -379
  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/assistantResponse.js +149 -101
  18. package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
  19. package/dist/src/browser/actions/attachments.js +246 -150
  20. package/dist/src/browser/actions/domEvents.js +2 -2
  21. package/dist/src/browser/actions/modelSelection.js +314 -104
  22. package/dist/src/browser/actions/navigation.js +161 -136
  23. package/dist/src/browser/actions/promptComposer.js +100 -64
  24. package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
  25. package/dist/src/browser/actions/thinkingTime.js +207 -110
  26. package/dist/src/browser/chromeLifecycle.js +62 -60
  27. package/dist/src/browser/config.js +34 -15
  28. package/dist/src/browser/constants.js +17 -12
  29. package/dist/src/browser/cookies.js +19 -19
  30. package/dist/src/browser/detect.js +62 -62
  31. package/dist/src/browser/domDebug.js +1 -1
  32. package/dist/src/browser/index.js +452 -303
  33. package/dist/src/browser/modelStrategy.js +1 -1
  34. package/dist/src/browser/pageActions.js +5 -5
  35. package/dist/src/browser/policies.js +16 -13
  36. package/dist/src/browser/profileState.js +44 -39
  37. package/dist/src/browser/prompt.js +72 -42
  38. package/dist/src/browser/promptSummary.js +5 -5
  39. package/dist/src/browser/providerDomFlow.js +17 -0
  40. package/dist/src/browser/providers/chatgptDomProvider.js +49 -0
  41. package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +254 -0
  42. package/dist/src/browser/providers/index.js +2 -0
  43. package/dist/src/browser/reattach.js +67 -34
  44. package/dist/src/browser/reattachHelpers.js +31 -26
  45. package/dist/src/browser/sessionRunner.js +37 -25
  46. package/dist/src/browser/utils.js +9 -9
  47. package/dist/src/browserMode.js +1 -1
  48. package/dist/src/cli/bridge/claudeConfig.js +16 -16
  49. package/dist/src/cli/bridge/client.js +28 -20
  50. package/dist/src/cli/bridge/codexConfig.js +16 -16
  51. package/dist/src/cli/bridge/doctor.js +47 -39
  52. package/dist/src/cli/bridge/host.js +58 -56
  53. package/dist/src/cli/browserConfig.js +65 -45
  54. package/dist/src/cli/browserDefaults.js +27 -26
  55. package/dist/src/cli/bundleWarnings.js +1 -1
  56. package/dist/src/cli/clipboard.js +11 -2
  57. package/dist/src/cli/detach.js +7 -4
  58. package/dist/src/cli/dryRun.js +29 -25
  59. package/dist/src/cli/duplicatePromptGuard.js +3 -3
  60. package/dist/src/cli/engine.js +9 -9
  61. package/dist/src/cli/errorUtils.js +1 -1
  62. package/dist/src/cli/fileSize.js +11 -0
  63. package/dist/src/cli/format.js +2 -2
  64. package/dist/src/cli/help.js +28 -28
  65. package/dist/src/cli/hiddenAliases.js +3 -3
  66. package/dist/src/cli/markdownBundle.js +12 -8
  67. package/dist/src/cli/markdownRenderer.js +15 -15
  68. package/dist/src/cli/notifier.js +77 -67
  69. package/dist/src/cli/options.js +145 -87
  70. package/dist/src/cli/oscUtils.js +1 -1
  71. package/dist/src/cli/promptRequirement.js +2 -2
  72. package/dist/src/cli/renderOutput.js +1 -1
  73. package/dist/src/cli/rootAlias.js +1 -1
  74. package/dist/src/cli/runOptions.js +37 -25
  75. package/dist/src/cli/sessionCommand.js +31 -21
  76. package/dist/src/cli/sessionDisplay.js +182 -79
  77. package/dist/src/cli/sessionLineage.js +60 -0
  78. package/dist/src/cli/sessionRunner.js +118 -90
  79. package/dist/src/cli/sessionTable.js +28 -24
  80. package/dist/src/cli/stdin.js +22 -0
  81. package/dist/src/cli/tagline.js +121 -124
  82. package/dist/src/cli/tui/index.js +140 -127
  83. package/dist/src/cli/writeOutputPath.js +5 -5
  84. package/dist/src/config.js +7 -7
  85. package/dist/src/gemini-web/browserSessionManager.js +80 -0
  86. package/dist/src/gemini-web/client.js +81 -64
  87. package/dist/src/gemini-web/executionMode.js +16 -0
  88. package/dist/src/gemini-web/executor.js +327 -169
  89. package/dist/src/gemini-web/index.js +1 -1
  90. package/dist/src/mcp/server.js +16 -12
  91. package/dist/src/mcp/tools/consult.js +81 -64
  92. package/dist/src/mcp/tools/sessionResources.js +12 -12
  93. package/dist/src/mcp/tools/sessions.js +26 -17
  94. package/dist/src/mcp/types.js +5 -5
  95. package/dist/src/mcp/utils.js +15 -7
  96. package/dist/src/oracle/background.js +15 -15
  97. package/dist/src/oracle/claude.js +53 -25
  98. package/dist/src/oracle/client.js +84 -46
  99. package/dist/src/oracle/config.js +124 -58
  100. package/dist/src/oracle/errors.js +38 -38
  101. package/dist/src/oracle/files.js +69 -45
  102. package/dist/src/oracle/finishLine.js +10 -8
  103. package/dist/src/oracle/format.js +3 -3
  104. package/dist/src/oracle/gemini.js +37 -30
  105. package/dist/src/oracle/logging.js +7 -7
  106. package/dist/src/oracle/markdown.js +28 -28
  107. package/dist/src/oracle/modelResolver.js +16 -16
  108. package/dist/src/oracle/multiModelRunner.js +12 -12
  109. package/dist/src/oracle/oscProgress.js +8 -8
  110. package/dist/src/oracle/promptAssembly.js +6 -3
  111. package/dist/src/oracle/request.js +23 -15
  112. package/dist/src/oracle/run.js +172 -140
  113. package/dist/src/oracle/runUtils.js +8 -5
  114. package/dist/src/oracle/tokenEstimate.js +6 -6
  115. package/dist/src/oracle/tokenStats.js +5 -5
  116. package/dist/src/oracle/tokenStringifier.js +5 -5
  117. package/dist/src/oracle.js +12 -12
  118. package/dist/src/oracleHome.js +3 -3
  119. package/dist/src/remote/client.js +25 -25
  120. package/dist/src/remote/health.js +20 -20
  121. package/dist/src/remote/remoteServiceConfig.js +9 -9
  122. package/dist/src/remote/server.js +129 -118
  123. package/dist/src/sessionManager.js +81 -75
  124. package/dist/src/sessionStore.js +3 -3
  125. package/dist/src/version.js +10 -10
  126. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  127. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  128. package/dist/vendor/oracle-notifier/README.md +2 -0
  129. package/package.json +69 -65
  130. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  131. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  132. package/vendor/oracle-notifier/README.md +2 -0
  133. package/dist/markdansi/types/index.js +0 -4
  134. package/dist/oracle/bin/oracle-cli.js +0 -472
  135. package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
  136. package/dist/oracle/src/browser/actions/attachments.js +0 -82
  137. package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
  138. package/dist/oracle/src/browser/actions/navigation.js +0 -75
  139. package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
  140. package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
  141. package/dist/oracle/src/browser/config.js +0 -33
  142. package/dist/oracle/src/browser/constants.js +0 -40
  143. package/dist/oracle/src/browser/cookies.js +0 -210
  144. package/dist/oracle/src/browser/domDebug.js +0 -36
  145. package/dist/oracle/src/browser/index.js +0 -331
  146. package/dist/oracle/src/browser/pageActions.js +0 -5
  147. package/dist/oracle/src/browser/prompt.js +0 -88
  148. package/dist/oracle/src/browser/promptSummary.js +0 -20
  149. package/dist/oracle/src/browser/sessionRunner.js +0 -80
  150. package/dist/oracle/src/browser/utils.js +0 -62
  151. package/dist/oracle/src/browserMode.js +0 -1
  152. package/dist/oracle/src/cli/browserConfig.js +0 -44
  153. package/dist/oracle/src/cli/dryRun.js +0 -59
  154. package/dist/oracle/src/cli/engine.js +0 -17
  155. package/dist/oracle/src/cli/errorUtils.js +0 -9
  156. package/dist/oracle/src/cli/help.js +0 -70
  157. package/dist/oracle/src/cli/markdownRenderer.js +0 -15
  158. package/dist/oracle/src/cli/options.js +0 -103
  159. package/dist/oracle/src/cli/promptRequirement.js +0 -14
  160. package/dist/oracle/src/cli/rootAlias.js +0 -30
  161. package/dist/oracle/src/cli/sessionCommand.js +0 -77
  162. package/dist/oracle/src/cli/sessionDisplay.js +0 -270
  163. package/dist/oracle/src/cli/sessionRunner.js +0 -94
  164. package/dist/oracle/src/heartbeat.js +0 -43
  165. package/dist/oracle/src/oracle/client.js +0 -48
  166. package/dist/oracle/src/oracle/config.js +0 -29
  167. package/dist/oracle/src/oracle/errors.js +0 -101
  168. package/dist/oracle/src/oracle/files.js +0 -220
  169. package/dist/oracle/src/oracle/format.js +0 -33
  170. package/dist/oracle/src/oracle/fsAdapter.js +0 -7
  171. package/dist/oracle/src/oracle/oscProgress.js +0 -60
  172. package/dist/oracle/src/oracle/request.js +0 -48
  173. package/dist/oracle/src/oracle/run.js +0 -444
  174. package/dist/oracle/src/oracle/tokenStats.js +0 -39
  175. package/dist/oracle/src/oracle/types.js +0 -1
  176. package/dist/oracle/src/oracle.js +0 -9
  177. package/dist/oracle/src/sessionManager.js +0 -205
  178. package/dist/oracle/src/version.js +0 -39
  179. package/dist/scripts/chrome/browser-tools.js +0 -295
  180. package/dist/src/browser/profileSync.js +0 -141
  181. /package/dist/{oracle/src/browser/types.js → src/gemini-web/executionClients.js} +0 -0
@@ -1,14 +1,17 @@
1
- import { InvalidArgumentError } from 'commander';
2
- import { parseDuration } from '../browserMode.js';
3
- import path from 'node:path';
4
- import fg from 'fast-glob';
5
- import { DEFAULT_MODEL, MODEL_CONFIGS } from '../oracle.js';
1
+ import { InvalidArgumentError } from "commander";
2
+ import { parseDuration } from "../browserMode.js";
3
+ import path from "node:path";
4
+ import fg from "fast-glob";
5
+ import { DEFAULT_MODEL, MODEL_CONFIGS } from "../oracle.js";
6
6
  export function collectPaths(value, previous = []) {
7
7
  if (!value) {
8
8
  return previous;
9
9
  }
10
10
  const nextValues = Array.isArray(value) ? value : [value];
11
- return previous.concat(nextValues.flatMap((entry) => entry.split(',')).map((entry) => entry.trim()).filter(Boolean));
11
+ return previous.concat(nextValues
12
+ .flatMap((entry) => entry.split(","))
13
+ .map((entry) => entry.trim())
14
+ .filter(Boolean));
12
15
  }
13
16
  /**
14
17
  * Merge all path-like CLI inputs (file/include aliases) into a single list, preserving order.
@@ -29,7 +32,7 @@ export function dedupePathInputs(inputs, { cwd = process.cwd() } = {}) {
29
32
  if (!raw)
30
33
  continue;
31
34
  let key = raw;
32
- if (!raw.startsWith('!') && !fg.isDynamicPattern(raw)) {
35
+ if (!raw.startsWith("!") && !fg.isDynamicPattern(raw)) {
33
36
  const absolute = path.isAbsolute(raw) ? raw : path.resolve(cwd, raw);
34
37
  key = `path:${path.normalize(absolute)}`;
35
38
  }
@@ -50,7 +53,7 @@ export function collectModelList(value, previous = []) {
50
53
  return previous;
51
54
  }
52
55
  const entries = value
53
- .split(',')
56
+ .split(",")
54
57
  .map((entry) => entry.trim())
55
58
  .filter((entry) => entry.length > 0);
56
59
  return previous.concat(entries);
@@ -58,7 +61,7 @@ export function collectModelList(value, previous = []) {
58
61
  export function parseFloatOption(value) {
59
62
  const parsed = Number.parseFloat(value);
60
63
  if (Number.isNaN(parsed)) {
61
- throw new InvalidArgumentError('Value must be a number.');
64
+ throw new InvalidArgumentError("Value must be a number.");
62
65
  }
63
66
  return parsed;
64
67
  }
@@ -68,7 +71,7 @@ export function parseIntOption(value) {
68
71
  }
69
72
  const parsed = Number.parseInt(value, 10);
70
73
  if (Number.isNaN(parsed)) {
71
- throw new InvalidArgumentError('Value must be an integer.');
74
+ throw new InvalidArgumentError("Value must be an integer.");
72
75
  }
73
76
  return parsed;
74
77
  }
@@ -76,9 +79,9 @@ export function parseHeartbeatOption(value) {
76
79
  if (value == null) {
77
80
  return 30;
78
81
  }
79
- if (typeof value === 'number') {
82
+ if (typeof value === "number") {
80
83
  if (Number.isNaN(value) || value < 0) {
81
- throw new InvalidArgumentError('Heartbeat interval must be zero or a positive number.');
84
+ throw new InvalidArgumentError("Heartbeat interval must be zero or a positive number.");
82
85
  }
83
86
  return value;
84
87
  }
@@ -86,42 +89,42 @@ export function parseHeartbeatOption(value) {
86
89
  if (normalized.length === 0) {
87
90
  return 30;
88
91
  }
89
- if (normalized === 'false' || normalized === 'off') {
92
+ if (normalized === "false" || normalized === "off") {
90
93
  return 0;
91
94
  }
92
95
  const parsed = Number.parseFloat(normalized);
93
96
  if (Number.isNaN(parsed) || parsed < 0) {
94
- throw new InvalidArgumentError('Heartbeat interval must be zero or a positive number.');
97
+ throw new InvalidArgumentError("Heartbeat interval must be zero or a positive number.");
95
98
  }
96
99
  return parsed;
97
100
  }
98
101
  export function usesDefaultStatusFilters(cmd) {
99
- const hoursSource = cmd.getOptionValueSource?.('hours') ?? 'default';
100
- const limitSource = cmd.getOptionValueSource?.('limit') ?? 'default';
101
- const allSource = cmd.getOptionValueSource?.('all') ?? 'default';
102
- return hoursSource === 'default' && limitSource === 'default' && allSource === 'default';
102
+ const hoursSource = cmd.getOptionValueSource?.("hours") ?? "default";
103
+ const limitSource = cmd.getOptionValueSource?.("limit") ?? "default";
104
+ const allSource = cmd.getOptionValueSource?.("all") ?? "default";
105
+ return hoursSource === "default" && limitSource === "default" && allSource === "default";
103
106
  }
104
107
  export function resolvePreviewMode(value) {
105
- if (typeof value === 'string' && value.length > 0) {
108
+ if (typeof value === "string" && value.length > 0) {
106
109
  return value;
107
110
  }
108
111
  if (value === true) {
109
- return 'summary';
112
+ return "summary";
110
113
  }
111
114
  return undefined;
112
115
  }
113
116
  export function parseSearchOption(value) {
114
117
  const normalized = value.trim().toLowerCase();
115
- if (['on', 'true', '1', 'yes'].includes(normalized)) {
118
+ if (["on", "true", "1", "yes"].includes(normalized)) {
116
119
  return true;
117
120
  }
118
- if (['off', 'false', '0', 'no'].includes(normalized)) {
121
+ if (["off", "false", "0", "no"].includes(normalized)) {
119
122
  return false;
120
123
  }
121
124
  throw new InvalidArgumentError('Search mode must be "on" or "off".');
122
125
  }
123
126
  export function normalizeModelOption(value) {
124
- return (value ?? '').trim();
127
+ return (value ?? "").trim();
125
128
  }
126
129
  export function normalizeBaseUrl(value) {
127
130
  const trimmed = value?.trim();
@@ -131,8 +134,8 @@ export function parseTimeoutOption(value) {
131
134
  if (value == null)
132
135
  return undefined;
133
136
  const normalized = value.trim().toLowerCase();
134
- if (normalized === 'auto')
135
- return 'auto';
137
+ if (normalized === "auto")
138
+ return "auto";
136
139
  const parsed = Number.parseFloat(normalized);
137
140
  if (Number.isNaN(parsed) || parsed <= 0) {
138
141
  throw new InvalidArgumentError('Timeout must be a positive number of seconds or "auto".');
@@ -152,49 +155,76 @@ export function parseDurationOption(value, label) {
152
155
  }
153
156
  return parsed;
154
157
  }
158
+ function isGeminiDeepThinkAlias(normalized) {
159
+ return ((normalized.includes("gemini") && normalized.includes("deep")) ||
160
+ normalized.includes("deep-think") ||
161
+ normalized.includes("deep_think") ||
162
+ normalized.includes("deepthink"));
163
+ }
155
164
  export function resolveApiModel(modelValue) {
156
165
  const normalized = normalizeModelOption(modelValue).toLowerCase();
157
166
  if (normalized in MODEL_CONFIGS) {
158
167
  return normalized;
159
168
  }
160
- if (normalized.includes('grok')) {
161
- return 'grok-4.1';
169
+ if (normalized.includes("/")) {
170
+ return normalized;
171
+ }
172
+ if (normalized.includes("grok")) {
173
+ return "grok-4.1";
174
+ }
175
+ if (normalized.includes("claude") && normalized.includes("sonnet")) {
176
+ return "claude-4.6-sonnet";
177
+ }
178
+ if (normalized.includes("claude") && normalized.includes("opus")) {
179
+ return "claude-4.1-opus";
162
180
  }
163
- if (normalized.includes('claude') && normalized.includes('sonnet')) {
164
- return 'claude-4.5-sonnet';
181
+ if (normalized.includes("5.5") && normalized.includes("pro")) {
182
+ return "gpt-5.5-pro";
165
183
  }
166
- if (normalized.includes('claude') && normalized.includes('opus')) {
167
- return 'claude-4.1-opus';
184
+ if (normalized.includes("5.5")) {
185
+ return "gpt-5.5";
168
186
  }
169
- if (normalized === 'claude' || normalized === 'sonnet' || /(^|\b)sonnet(\b|$)/.test(normalized)) {
170
- return 'claude-4.5-sonnet';
187
+ if (normalized.includes("5.4") && normalized.includes("pro")) {
188
+ return "gpt-5.4-pro";
171
189
  }
172
- if (normalized === 'opus' || normalized === 'claude-4.1') {
173
- return 'claude-4.1-opus';
190
+ if (normalized.includes("5.4")) {
191
+ return "gpt-5.4";
174
192
  }
175
- if (normalized.includes('5.0') || normalized === 'gpt-5-pro' || normalized === 'gpt-5') {
176
- return 'gpt-5-pro';
193
+ if (normalized === "claude" || normalized === "sonnet" || /(^|\b)sonnet(\b|$)/.test(normalized)) {
194
+ return "claude-4.6-sonnet";
177
195
  }
178
- if (normalized.includes('5-pro') && !normalized.includes('5.1')) {
179
- return 'gpt-5-pro';
196
+ if (normalized === "opus" || normalized === "claude-4.1") {
197
+ return "claude-4.1-opus";
180
198
  }
181
- if (normalized.includes('5.2') && normalized.includes('pro')) {
182
- return 'gpt-5.2-pro';
199
+ if (normalized.includes("5.0") || normalized === "gpt-5-pro" || normalized === "gpt-5") {
200
+ return "gpt-5-pro";
183
201
  }
184
- if (normalized.includes('5.1') && normalized.includes('pro')) {
185
- return 'gpt-5.1-pro';
202
+ if (normalized.includes("5-pro") && !normalized.includes("5.1")) {
203
+ return "gpt-5-pro";
186
204
  }
187
- if (normalized.includes('codex')) {
188
- if (normalized.includes('max')) {
189
- throw new InvalidArgumentError('gpt-5.1-codex-max is not available yet. OpenAI has not released the API.');
205
+ if (normalized.includes("5.2") && normalized.includes("pro")) {
206
+ return "gpt-5.2-pro";
207
+ }
208
+ if (normalized.includes("5.1") && normalized.includes("pro")) {
209
+ return "gpt-5.1-pro";
210
+ }
211
+ if (normalized.includes("codex")) {
212
+ if (normalized.includes("max")) {
213
+ throw new InvalidArgumentError("gpt-5.1-codex-max is not available yet. OpenAI has not released the API.");
190
214
  }
191
- return 'gpt-5.1-codex';
215
+ return "gpt-5.1-codex";
192
216
  }
193
- if (normalized.includes('gemini')) {
194
- return 'gemini-3-pro';
217
+ if (isGeminiDeepThinkAlias(normalized)) {
218
+ throw new InvalidArgumentError("Gemini Deep Think is browser-only today. Use --engine browser --model gemini-3-deep-think.");
195
219
  }
196
- if (normalized.includes('pro')) {
197
- return 'gpt-5.2-pro';
220
+ if (normalized.includes("gemini")) {
221
+ if (normalized.includes("3.1") || normalized.includes("3_1")) {
222
+ return "gemini-3.1-pro";
223
+ }
224
+ return "gemini-3-pro";
225
+ }
226
+ if (normalized.includes("pro")) {
227
+ return DEFAULT_MODEL;
198
228
  }
199
229
  // Passthrough for custom/OpenRouter model IDs.
200
230
  return normalized;
@@ -207,57 +237,85 @@ export function inferModelFromLabel(modelValue) {
207
237
  if (normalized in MODEL_CONFIGS) {
208
238
  return normalized;
209
239
  }
210
- if (normalized.includes('grok')) {
211
- return 'grok-4.1';
240
+ if (normalized.includes("/")) {
241
+ return normalized;
242
+ }
243
+ if (normalized.includes("grok")) {
244
+ return "grok-4.1";
245
+ }
246
+ if (normalized.includes("claude") && normalized.includes("sonnet")) {
247
+ return "claude-4.6-sonnet";
248
+ }
249
+ if (normalized.includes("claude") && normalized.includes("opus")) {
250
+ return "claude-4.1-opus";
251
+ }
252
+ if (normalized.includes("codex")) {
253
+ return "gpt-5.1-codex";
212
254
  }
213
- if (normalized.includes('claude') && normalized.includes('sonnet')) {
214
- return 'claude-4.5-sonnet';
255
+ if (isGeminiDeepThinkAlias(normalized)) {
256
+ return "gemini-3-pro-deep-think";
215
257
  }
216
- if (normalized.includes('claude') && normalized.includes('opus')) {
217
- return 'claude-4.1-opus';
258
+ if (normalized.includes("gemini")) {
259
+ if (normalized.includes("3.1") || normalized.includes("3_1")) {
260
+ return "gemini-3.1-pro";
261
+ }
262
+ return "gemini-3-pro";
263
+ }
264
+ if (normalized.includes("classic")) {
265
+ return "gpt-5-pro";
266
+ }
267
+ if (normalized.includes("thinking") && normalized.includes("heavy")) {
268
+ return "gpt-5.5";
218
269
  }
219
- if (normalized.includes('codex')) {
220
- return 'gpt-5.1-codex';
270
+ if ((normalized.includes("5.5") || normalized.includes("5_5")) && normalized.includes("pro")) {
271
+ return "gpt-5.5-pro";
221
272
  }
222
- if (normalized.includes('gemini')) {
223
- return 'gemini-3-pro';
273
+ if (normalized.includes("5.5") || normalized.includes("5_5")) {
274
+ return "gpt-5.5";
224
275
  }
225
- if (normalized.includes('classic')) {
226
- return 'gpt-5-pro';
276
+ if ((normalized.includes("5.4") || normalized.includes("5_4")) && normalized.includes("pro")) {
277
+ return "gpt-5.4-pro";
227
278
  }
228
- if ((normalized.includes('5.2') || normalized.includes('5_2')) && normalized.includes('pro')) {
229
- return 'gpt-5.2-pro';
279
+ if (normalized.includes("5.4") || normalized.includes("5_4")) {
280
+ return "gpt-5.4";
281
+ }
282
+ if ((normalized.includes("5.2") || normalized.includes("5_2")) && normalized.includes("pro")) {
283
+ return "gpt-5.2-pro";
230
284
  }
231
285
  // Browser-only: pass through 5.2 thinking/instant variants for browser label mapping
232
- if ((normalized.includes('5.2') || normalized.includes('5_2')) && normalized.includes('thinking')) {
233
- return 'gpt-5.2-thinking';
286
+ if ((normalized.includes("5.2") || normalized.includes("5_2")) &&
287
+ normalized.includes("thinking")) {
288
+ return "gpt-5.2-thinking";
234
289
  }
235
- if ((normalized.includes('5.2') || normalized.includes('5_2')) && normalized.includes('instant')) {
236
- return 'gpt-5.2-instant';
290
+ if ((normalized.includes("5.2") || normalized.includes("5_2")) &&
291
+ normalized.includes("instant")) {
292
+ return "gpt-5.2-instant";
237
293
  }
238
- if (normalized.includes('5.0') || normalized.includes('5-pro')) {
239
- return 'gpt-5-pro';
294
+ if (normalized.includes("5.0") || normalized.includes("5-pro")) {
295
+ return "gpt-5-pro";
240
296
  }
241
- if (normalized.includes('gpt-5') &&
242
- normalized.includes('pro') &&
243
- !normalized.includes('5.1') &&
244
- !normalized.includes('5.2')) {
245
- return 'gpt-5-pro';
297
+ if (normalized.includes("gpt-5") &&
298
+ normalized.includes("pro") &&
299
+ !normalized.includes("5.1") &&
300
+ !normalized.includes("5.2") &&
301
+ !normalized.includes("5.5") &&
302
+ !normalized.includes("5.4")) {
303
+ return "gpt-5-pro";
246
304
  }
247
- if ((normalized.includes('5.1') || normalized.includes('5_1')) && normalized.includes('pro')) {
248
- return 'gpt-5.1-pro';
305
+ if ((normalized.includes("5.1") || normalized.includes("5_1")) && normalized.includes("pro")) {
306
+ return "gpt-5.1-pro";
249
307
  }
250
- if (normalized.includes('pro')) {
251
- return 'gpt-5.2-pro';
308
+ if (normalized.includes("pro")) {
309
+ return DEFAULT_MODEL;
252
310
  }
253
- if (normalized.includes('5.1') || normalized.includes('5_1')) {
254
- return 'gpt-5.1';
311
+ if (normalized.includes("5.1") || normalized.includes("5_1")) {
312
+ return "gpt-5.1";
255
313
  }
256
- if (normalized.includes('thinking')) {
257
- return 'gpt-5.2-thinking';
314
+ if (normalized.includes("thinking")) {
315
+ return "gpt-5.2-thinking";
258
316
  }
259
- if (normalized.includes('instant') || normalized.includes('fast')) {
260
- return 'gpt-5.2-instant';
317
+ if (normalized.includes("instant") || normalized.includes("fast")) {
318
+ return "gpt-5.2-instant";
261
319
  }
262
- return 'gpt-5.2';
320
+ return "gpt-5.2";
263
321
  }
@@ -1,2 +1,2 @@
1
1
  // Utilities for handling OSC progress codes embedded in stored logs.
2
- export { sanitizeOscProgress } from 'osc-progress';
2
+ export { sanitizeOscProgress } from "osc-progress";
@@ -10,8 +10,8 @@ export function shouldRequirePrompt(rawArgs, options) {
10
10
  options.execSession ||
11
11
  options.status ||
12
12
  options.debugHelp ||
13
- firstArg === 'status' ||
14
- firstArg === 'session');
13
+ firstArg === "status" ||
14
+ firstArg === "session");
15
15
  const requiresPrompt = options.renderMarkdown || Boolean(options.preview) || Boolean(options.dryRun) || !bypassPrompt;
16
16
  return requiresPrompt && !options.prompt;
17
17
  }
@@ -1,4 +1,4 @@
1
- import { ensureShikiReady, renderMarkdownAnsi } from './markdownRenderer.js';
1
+ import { ensureShikiReady, renderMarkdownAnsi } from "./markdownRenderer.js";
2
2
  export function shouldRenderRich(options = {}) {
3
3
  return options.richTty ?? Boolean(process.stdout.isTTY);
4
4
  }
@@ -1,4 +1,4 @@
1
- import { attachSession, showStatus } from './sessionDisplay.js';
1
+ import { attachSession, showStatus } from "./sessionDisplay.js";
2
2
  const defaultDeps = {
3
3
  attachSession,
4
4
  showStatus,
@@ -1,44 +1,55 @@
1
- import { DEFAULT_MODEL, MODEL_CONFIGS } from '../oracle.js';
2
- import { resolveEngine } from './engine.js';
3
- import { normalizeModelOption, inferModelFromLabel, resolveApiModel, normalizeBaseUrl } from './options.js';
4
- import { resolveGeminiModelId } from '../oracle/gemini.js';
5
- import { PromptValidationError } from '../oracle/errors.js';
6
- import { normalizeChatGptModelForBrowser } from './browserConfig.js';
1
+ import { DEFAULT_MODEL, MODEL_CONFIGS } from "../oracle.js";
2
+ import { resolveEngine } from "./engine.js";
3
+ import { normalizeModelOption, inferModelFromLabel, resolveApiModel, normalizeBaseUrl, } from "./options.js";
4
+ import { resolveGeminiModelId } from "../oracle/gemini.js";
5
+ import { PromptValidationError } from "../oracle/errors.js";
6
+ import { normalizeChatGptModelForBrowser } from "./browserConfig.js";
7
+ import { resolveConfiguredMaxFileSizeBytes } from "./fileSize.js";
7
8
  export function resolveRunOptionsFromConfig({ prompt, files = [], model, models, engine, userConfig, env = process.env, }) {
8
9
  const resolvedEngine = resolveEngineWithConfig({ engine, configEngine: userConfig?.engine, env });
9
- const browserRequested = engine === 'browser';
10
- const browserConfigured = userConfig?.engine === 'browser';
10
+ const browserRequested = engine === "browser";
11
+ const browserConfigured = userConfig?.engine === "browser";
11
12
  const requestedModelList = Array.isArray(models) ? models : [];
12
- const normalizedRequestedModels = requestedModelList.map((entry) => normalizeModelOption(entry)).filter(Boolean);
13
+ const normalizedRequestedModels = requestedModelList
14
+ .map((entry) => normalizeModelOption(entry))
15
+ .filter(Boolean);
13
16
  const cliModelArg = normalizeModelOption(model ?? userConfig?.model) || DEFAULT_MODEL;
14
- const inferredModel = resolvedEngine === 'browser' && normalizedRequestedModels.length === 0
17
+ const inferredModel = resolvedEngine === "browser" && normalizedRequestedModels.length === 0
15
18
  ? inferModelFromLabel(cliModelArg)
16
19
  : resolveApiModel(cliModelArg);
17
- // Browser engine maps Pro/legacy aliases to the latest ChatGPT picker targets (GPT-5.2 / GPT-5.2 Pro).
18
- const resolvedModel = resolvedEngine === 'browser' ? normalizeChatGptModelForBrowser(inferredModel) : inferredModel;
19
- const isCodex = resolvedModel.startsWith('gpt-5.1-codex');
20
- const isClaude = resolvedModel.startsWith('claude');
21
- const isGrok = resolvedModel.startsWith('grok');
22
- const engineWasBrowser = resolvedEngine === 'browser';
20
+ // Browser engine maps Pro/legacy aliases to the latest ChatGPT picker targets.
21
+ const resolvedModel = resolvedEngine === "browser" ? normalizeChatGptModelForBrowser(inferredModel) : inferredModel;
22
+ const isCodex = resolvedModel.startsWith("gpt-5.1-codex");
23
+ const isClaude = resolvedModel.startsWith("claude");
24
+ const isGrok = resolvedModel.startsWith("grok");
25
+ const isGeminiApiOnly = resolvedModel === "gemini-3.1-pro";
26
+ const engineWasBrowser = resolvedEngine === "browser";
23
27
  const allModels = normalizedRequestedModels.length > 0
24
28
  ? Array.from(new Set(normalizedRequestedModels.map((entry) => resolveApiModel(entry))))
25
29
  : [resolvedModel];
26
- const isBrowserCompatible = (m) => m.startsWith('gpt-') || m.startsWith('gemini');
30
+ const includesGeminiApiOnly = allModels.some((m) => m === "gemini-3.1-pro");
31
+ if ((browserRequested || browserConfigured) && includesGeminiApiOnly) {
32
+ throw new PromptValidationError("gemini-3.1-pro is API-only today. Use --engine api or switch to gemini-3-pro for Gemini web.", { engine: "browser", models: allModels });
33
+ }
34
+ const isBrowserCompatible = (m) => m.startsWith("gpt-") || m.startsWith("gemini");
27
35
  const hasNonBrowserCompatibleTarget = (browserRequested || browserConfigured) && allModels.some((m) => !isBrowserCompatible(m));
28
36
  if (hasNonBrowserCompatibleTarget) {
29
- throw new PromptValidationError('Browser engine only supports GPT and Gemini models. Re-run with --engine api for Grok, Claude, or other models.', { engine: 'browser', models: allModels });
37
+ throw new PromptValidationError("Browser engine only supports GPT and Gemini models. Re-run with --engine api for Grok, Claude, or other models.", { engine: "browser", models: allModels });
30
38
  }
31
- const engineCoercedToApi = engineWasBrowser && (isCodex || isClaude || isGrok);
32
- const fixedEngine = isCodex || isClaude || isGrok || normalizedRequestedModels.length > 0 ? 'api' : resolvedEngine;
39
+ const engineCoercedToApi = engineWasBrowser && (isCodex || isClaude || isGrok || isGeminiApiOnly);
40
+ const fixedEngine = isCodex || isClaude || isGrok || isGeminiApiOnly || normalizedRequestedModels.length > 0
41
+ ? "api"
42
+ : resolvedEngine;
33
43
  const promptWithSuffix = userConfig?.promptSuffix && userConfig.promptSuffix.trim().length > 0
34
44
  ? `${prompt.trim()}\n${userConfig.promptSuffix}`
35
45
  : prompt;
36
- const search = userConfig?.search !== 'off';
46
+ const search = userConfig?.search !== "off";
37
47
  const heartbeatIntervalMs = userConfig?.heartbeatSeconds !== undefined ? userConfig.heartbeatSeconds * 1000 : 30_000;
48
+ const maxFileSizeBytes = resolveConfiguredMaxFileSizeBytes(userConfig, env);
38
49
  const baseUrl = normalizeBaseUrl(userConfig?.apiBaseUrl ??
39
50
  (isClaude ? env.ANTHROPIC_BASE_URL : isGrok ? env.XAI_BASE_URL : env.OPENAI_BASE_URL));
40
51
  const uniqueMultiModels = normalizedRequestedModels.length > 0 ? allModels : [];
41
- const includesCodexMultiModel = uniqueMultiModels.some((entry) => entry.startsWith('gpt-5.1-codex'));
52
+ const includesCodexMultiModel = uniqueMultiModels.some((entry) => entry.startsWith("gpt-5.1-codex"));
42
53
  if (includesCodexMultiModel && browserRequested) {
43
54
  // Silent coerce; multi-model still forces API.
44
55
  }
@@ -49,6 +60,7 @@ export function resolveRunOptionsFromConfig({ prompt, files = [], model, models,
49
60
  model: chosenModel,
50
61
  models: uniqueMultiModels.length > 0 ? uniqueMultiModels : undefined,
51
62
  file: files ?? [],
63
+ maxFileSizeBytes,
52
64
  search,
53
65
  heartbeatIntervalMs,
54
66
  filesReport: userConfig?.filesReport,
@@ -61,8 +73,8 @@ export function resolveRunOptionsFromConfig({ prompt, files = [], model, models,
61
73
  function resolveEngineWithConfig({ engine, configEngine, env, }) {
62
74
  if (engine)
63
75
  return engine;
64
- const envOverride = (env.ORACLE_ENGINE ?? '').trim().toLowerCase();
65
- if (envOverride === 'api' || envOverride === 'browser') {
76
+ const envOverride = (env.ORACLE_ENGINE ?? "").trim().toLowerCase();
77
+ if (envOverride === "api" || envOverride === "browser") {
66
78
  return envOverride;
67
79
  }
68
80
  if (configEngine)
@@ -70,7 +82,7 @@ function resolveEngineWithConfig({ engine, configEngine, env, }) {
70
82
  return resolveEngine({ engine: undefined, env });
71
83
  }
72
84
  function resolveEffectiveModelId(model) {
73
- if (typeof model === 'string' && model.startsWith('gemini')) {
85
+ if (typeof model === "string" && model.startsWith("gemini")) {
74
86
  return resolveGeminiModelId(model);
75
87
  }
76
88
  const config = MODEL_CONFIGS[model];
@@ -1,7 +1,7 @@
1
- import chalk from 'chalk';
2
- import { usesDefaultStatusFilters } from './options.js';
3
- import { attachSession, showStatus } from './sessionDisplay.js';
4
- import { sessionStore } from '../sessionStore.js';
1
+ import chalk from "chalk";
2
+ import { usesDefaultStatusFilters } from "./options.js";
3
+ import { attachSession, showStatus, } from "./sessionDisplay.js";
4
+ import { sessionStore } from "../sessionStore.js";
5
5
  const defaultDependencies = {
6
6
  showStatus,
7
7
  attachSession,
@@ -9,39 +9,49 @@ const defaultDependencies = {
9
9
  deleteSessionsOlderThan: (options) => sessionStore.deleteOlderThan(options),
10
10
  getSessionPaths: (sessionId) => sessionStore.getPaths(sessionId),
11
11
  };
12
- const SESSION_OPTION_KEYS = new Set(['hours', 'limit', 'all', 'clear', 'clean', 'render', 'renderMarkdown', 'path', 'model']);
12
+ const SESSION_OPTION_KEYS = new Set([
13
+ "hours",
14
+ "limit",
15
+ "all",
16
+ "clear",
17
+ "clean",
18
+ "render",
19
+ "renderMarkdown",
20
+ "path",
21
+ "model",
22
+ ]);
13
23
  export async function handleSessionCommand(sessionId, command, deps = defaultDependencies) {
14
24
  const sessionOptions = command.opts();
15
25
  if (sessionOptions.verboseRender) {
16
- process.env.ORACLE_VERBOSE_RENDER = '1';
26
+ process.env.ORACLE_VERBOSE_RENDER = "1";
17
27
  }
18
- const renderSource = command.getOptionValueSource?.('render');
19
- const renderMarkdownSource = command.getOptionValueSource?.('renderMarkdown');
20
- const renderExplicit = renderSource === 'cli' || renderMarkdownSource === 'cli';
28
+ const renderSource = command.getOptionValueSource?.("render");
29
+ const renderMarkdownSource = command.getOptionValueSource?.("renderMarkdown");
30
+ const renderExplicit = renderSource === "cli" || renderMarkdownSource === "cli";
21
31
  const autoRender = !renderExplicit && process.stdout.isTTY;
22
32
  const pathRequested = Boolean(sessionOptions.path);
23
33
  const clearRequested = Boolean(sessionOptions.clear || sessionOptions.clean);
24
34
  if (clearRequested) {
25
35
  if (sessionId) {
26
- console.error('Cannot combine a session ID with --clear. Remove the ID to delete cached sessions.');
36
+ console.error("Cannot combine a session ID with --clear. Remove the ID to delete cached sessions.");
27
37
  process.exitCode = 1;
28
38
  return;
29
39
  }
30
40
  const hours = sessionOptions.hours;
31
41
  const includeAll = sessionOptions.all;
32
42
  const result = await deps.deleteSessionsOlderThan({ hours, includeAll });
33
- const scope = includeAll ? 'all stored sessions' : `sessions older than ${hours}h`;
43
+ const scope = includeAll ? "all stored sessions" : `sessions older than ${hours}h`;
34
44
  console.log(formatSessionCleanupMessage(result, scope));
35
45
  return;
36
46
  }
37
- if (sessionId === 'clear' || sessionId === 'clean') {
47
+ if (sessionId === "clear" || sessionId === "clean") {
38
48
  console.error('Session cleanup now uses --clear. Run "oracle session --clear --hours <n>" instead.');
39
49
  process.exitCode = 1;
40
50
  return;
41
51
  }
42
52
  if (pathRequested) {
43
53
  if (!sessionId) {
44
- console.error('The --path flag requires a session ID.');
54
+ console.error("The --path flag requires a session ID.");
45
55
  process.exitCode = 1;
46
56
  return;
47
57
  }
@@ -50,10 +60,10 @@ export async function handleSessionCommand(sessionId, command, deps = defaultDep
50
60
  const richTty = Boolean(process.stdout.isTTY && chalk.level > 0);
51
61
  const label = (text) => (richTty ? chalk.cyan(text) : text);
52
62
  const value = (text) => (richTty ? chalk.dim(text) : text);
53
- console.log(`${label('Session dir:')} ${value(paths.dir)}`);
54
- console.log(`${label('Metadata:')} ${value(paths.metadata)}`);
55
- console.log(`${label('Request:')} ${value(paths.request)}`);
56
- console.log(`${label('Log:')} ${value(paths.log)}`);
63
+ console.log(`${label("Session dir:")} ${value(paths.dir)}`);
64
+ console.log(`${label("Metadata:")} ${value(paths.metadata)}`);
65
+ console.log(`${label("Request:")} ${value(paths.request)}`);
66
+ console.log(`${label("Log:")} ${value(paths.log)}`);
57
67
  }
58
68
  catch (error) {
59
69
  console.error(error instanceof Error ? error.message : String(error));
@@ -75,7 +85,7 @@ export async function handleSessionCommand(sessionId, command, deps = defaultDep
75
85
  // Surface any root-level flags that were provided but are ignored when attaching to a session.
76
86
  const ignoredFlags = listIgnoredFlags(command);
77
87
  if (ignoredFlags.length > 0) {
78
- console.log(`Ignoring flags on session attach: ${ignoredFlags.join(', ')}`);
88
+ console.log(`Ignoring flags on session attach: ${ignoredFlags.join(", ")}`);
79
89
  }
80
90
  const renderMarkdown = Boolean(sessionOptions.render || sessionOptions.renderMarkdown || autoRender);
81
91
  await deps.attachSession(sessionId, {
@@ -85,8 +95,8 @@ export async function handleSessionCommand(sessionId, command, deps = defaultDep
85
95
  });
86
96
  }
87
97
  export function formatSessionCleanupMessage(result, scope) {
88
- const deletedLabel = `${result.deleted} ${result.deleted === 1 ? 'session' : 'sessions'}`;
89
- const remainingLabel = `${result.remaining} ${result.remaining === 1 ? 'session' : 'sessions'} remain`;
98
+ const deletedLabel = `${result.deleted} ${result.deleted === 1 ? "session" : "sessions"}`;
99
+ const remainingLabel = `${result.remaining} ${result.remaining === 1 ? "session" : "sessions"} remain`;
90
100
  const hint = 'Run "oracle session --clear --all" to delete everything.';
91
101
  return `Deleted ${deletedLabel} (${scope}). ${remainingLabel}.\n${hint}`;
92
102
  }
@@ -98,7 +108,7 @@ function listIgnoredFlags(command) {
98
108
  continue;
99
109
  }
100
110
  const source = command.getOptionValueSource?.(key);
101
- if (source !== 'cli' && source !== 'env') {
111
+ if (source !== "cli" && source !== "env") {
102
112
  continue;
103
113
  }
104
114
  const value = opts[key];