@steipete/oracle 0.12.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +72 -61
  2. package/dist/bin/oracle-cli.js +77 -68
  3. package/dist/bin/oracle-mcp.js +0 -0
  4. package/dist/docs-site/.nojekyll +0 -0
  5. package/dist/docs-site/CNAME +1 -0
  6. package/dist/docs-site/RELEASING.html +410 -0
  7. package/dist/docs-site/agents.html +374 -0
  8. package/dist/docs-site/anthropic.html +368 -0
  9. package/dist/docs-site/bridge.html +400 -0
  10. package/dist/docs-site/browser-mode.html +593 -0
  11. package/dist/docs-site/chromium-forks.html +347 -0
  12. package/dist/docs-site/cli-reference.html +346 -0
  13. package/dist/docs-site/configuration.html +452 -0
  14. package/dist/docs-site/favicon.svg +14 -0
  15. package/dist/docs-site/followup.html +375 -0
  16. package/dist/docs-site/gemini.html +383 -0
  17. package/dist/docs-site/grok.html +325 -0
  18. package/dist/docs-site/index.html +360 -0
  19. package/dist/docs-site/install.html +335 -0
  20. package/dist/docs-site/linux.html +321 -0
  21. package/dist/docs-site/llms.txt +43 -0
  22. package/dist/docs-site/manual-tests.html +596 -0
  23. package/dist/docs-site/mcp.html +391 -0
  24. package/dist/docs-site/multimodel.html +364 -0
  25. package/dist/docs-site/mythical-pro-agents.html +360 -0
  26. package/dist/docs-site/notifier.html +338 -0
  27. package/dist/docs-site/openai-endpoints.html +387 -0
  28. package/dist/docs-site/openrouter.html +344 -0
  29. package/dist/docs-site/quickstart.html +369 -0
  30. package/dist/docs-site/refactor/ux.html +532 -0
  31. package/dist/docs-site/sessions.html +388 -0
  32. package/dist/docs-site/social-card.png +0 -0
  33. package/dist/docs-site/social-card.svg +79 -0
  34. package/dist/docs-site/spec.html +363 -0
  35. package/dist/docs-site/testing.html +320 -0
  36. package/dist/docs-site/tui-debug.html +326 -0
  37. package/dist/docs-site/windows-work.html +323 -0
  38. package/dist/docs-site/windows.html +320 -0
  39. package/dist/src/browser/actions/assistantResponse.js +91 -9
  40. package/dist/src/browser/actions/deepResearch.js +180 -52
  41. package/dist/src/browser/actions/modelSelection.js +111 -24
  42. package/dist/src/browser/actions/navigation.js +358 -67
  43. package/dist/src/browser/actions/promptComposer.js +241 -46
  44. package/dist/src/browser/actions/thinkingTime.js +308 -89
  45. package/dist/src/browser/artifacts.js +19 -0
  46. package/dist/src/browser/chatgptFiles.js +797 -0
  47. package/dist/src/browser/chatgptImages.js +130 -3
  48. package/dist/src/browser/chromeLifecycle.js +4 -0
  49. package/dist/src/browser/config.js +4 -0
  50. package/dist/src/browser/index.js +353 -41
  51. package/dist/src/browser/pageActions.js +1 -1
  52. package/dist/src/browser/policies.js +2 -6
  53. package/dist/src/browser/projectSourcesRunner.js +14 -2
  54. package/dist/src/browser/prompt.js +11 -15
  55. package/dist/src/browser/providers/chatgptDomProvider.js +1 -0
  56. package/dist/src/browser/reattach.js +6 -2
  57. package/dist/src/browser/reattachability.js +22 -10
  58. package/dist/src/browser/recoverConversation.js +73 -0
  59. package/dist/src/browser/sessionRunner.js +4 -1
  60. package/dist/src/cli/bridge/doctor.js +7 -2
  61. package/dist/src/cli/browserConfig.js +13 -2
  62. package/dist/src/cli/browserDefaults.js +3 -0
  63. package/dist/src/cli/browserTabs.js +129 -54
  64. package/dist/src/cli/engine.js +6 -2
  65. package/dist/src/cli/followup.js +72 -0
  66. package/dist/src/cli/help.js +1 -1
  67. package/dist/src/cli/options.js +20 -0
  68. package/dist/src/cli/reattachGuidance.js +8 -0
  69. package/dist/src/cli/runOptions.js +10 -31
  70. package/dist/src/cli/sessionCommand.js +4 -0
  71. package/dist/src/cli/sessionDisplay.js +17 -3
  72. package/dist/src/cli/sessionLineage.js +7 -4
  73. package/dist/src/cli/sessionRunner.js +16 -1
  74. package/dist/src/config.js +164 -9
  75. package/dist/src/gemini-web/client.js +5 -10
  76. package/dist/src/gemini-web/executor.js +1 -24
  77. package/dist/src/gemini-web/models.js +83 -0
  78. package/dist/src/mcp/server.js +2 -0
  79. package/dist/src/mcp/tools/chatgptImage.js +127 -0
  80. package/dist/src/mcp/tools/consult.js +261 -167
  81. package/dist/src/mcp/types.js +2 -0
  82. package/dist/src/mcp/utils.js +87 -1
  83. package/dist/src/oracle/config.js +27 -1
  84. package/dist/src/oracle/files.js +4 -6
  85. package/dist/src/oracle/geminiModels.js +2 -0
  86. package/dist/src/oracle/markdown.js +36 -3
  87. package/dist/src/oracle/modelResolver.js +21 -13
  88. package/dist/src/oracle/promptAssembly.js +2 -4
  89. package/dist/src/oracle/request.js +3 -5
  90. package/dist/src/oracle/tokenStats.js +5 -1
  91. package/dist/src/oracle.js +1 -1
  92. package/dist/src/sessionManager.js +1 -0
  93. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  94. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  95. package/package.json +20 -29
  96. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  97. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
