@steipete/oracle 0.13.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +22 -11
  2. package/dist/bin/oracle-cli.js +71 -71
  3. package/dist/src/browser/actions/assistantResponse.js +93 -10
  4. package/dist/src/browser/actions/deepResearch.js +180 -52
  5. package/dist/src/browser/actions/modelSelection.js +357 -40
  6. package/dist/src/browser/actions/navigation.js +294 -65
  7. package/dist/src/browser/actions/promptComposer.js +50 -4
  8. package/dist/src/browser/actions/thinkingTime.js +517 -105
  9. package/dist/src/browser/artifacts.js +19 -0
  10. package/dist/src/browser/chatgptFiles.js +797 -0
  11. package/dist/src/browser/chatgptImages.js +256 -27
  12. package/dist/src/browser/chromeLifecycle.js +4 -0
  13. package/dist/src/browser/config.js +2 -0
  14. package/dist/src/browser/index.js +404 -41
  15. package/dist/src/browser/pageActions.js +1 -1
  16. package/dist/src/browser/policies.js +2 -6
  17. package/dist/src/browser/projectSourcesRunner.js +16 -3
  18. package/dist/src/browser/prompt.js +161 -36
  19. package/dist/src/browser/reattach.js +6 -2
  20. package/dist/src/browser/reattachability.js +22 -10
  21. package/dist/src/browser/recoverConversation.js +73 -0
  22. package/dist/src/browser/sessionRunner.js +4 -1
  23. package/dist/src/cli/browserConfig.js +6 -2
  24. package/dist/src/cli/browserDefaults.js +2 -1
  25. package/dist/src/cli/browserTabs.js +129 -54
  26. package/dist/src/cli/followup.js +72 -0
  27. package/dist/src/cli/help.js +1 -1
  28. package/dist/src/cli/options.js +24 -0
  29. package/dist/src/cli/reattachGuidance.js +8 -0
  30. package/dist/src/cli/runOptions.js +2 -12
  31. package/dist/src/cli/sessionCommand.js +4 -0
  32. package/dist/src/cli/sessionDisplay.js +17 -3
  33. package/dist/src/cli/sessionLineage.js +7 -4
  34. package/dist/src/cli/sessionRunner.js +16 -1
  35. package/dist/src/gemini-web/client.js +5 -10
  36. package/dist/src/gemini-web/executor.js +1 -24
  37. package/dist/src/gemini-web/models.js +83 -0
  38. package/dist/src/mcp/server.js +2 -0
  39. package/dist/src/mcp/tools/chatgptImage.js +132 -0
  40. package/dist/src/mcp/tools/consult.js +270 -175
  41. package/dist/src/mcp/types.js +13 -2
  42. package/dist/src/mcp/utils.js +87 -1
  43. package/dist/src/oracle/config.js +27 -1
  44. package/dist/src/oracle/files.js +4 -6
  45. package/dist/src/oracle/geminiModels.js +2 -0
  46. package/dist/src/oracle/markdown.js +36 -3
  47. package/dist/src/oracle/modelResolver.js +21 -13
  48. package/dist/src/oracle/promptAssembly.js +2 -4
  49. package/dist/src/oracle/request.js +3 -5
  50. package/dist/src/oracle/thinkingTime.js +40 -0
  51. package/dist/src/oracle/tokenStats.js +5 -1
  52. package/dist/src/oracle.js +1 -1
  53. package/dist/src/sessionManager.js +1 -0
  54. package/dist/vendor/oracle-notifier/build-notifier.sh +0 -0
  55. package/package.json +47 -58
  56. package/vendor/oracle-notifier/build-notifier.sh +0 -0
  57. package/dist/bin/oracle.js +0 -569
  58. package/dist/src/browser/chromeCookies.js +0 -312
  59. package/dist/src/browser/keytarShim.js +0 -56
  60. package/dist/src/browser/windowsCookies.js +0 -219
  61. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  62. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/Info.plist +0 -20
  63. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  64. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
  65. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +0 -128
  66. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  67. package/vendor/oracle-notifier/OracleNotifier.app/Contents/Info.plist +0 -20
  68. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  69. package/vendor/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
  70. package/vendor/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +0 -128
  71. package/vendor/oracle-notifier/README.md +0 -26
@@ -1,6 +1,80 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
1
3
  import { resolveRunOptionsFromConfig } from "../cli/runOptions.js";