@@ -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")
@@ -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
  },
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@steipete/oracle",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
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
- "homepage": "https://github.com/steipete/oracle#readme",
6
+ "homepage": "https://askoracle.sh",
7
7
  "bugs": {
8
8
  "url": "https://github.com/steipete/oracle/issues"
9
9
  },
@@ -58,7 +58,7 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "@anthropic-ai/tokenizer": "^0.0.4",
61
- "@google/genai": "^2.0.1",
61
+ "@google/genai": "^2.8.0",
62
62
  "@google/generative-ai": "^0.24.1",
63
63
  "@modelcontextprotocol/sdk": "^1.29.0",
64
64
  "@steipete/sweet-cookie": "^0.3.0",
@@ -66,38 +66,38 @@
66
66
  "chrome-launcher": "^1.2.1",
67
67
  "chrome-remote-interface": "^0.34.0",
68
68
  "clipboardy": "^5.3.1",
69
- "commander": "^14.0.3",
69
+ "commander": "^15.0.0",
70
70
  "dotenv": "^17.4.2",
71
71
  "fast-glob": "^3.3.3",
72
72
  "gpt-tokenizer": "^3.4.0",
73
- "inquirer": "13.4.3",
73
+ "inquirer": "14.0.2",
74
74
  "json5": "^2.2.3",
75
75
  "kleur": "^4.1.5",
76
76
  "markdansi": "0.2.1",
77
- "openai": "^6.38.0",
77
+ "openai": "^6.42.0",
78
78
  "osc-progress": "^0.3.0",
79
- "qs": "^6.15.1",
80
- "shiki": "^4.0.2",
79
+ "qs": "^6.15.2",
80
+ "shiki": "^4.2.0",
81
81
  "toasted-notifier": "^10.1.0",
82
82
  "tokentally": "^0.1.1",
83
- "zod": "^4.4.2"
83
+ "zod": "^4.4.3"
84
84
  },
85
85
  "devDependencies": {
86
86
  "@anthropic-ai/tokenizer": "^0.0.4",
87
87
  "@types/chrome-remote-interface": "^0.34.0",
88
88
  "@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",
89
+ "@types/node": "^25.9.2",
90
+ "@typescript/native-preview": "7.0.0-dev.20260609.1",
91
+ "@vitest/coverage-v8": "4.1.8",
92
+ "devtools-protocol": "0.0.1643702",
93
+ "es-toolkit": "^1.47.0",
94
94
  "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",
95
+ "oxfmt": "0.54.0",
96
+ "oxlint": "^1.69.0",
97
+ "puppeteer-core": "^25.1.0",
98
+ "tsx": "^4.22.4",
99
99
  "typescript": "^6.0.3",
100
- "vitest": "^4.1.5"
100
+ "vitest": "^4.1.8"
101
101
  },
102
102
  "devEngines": {
103
103
  "runtime": [
@@ -110,14 +110,5 @@
110
110
  "engines": {
111
111
  "node": ">=24"
112
112
  },
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
- ]
122
- }
113
+ "packageManager": "pnpm@10.33.2"
123
114
  }