4
+ import { getOracleHomeDir } from "../oracleHome.js";
2
5
  import { Launcher } from "chrome-launcher";
3
- export function mapConsultToRunOptions({ prompt, files, model, models, engine, search, browserAttachments, browserBundleFiles, browserBundleFormat, browserFollowUps, userConfig, env = process.env, }) {
6
+ const ALLOW_EXTERNAL_OUTPUT_ENV = "ORACLE_MCP_ALLOW_EXTERNAL_OUTPUT";
7
+ const MCP_OUTPUT_DIRNAME = "generated";
8
+ /**
9
+ * Whether MCP callers may write generated images outside the dedicated Oracle
10
+ * output directory. Off by default: MCP clients are less trusted than CLI users.
11
+ */
12
+ export function isExternalMcpOutputAllowed(env = process.env) {
13
+ const raw = env[ALLOW_EXTERNAL_OUTPUT_ENV]?.trim().toLowerCase();
14
+ return raw === "1" || raw === "true" || raw === "yes" || raw === "on";
15
+ }
16
+ /**
17
+ * Resolve `target` through symlinks for the portion that exists on disk, then
18
+ * re-append the not-yet-created remainder. A lexical `path.resolve` is not
19
+ * enough: a symlinked parent under the Oracle home (e.g. `~/.oracle/generated`
20
+ * -> `/tmp/evil`) would pass a string-prefix check while the actual write lands
21
+ * outside the boundary. realpath-ing the deepest existing ancestor closes that.
22
+ */
23
+ function resolveThroughSymlinks(target) {
24
+ const resolved = path.resolve(target);
25
+ let current = resolved;
26
+ const tail = [];
27
+ // Bounded by the number of path segments; dirname() converges to the root.
28
+ for (;;) {
29
+ try {
30
+ const real = fs.realpathSync(current);
31
+ return tail.length ? path.join(real, ...tail) : real;
32
+ }
33
+ catch (error) {
34
+ if (error.code !== "ENOENT") {
35
+ throw error;
36
+ }
37
+ try {
38
+ if (fs.lstatSync(current).isSymbolicLink()) {
39
+ throw new Error(`MCP output path contains an unresolved symlink: ${current}`);
40
+ }
41
+ }
42
+ catch (lstatError) {
43
+ if (lstatError.code !== "ENOENT") {
44
+ throw lstatError;
45
+ }
46
+ }
47
+ const parent = path.dirname(current);
48
+ if (parent === current) {
49
+ return resolved;
50
+ }
51
+ tail.unshift(path.basename(current));
52
+ current = parent;
53
+ }
54
+ }
55
+ }
56
+ /**
57
+ * Constrain an MCP-supplied output path to ORACLE_HOME_DIR/generated and return
58
+ * its canonical absolute form. This keeps agent writes away from Oracle config,
59
+ * session metadata, and browser profile state. Existing symlinks are resolved
60
+ * before the boundary check. Set ORACLE_MCP_ALLOW_EXTERNAL_OUTPUT to opt into
61
+ * arbitrary output paths as an explicit operator decision.
62
+ */
63
+ export function resolveMcpOutputPath(requestedPath, field, env = process.env) {
64
+ const resolved = path.resolve(requestedPath);
65
+ if (isExternalMcpOutputAllowed(env)) {
66
+ return resolved;
67
+ }
68
+ const oracleHome = resolveThroughSymlinks(getOracleHomeDir());
69
+ const root = path.join(oracleHome, MCP_OUTPUT_DIRNAME);
70
+ const realTarget = resolveThroughSymlinks(resolved);
71
+ if (realTarget.startsWith(`${root}${path.sep}`)) {
72
+ return realTarget;
73
+ }
74
+ throw new Error(`MCP "${field}" must resolve under the generated output directory (${root}); got "${realTarget}". ` +
75
+ `Use a path under that directory, or set ${ALLOW_EXTERNAL_OUTPUT_ENV}=1 to allow external output paths.`);
76
+ }
77
+ export function mapConsultToRunOptions({ prompt, files, model, models, engine, search, browserAttachments, browserBundleFiles, browserBundleFormat, browserFollowUps, generateImage, outputPath, userConfig, env = process.env, }) {
4
78
  // Normalize CLI-style inputs through the shared resolver so config/env defaults apply,
5
79
  // then overlay MCP-only overrides such as explicit search toggles.
6
80
  const mergedModels = Array.isArray(models) && models.length > 0
@@ -32,6 +106,18 @@ export function mapConsultToRunOptions({ prompt, files, model, models, engine, s
32
106
  .map((entry) => entry.trim())
33
107
  .filter(Boolean);
34
108
  }
109
+ const imageOutputPath = generateImage?.trim();
110
+ if (imageOutputPath) {
111
+ result.runOptions.generateImage = resolveMcpOutputPath(imageOutputPath, "generateImage", env);
112
+ }
113
+ const secondaryOutputPath = outputPath?.trim();
114
+ if (secondaryOutputPath) {
115
+ result.runOptions.outputPath = resolveMcpOutputPath(secondaryOutputPath, "outputPath", env);
116
+ }
117
+ if (result.resolvedEngine !== "browser" &&
118
+ (result.runOptions.generateImage || result.runOptions.outputPath)) {
119
+ throw new Error('MCP image output requires engine:"browser".');
120
+ }
35
121
  return result;
36
122
  }
37
123
  export function ensureBrowserAvailable(engine, options) {
@@ -164,6 +164,32 @@ export const MODEL_CONFIGS = {
164
164
  supportsBackground: false,
165
165
  supportsSearch: true,
166
166
  },
167
+ "gemini-3.5-flash": {
168
+ model: "gemini-3.5-flash",
169
+ provider: "google",
170
+ tokenizer: countTokensGpt5Pro,
171
+ inputLimit: 1_048_576,
172
+ pricing: {
173
+ inputPerToken: 1.5 / 1_000_000,
174
+ outputPerToken: 9 / 1_000_000,
175
+ },
176
+ reasoning: null,
177
+ supportsBackground: false,
178
+ supportsSearch: true,
179
+ },
180
+ "gemini-3.1-flash-lite": {
181
+ model: "gemini-3.1-flash-lite",
182
+ provider: "google",
183
+ tokenizer: countTokensGpt5Pro,
184
+ inputLimit: 1_048_576,
185
+ pricing: {
186
+ inputPerToken: 0.25 / 1_000_000,
187
+ outputPerToken: 1.5 / 1_000_000,
188
+ },
189
+ reasoning: null,
190
+ supportsBackground: false,
191
+ supportsSearch: true,
192
+ },
167
193
  "gemini-3-pro": {
168
194
  model: "gemini-3-pro",
169
195
  provider: "google",
@@ -223,6 +249,6 @@ export const MODEL_CONFIGS = {
223
249
  };
224
250
  export const DEFAULT_SYSTEM_PROMPT = [
225
251
  "You are Oracle, a focused one-shot problem solver.",
226
- "Emphasize direct answers and cite any files referenced.",
252
+ "Emphasize direct answers and cite referenced files as path:line or path:line-line when line numbers are available.",
227
253
  ].join(" ");
228
254
  export const TOKENIZER_OPTIONS = { allowedSpecial: "all" };
@@ -382,15 +382,13 @@ function relativePath(targetPath, cwd) {
382
382
  const relative = path.relative(cwd, targetPath);
383
383
  return relative || targetPath;
384
384
  }
385
+ function formatLegacyFileSection(index, displayPath, content) {
386
+ return [`### File ${index}: ${displayPath}`, "```", content.trimEnd(), "```"].join("\n");
387
+ }
385
388
  export function createFileSections(files, cwd = process.cwd()) {
386
389
  return files.map((file, index) => {
387
390
  const relative = toPosix(path.relative(cwd, file.path) || file.path);
388
- const sectionText = [
389
- `### File ${index + 1}: ${relative}`,
390
- "```",
391
- file.content.trimEnd(),
392
- "```",
393
- ].join("\n");
391
+ const sectionText = formatLegacyFileSection(index + 1, relative, file.content);
394
392
  return {
395
393
  index: index + 1,
396
394
  absolutePath: file.path,
@@ -1,5 +1,7 @@
1
1
  const MODEL_ID_MAP = {
2
+ "gemini-3.1-flash-lite": "gemini-3.1-flash-lite",
2
3
  "gemini-3.1-pro": "gemini-3.1-pro-preview",
4
+ "gemini-3.5-flash": "gemini-3.5-flash",
3
5
  "gemini-3-pro": "gemini-3-pro-preview",
4
6
  "gpt-5.5": "gpt-5.5",
5
7
  "gpt-5.5-pro": "gpt-5.5-pro",
@@ -36,11 +36,44 @@ function pickFence(content) {
36
36
  const fenceLength = Math.max(3, maxTicks + 1);
37
37
  return "`".repeat(fenceLength);
38
38
  }
39
- export function formatFileSection(displayPath, content) {
39
+ function formatLineRange(lineCount) {
40
+ return lineCount === 0 ? "Lines: 0" : `Lines: 1-${lineCount}`;
41
+ }
42
+ function addLineNumbers(content) {
43
+ if (content.length === 0) {
44
+ return { text: "", lineCount: 0 };
45
+ }
46
+ const lines = content.split("\n");
47
+ const width = String(lines.length).length;
48
+ const numbered = lines
49
+ .map((line, index) => `${String(index + 1).padStart(width, " ")} | ${line}`)
50
+ .join("\n");
51
+ return { text: numbered, lineCount: lines.length };
52
+ }
53
+ function renderFileSection(displayPath, content, options) {
40
54
  const fence = pickFence(content);
41
55
  const lang = detectFenceLanguage(displayPath);
42
56
  const normalized = content.replace(/\s+$/u, "");
43
- const header = `### File: ${displayPath}`;
57
+ const header = options.index == null
58
+ ? `### File: ${displayPath}`
59
+ : `### File ${options.index}: ${displayPath}`;
44
60
  const fenceOpen = lang ? `${fence}${lang}` : fence;
45
- return [header, fenceOpen, normalized, fence, ""].join("\n");
61
+ if (options.lineNumbers !== true) {
62
+ return [header, fenceOpen, normalized, fence, ""].join("\n");
63
+ }
64
+ const numbered = addLineNumbers(normalized);
65
+ return [header, formatLineRange(numbered.lineCount), fenceOpen, numbered.text, fence, ""].join("\n");
66
+ }
67
+ export function formatFileSection(displayPath, content, options = {}) {
68
+ return renderFileSection(displayPath, content, options);
69
+ }
70
+ export function formatFileSections(sections, options = {}) {
71
+ const lineNumbers = options.lineNumbers ?? true;
72
+ const rendered = sections
73
+ .map((section) => renderFileSection(section.displayPath, section.content, {
74
+ lineNumbers,
75
+ index: options.includeFileIndex ? section.index : undefined,
76
+ }).trimEnd())
77
+ .join("\n\n");
78
+ return options.trailingNewline && rendered ? `${rendered}\n` : rendered;
46
79
  }
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { MODEL_CONFIGS, PRO_MODELS } from "./config.js";
3
- import { pricingFromUsdPerMillion } from "tokentally";
3
+ import { pricingFromUsdPerToken } from "tokentally";
4
4
  const OPENROUTER_DEFAULT_BASE = "https://openrouter.ai/api/v1";
5
5
  const OPENROUTER_MODELS_ENDPOINT = "https://openrouter.ai/api/v1/models";
6
6
  const require = createRequire(import.meta.url);
@@ -42,6 +42,25 @@ export function normalizeOpenRouterBaseUrl(baseUrl) {
42
42
  export function safeModelSlug(model) {
43
43
  return model.replace(/[/\\]/g, "__").replace(/[:*?"<>|]/g, "_");
44
44
  }
45
+ function openRouterPricing(pricing) {
46
+ const parsePrice = (value) => {
47
+ const parsed = typeof value === "number"
48
+ ? value
49
+ : typeof value === "string" && value.trim() !== ""
50
+ ? Number(value)
51
+ : NaN;
52
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
53
+ };
54
+ const inputUsdPerToken = parsePrice(pricing?.prompt);
55
+ const outputUsdPerToken = parsePrice(pricing?.completion);
56
+ if (inputUsdPerToken === null || outputUsdPerToken === null)
57
+ return null;
58
+ const normalized = pricingFromUsdPerToken({ inputUsdPerToken, outputUsdPerToken });
59
+ return {
60
+ inputPerToken: normalized.inputUsdPerToken,
61
+ outputPerToken: normalized.outputUsdPerToken,
62
+ };
63
+ }
45
64
  const catalogCache = new Map();
46
65
  const CACHE_TTL_MS = 5 * 60 * 1000;
47
66
  const MAX_CACHE_ENTRIES = 20;
@@ -125,18 +144,7 @@ export async function resolveModelConfig(model, options = {}) {
125
144
  openRouterId: targetId,
126
145
  provider: known?.provider ?? "other",
127
146
  inputLimit: info.context_length ?? known?.inputLimit ?? 200_000,
128
- pricing: info.pricing && info.pricing.prompt != null && info.pricing.completion != null
129
- ? (() => {
130
- const pricing = pricingFromUsdPerMillion({
131
- inputUsdPerMillion: info.pricing.prompt,
132
- outputUsdPerMillion: info.pricing.completion,
133
- });
134
- return {
135
- inputPerToken: pricing.inputUsdPerToken,
136
- outputPerToken: pricing.outputUsdPerToken,
137
- };
138
- })()
139
- : (known?.pricing ?? null),
147
+ pricing: openRouterPricing(info.pricing) ?? known?.pricing ?? null,
140
148
  supportsBackground: known?.supportsBackground ?? true,
141
149
  supportsSearch: known?.supportsSearch ?? true,
142
150
  };
@@ -1,4 +1,4 @@
1
- import { formatFileSection } from "./markdown.js";
1
+ import { formatFileSections } from "./markdown.js";
2
2
  /**
3
3
  * Build the shared markdown structure for system/user/file sections.
4
4
  * Collapses excessive blank lines and trims trailing whitespace to keep
@@ -6,9 +6,7 @@ import { formatFileSection } from "./markdown.js";
6
6
  */
7
7
  export function buildPromptMarkdown(systemPrompt, userPrompt, sections) {
8
8
  const lines = ["[SYSTEM]", systemPrompt, "", "[USER]", userPrompt, ""];
9
- sections.forEach((section) => {
10
- lines.push(formatFileSection(section.displayPath, section.content));
11
- });
9
+ lines.push(formatFileSections(sections, { trailingNewline: true }));
12
10
  return lines
13
11
  .join("\n")
14
12
  .replace(/\n{3,}/g, "\n\n")
@@ -1,14 +1,14 @@
1
1
  import fs from "node:fs/promises";
2
2
  import { DEFAULT_SYSTEM_PROMPT } from "./config.js";
3
3
  import { createFileSections, readFiles } from "./files.js";
4
- import { formatFileSection } from "./markdown.js";
4
+ import { formatFileSections } from "./markdown.js";
5
5
  import { createFsAdapter } from "./fsAdapter.js";
6
6
  export function buildPrompt(basePrompt, files, cwd = process.cwd()) {
7
7
  if (!files.length) {
8
8
  return basePrompt;
9
9
  }
10
10
  const sections = createFileSections(files, cwd);
11
- const sectionText = sections.map((section) => section.sectionText).join("\n\n");
11
+ const sectionText = formatFileSections(sections, { includeFileIndex: true });
12
12
  return `${basePrompt.trim()}\n\n${sectionText}`;
13
13
  }
14
14
  export function buildRequestBody({ modelConfig, systemPrompt, userPrompt, searchEnabled, maxOutputTokens, background, storeResponse, previousResponseId, }) {
@@ -48,9 +48,7 @@ export async function renderPromptMarkdown(options, deps = {}) {
48
48
  const userPrompt = (options.prompt ?? "").trim();
49
49
  const lines = ["[SYSTEM]", systemPrompt, ""];
50
50
  lines.push("[USER]", userPrompt, "");
51
- sections.forEach((section) => {
52
- lines.push(formatFileSection(section.displayPath, section.content));
53
- });
51
+ lines.push(formatFileSections(sections));
54
52
  return lines
55
53
  .join("\n")
56
54
  .replace(/\n{3,}/g, "\n\n")
@@ -0,0 +1,40 @@
1
+ export const THINKING_TIME_LEVELS = ["light", "standard", "extended", "heavy"];
2
+ export const THINKING_TIME_ALIASES = [
3
+ "instant",
4
+ "low",
5
+ "medium",
6
+ "high",
7
+ "extra-high",
8
+ "extra high",
9
+ "extrahigh",
10
+ "xhigh",
11
+ ];
12
+ export const THINKING_TIME_INPUT_VALUES = [
13
+ ...THINKING_TIME_LEVELS,
14
+ ...THINKING_TIME_ALIASES,
15
+ ];
16
+ export function normalizeThinkingTimeLevel(value) {
17
+ const normalized = (value ?? "")
18
+ .trim()
19
+ .toLowerCase()
20
+ .replace(/[_\s]+/g, "-");
21
+ switch (normalized) {
22
+ case "light":
23
+ case "instant":
24
+ case "low":
25
+ return "light";
26
+ case "standard":
27
+ case "medium":
28
+ return "standard";
29
+ case "extended":
30
+ case "high":
31
+ return "extended";
32
+ case "heavy":
33
+ case "extra-high":
34
+ case "extrahigh":
35
+ case "xhigh":
36
+ return "heavy";
37
+ default:
38
+ return null;
39
+ }
40
+ }
@@ -1,5 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import { createFileSections } from "./files.js";
3
+ import { formatFileSections } from "./markdown.js";
3
4
  export function getFileTokenStats(files, { cwd = process.cwd(), tokenizer, tokenizerOptions, inputTokenBudget, }) {
4
5
  if (!files.length) {
5
6
  return { stats: [], totalTokens: 0 };
@@ -7,7 +8,10 @@ export function getFileTokenStats(files, { cwd = process.cwd(), tokenizer, token
7
8
  const sections = createFileSections(files, cwd);
8
9
  const stats = sections
9
10
  .map((section) => {
10
- const tokens = tokenizer(section.sectionText, tokenizerOptions);
11
+ const sectionText = formatFileSections([section], {
12
+ includeFileIndex: true,
13
+ }).trimEnd();
14
+ const tokens = tokenizer(sectionText, tokenizerOptions);
11
15
  const percent = inputTokenBudget ? (tokens / inputTokenBudget) * 100 : undefined;
12
16
  return {
13
17
  path: section.absolutePath,
@@ -4,7 +4,7 @@ export { readFiles, createFileSections } from "./oracle/files.js";
4
4
  export { buildPrompt, buildRequestBody, renderPromptMarkdown } from "./oracle/request.js";
5
5
  export { estimateRequestTokens } from "./oracle/tokenEstimate.js";
6
6
  export { formatUSD, formatNumber, formatElapsed } from "./oracle/format.js";
7
- export { formatFileSection } from "./oracle/markdown.js";
7
+ export { formatFileSection, formatFileSections } from "./oracle/markdown.js";
8
8
  export { getFileTokenStats, printFileTokenStats } from "./oracle/tokenStats.js";
9
9
  export { OracleResponseError, OracleTransportError, OracleUserError, FileValidationError, BrowserAutomationError, PromptValidationError, describeTransportError, extractResponseMetadata, asOracleUserError, toTransportError, } from "./oracle/errors.js";
10
10
  export { createDefaultClientFactory } from "./oracle/client.js";
@@ -228,6 +228,7 @@ export async function initializeSession(options, cwd, notifications, baseSlugOve
228
228
  editImage: options.editImage,
229
229
  outputPath: options.outputPath,
230
230
  browserFollowUps: options.browserFollowUps,
231
+ browserResumeConversationUrl: options.browserResumeConversationUrl,
231
232
  aspectRatio: options.aspectRatio,
232
233
  geminiShowThoughts: options.geminiShowThoughts,
233
234
  },
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steipete/oracle",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "description": "CLI wrapper around OpenAI Responses API with GPT-5.5 Pro, GPT-5.5, GPT-5.4, GPT-5.2, GPT-5.1, and GPT-5.1 Codex high reasoning modes.",
5
5
  "keywords": [],
6
6
  "homepage": "https://askoracle.sh",
@@ -28,76 +28,48 @@
28
28
  ],
29
29
  "type": "module",
30
30
  "main": "dist/bin/oracle-cli.js",
31
- "scripts": {
32
- "docs:list": "tsx scripts/docs-list.ts",
33
- "docs:check": "node --no-deprecation --import tsx bin/oracle-cli.ts docs check",
34
- "docs:site": "node scripts/build-docs-site.mjs",
35
- "build": "tsgo -p tsconfig.build.json && pnpm run build:vendor",
36
- "build:vendor": "node -e \"const fs=require('fs'); const path=require('path'); const vendorRoot=path.join('dist','vendor'); fs.rmSync(vendorRoot,{recursive:true,force:true}); const vendors=[['oracle-notifier']]; vendors.forEach(([name])=>{const src=path.join('vendor',name); const dest=path.join(vendorRoot,name); fs.mkdirSync(dest,{recursive:true}); if(fs.existsSync(src)){fs.cpSync(src,dest,{recursive:true,force:true});}});\"",
37
- "start": "pnpm run build && node ./dist/scripts/run-cli.js",
38
- "oracle": "pnpm start",
39
- "check": "pnpm run format:check && pnpm run lint",
40
- "typecheck": "tsgo --noEmit",
41
- "format": "oxfmt --write .",
42
- "format:check": "oxfmt --check .",
43
- "format:diff": "oxfmt --write . && git --no-pager diff",
44
- "lint": "pnpm run typecheck && oxlint",
45
- "lint:fix": "oxlint --fix && pnpm run format",
46
- "test": "vitest run",
47
- "test:mcp": "pnpm run build && pnpm run test:mcp:unit && pnpm run test:mcp:mcporter",
48
- "test:mcp:unit": "vitest run tests/mcp*.test.ts tests/mcp/**/*.test.ts",
49
- "test:mcp:mcporter": "CHROME_DEVTOOLS_URL=http://127.0.0.1:0 pnpm dlx mcporter list oracle-local --schema --config config/mcporter.json && CHROME_DEVTOOLS_URL=http://127.0.0.1:0 pnpm dlx mcporter call oracle-local.sessions limit:1 --config config/mcporter.json",
50
- "test:browser": "pnpm run build && tsx scripts/test-browser.ts && ./scripts/browser-smoke.sh",
51
- "test:packed-cli": "node scripts/packed-cli-smoke.mjs",
52
- "test:live": "ORACLE_LIVE_TEST=1 vitest run tests/live --exclude tests/live/openai-live.test.ts",
53
- "test:live:fast": "ORACLE_LIVE_TEST=1 ORACLE_LIVE_TEST_FAST=1 vitest run tests/live/browser-fast-live.test.ts",
54
- "test:pro": "ORACLE_LIVE_TEST=1 vitest run tests/live/openai-live.test.ts",
55
- "test:coverage": "vitest run --coverage",
56
- "prepare": "pnpm run build",
57
- "mcp": "pnpm run build && node ./dist/bin/oracle-mcp.js"
58
- },
59
31
  "dependencies": {
60
32
  "@anthropic-ai/tokenizer": "^0.0.4",
61
- "@google/genai": "^2.0.1",
33
+ "@google/genai": "^2.8.0",
62
34
  "@google/generative-ai": "^0.24.1",
63
35
  "@modelcontextprotocol/sdk": "^1.29.0",
64
- "@steipete/sweet-cookie": "^0.3.0",
36
+ "@steipete/sweet-cookie": "^0.4.0",
65
37
  "chalk": "^5.6.2",
66
38
  "chrome-launcher": "^1.2.1",
67
39
  "chrome-remote-interface": "^0.34.0",
68
40
  "clipboardy": "^5.3.1",
69
- "commander": "^14.0.3",
41
+ "commander": "^15.0.0",
70
42
  "dotenv": "^17.4.2",
71
43
  "fast-glob": "^3.3.3",
72
44
  "gpt-tokenizer": "^3.4.0",
73
- "inquirer": "13.4.3",
45
+ "inquirer": "14.0.2",
74
46
  "json5": "^2.2.3",
75
47
  "kleur": "^4.1.5",
76
- "markdansi": "0.2.1",
77
- "openai": "^6.38.0",
48
+ "markdansi": "0.3.1",
49
+ "openai": "^6.42.0",
78
50
  "osc-progress": "^0.3.0",
79
- "qs": "^6.15.1",
80
- "shiki": "^4.0.2",
51
+ "qs": "^6.15.2",
52
+ "shiki": "^4.2.0",
81
53
  "toasted-notifier": "^10.1.0",
82
54
  "tokentally": "^0.1.1",
83
- "zod": "^4.4.2"
55
+ "zod": "^4.4.3"
84
56
  },
85
57
  "devDependencies": {
86
58
  "@anthropic-ai/tokenizer": "^0.0.4",
87
59
  "@types/chrome-remote-interface": "^0.34.0",
88
60
  "@types/inquirer": "^9.0.9",
89
- "@types/node": "^25.6.0",
90
- "@typescript/native-preview": "7.0.0-dev.20260516.1",
91
- "@vitest/coverage-v8": "4.1.6",
92
- "devtools-protocol": "0.0.1629771",
93
- "es-toolkit": "^1.46.1",
61
+ "@types/node": "^25.9.2",
62
+ "@typescript/native-preview": "7.0.0-dev.20260612.1",
63
+ "@vitest/coverage-v8": "4.1.8",
64
+ "devtools-protocol": "0.0.1643702",
65
+ "es-toolkit": "^1.47.0",
94
66
  "esbuild": "^0.28.0",
95
- "oxfmt": "0.50.0",
96
- "oxlint": "^1.62.0",
97
- "puppeteer-core": "^25.0.2",
98
- "tsx": "^4.21.0",
67
+ "oxfmt": "0.54.0",
68
+ "oxlint": "^1.69.0",
69
+ "puppeteer-core": "^25.1.0",
70
+ "tsx": "^4.22.4",
99
71
  "typescript": "^6.0.3",
100
- "vitest": "^4.1.5"
72
+ "vitest": "^4.1.8"
101
73
  },
102
74
  "devEngines": {
103
75
  "runtime": [
@@ -110,14 +82,31 @@
110
82
  "engines": {
111
83
  "node": ">=24"
112
84
  },
113
- "packageManager": "pnpm@10.33.2",
114
- "pnpm": {
115
- "overrides": {
116
- "devtools-protocol": "0.0.1629771",
117
- "vite": "7.3.2"
118
- },
119
- "onlyBuiltDependencies": [
120
- "esbuild"
121
- ]
85
+ "scripts": {
86
+ "docs:list": "tsx scripts/docs-list.ts",
87
+ "docs:check": "node --no-deprecation --import tsx bin/oracle-cli.ts docs check",
88
+ "docs:site": "node scripts/build-docs-site.mjs",
89
+ "build": "tsgo -p tsconfig.build.json && pnpm run build:vendor",
90
+ "build:vendor": "node -e \"const fs=require('fs'); const path=require('path'); const vendorRoot=path.join('dist','vendor'); fs.rmSync(vendorRoot,{recursive:true,force:true}); const vendors=[['oracle-notifier']]; vendors.forEach(([name])=>{const src=path.join('vendor',name); const dest=path.join(vendorRoot,name); fs.mkdirSync(dest,{recursive:true}); if(fs.existsSync(src)){fs.cpSync(src,dest,{recursive:true,force:true});}});\"",
91
+ "start": "pnpm run build && node ./dist/scripts/run-cli.js",
92
+ "oracle": "pnpm start",
93
+ "check": "pnpm run format:check && pnpm run lint",
94
+ "typecheck": "tsgo --noEmit",
95
+ "format": "oxfmt --write .",
96
+ "format:check": "oxfmt --check .",
97
+ "format:diff": "oxfmt --write . && git --no-pager diff",
98
+ "lint": "pnpm run typecheck && oxlint",
99
+ "lint:fix": "oxlint --fix && pnpm run format",
100
+ "test": "vitest run",
101
+ "test:mcp": "pnpm run build && pnpm run test:mcp:unit && pnpm run test:mcp:mcporter",
102
+ "test:mcp:unit": "vitest run tests/mcp*.test.ts tests/mcp/**/*.test.ts",
103
+ "test:mcp:mcporter": "CHROME_DEVTOOLS_URL=http://127.0.0.1:0 pnpm dlx mcporter list oracle-local --schema --config config/mcporter.json && CHROME_DEVTOOLS_URL=http://127.0.0.1:0 pnpm dlx mcporter call oracle-local.sessions limit:1 --config config/mcporter.json",
104
+ "test:browser": "pnpm run build && tsx scripts/test-browser.ts && ./scripts/browser-smoke.sh",
105
+ "test:packed-cli": "node scripts/packed-cli-smoke.mjs",
106
+ "test:live": "ORACLE_LIVE_TEST=1 vitest run tests/live --exclude tests/live/openai-live.test.ts",
107
+ "test:live:fast": "ORACLE_LIVE_TEST=1 ORACLE_LIVE_TEST_FAST=1 vitest run tests/live/browser-fast-live.test.ts",
108
+ "test:pro": "ORACLE_LIVE_TEST=1 vitest run tests/live/openai-live.test.ts",
109
+ "test:coverage": "vitest run --coverage",
110
+ "mcp": "pnpm run build && node ./dist/bin/oracle-mcp.js"
122
111
  }
123
- }
112
+ }
File without changes