@steipete/oracle 0.9.0 → 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 (177) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +61 -48
  3. package/dist/bin/oracle-cli.js +455 -402
  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 +275 -117
  22. package/dist/src/browser/actions/navigation.js +161 -137
  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 +390 -295
  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 +1 -1
  40. package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
  41. package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
  42. package/dist/src/browser/providers/index.js +2 -2
  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 +62 -48
  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 +2 -2
  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 +3 -3
  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 +7 -7
  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 +127 -106
  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 +32 -28
  75. package/dist/src/cli/sessionCommand.js +31 -21
  76. package/dist/src/cli/sessionDisplay.js +95 -81
  77. package/dist/src/cli/sessionLineage.js +6 -2
  78. package/dist/src/cli/sessionRunner.js +103 -93
  79. package/dist/src/cli/sessionTable.js +26 -23
  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 +139 -128
  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 +19 -15
  86. package/dist/src/gemini-web/client.js +76 -70
  87. package/dist/src/gemini-web/executionMode.js +6 -8
  88. package/dist/src/gemini-web/executor.js +98 -93
  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 +51 -47
  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 +50 -41
  99. package/dist/src/oracle/config.js +96 -66
  100. package/dist/src/oracle/errors.js +38 -38
  101. package/dist/src/oracle/files.js +55 -46
  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 -33
  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 +16 -13
  112. package/dist/src/oracle/run.js +156 -134
  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 +77 -75
  124. package/dist/src/sessionStore.js +3 -3
  125. package/dist/src/version.js +10 -10
  126. package/dist/vendor/oracle-notifier/README.md +2 -0
  127. package/package.json +66 -62
  128. package/vendor/oracle-notifier/README.md +2 -0
  129. package/dist/markdansi/types/index.js +0 -4
  130. package/dist/oracle/bin/oracle-cli.js +0 -472
  131. package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
  132. package/dist/oracle/src/browser/actions/attachments.js +0 -82
  133. package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
  134. package/dist/oracle/src/browser/actions/navigation.js +0 -75
  135. package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
  136. package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
  137. package/dist/oracle/src/browser/config.js +0 -33
  138. package/dist/oracle/src/browser/constants.js +0 -40
  139. package/dist/oracle/src/browser/cookies.js +0 -210
  140. package/dist/oracle/src/browser/domDebug.js +0 -36
  141. package/dist/oracle/src/browser/index.js +0 -331
  142. package/dist/oracle/src/browser/pageActions.js +0 -5
  143. package/dist/oracle/src/browser/prompt.js +0 -88
  144. package/dist/oracle/src/browser/promptSummary.js +0 -20
  145. package/dist/oracle/src/browser/sessionRunner.js +0 -80
  146. package/dist/oracle/src/browser/types.js +0 -1
  147. package/dist/oracle/src/browser/utils.js +0 -62
  148. package/dist/oracle/src/browserMode.js +0 -1
  149. package/dist/oracle/src/cli/browserConfig.js +0 -44
  150. package/dist/oracle/src/cli/dryRun.js +0 -59
  151. package/dist/oracle/src/cli/engine.js +0 -17
  152. package/dist/oracle/src/cli/errorUtils.js +0 -9
  153. package/dist/oracle/src/cli/help.js +0 -70
  154. package/dist/oracle/src/cli/markdownRenderer.js +0 -15
  155. package/dist/oracle/src/cli/options.js +0 -103
  156. package/dist/oracle/src/cli/promptRequirement.js +0 -14
  157. package/dist/oracle/src/cli/rootAlias.js +0 -30
  158. package/dist/oracle/src/cli/sessionCommand.js +0 -77
  159. package/dist/oracle/src/cli/sessionDisplay.js +0 -270
  160. package/dist/oracle/src/cli/sessionRunner.js +0 -94
  161. package/dist/oracle/src/heartbeat.js +0 -43
  162. package/dist/oracle/src/oracle/client.js +0 -48
  163. package/dist/oracle/src/oracle/config.js +0 -29
  164. package/dist/oracle/src/oracle/errors.js +0 -101
  165. package/dist/oracle/src/oracle/files.js +0 -220
  166. package/dist/oracle/src/oracle/format.js +0 -33
  167. package/dist/oracle/src/oracle/fsAdapter.js +0 -7
  168. package/dist/oracle/src/oracle/oscProgress.js +0 -60
  169. package/dist/oracle/src/oracle/request.js +0 -48
  170. package/dist/oracle/src/oracle/run.js +0 -444
  171. package/dist/oracle/src/oracle/tokenStats.js +0 -39
  172. package/dist/oracle/src/oracle/types.js +0 -1
  173. package/dist/oracle/src/oracle.js +0 -9
  174. package/dist/oracle/src/sessionManager.js +0 -205
  175. package/dist/oracle/src/version.js +0 -39
  176. package/dist/scripts/chrome/browser-tools.js +0 -295
  177. package/dist/src/browser/profileSync.js +0 -141
@@ -1,10 +1,10 @@
1
- import { z } from 'zod';
2
- import { getCliVersion } from '../../version.js';
3
- import { LoggingMessageNotificationParamsSchema } from '@modelcontextprotocol/sdk/types.js';
4
- import { ensureBrowserAvailable, mapConsultToRunOptions } from '../utils.js';
5
- import { sessionStore } from '../../sessionStore.js';
6
- import { resolveRemoteServiceConfig } from '../../remote/remoteServiceConfig.js';
7
- import { createRemoteBrowserExecutor } from '../../remote/client.js';
1
+ import { z } from "zod";
2
+ import { getCliVersion } from "../../version.js";
3
+ import { LoggingMessageNotificationParamsSchema } from "@modelcontextprotocol/sdk/types.js";
4
+ import { ensureBrowserAvailable, mapConsultToRunOptions } from "../utils.js";
5
+ import { sessionStore } from "../../sessionStore.js";
6
+ import { resolveRemoteServiceConfig } from "../../remote/remoteServiceConfig.js";
7
+ import { createRemoteBrowserExecutor } from "../../remote/client.js";
8
8
  async function readSessionLogTail(sessionId, maxBytes) {
9
9
  try {
10
10
  const log = await sessionStore.readLog(sessionId);
@@ -17,62 +17,59 @@ async function readSessionLogTail(sessionId, maxBytes) {
17
17
  return null;
18
18
  }
19
19
  }
20
- import { performSessionRun } from '../../cli/sessionRunner.js';
21
- import { CHATGPT_URL } from '../../browser/constants.js';
22
- import { consultInputSchema } from '../types.js';
23
- import { loadUserConfig } from '../../config.js';
24
- import { resolveNotificationSettings } from '../../cli/notifier.js';
25
- import { mapModelToBrowserLabel, resolveBrowserModelLabel } from '../../cli/browserConfig.js';
20
+ import { performSessionRun } from "../../cli/sessionRunner.js";
21
+ import { CHATGPT_URL } from "../../browser/constants.js";
22
+ import { consultInputSchema } from "../types.js";
23
+ import { loadUserConfig } from "../../config.js";
24
+ import { resolveNotificationSettings } from "../../cli/notifier.js";
25
+ import { mapModelToBrowserLabel, resolveBrowserModelLabel } from "../../cli/browserConfig.js";
26
26
  // Use raw shapes so the MCP SDK (with its bundled Zod) wraps them and emits valid JSON Schema.
27
27
  const consultInputShape = {
28
- prompt: z
29
- .string()
30
- .min(1, 'Prompt is required.')
31
- .describe('User prompt to run.'),
28
+ prompt: z.string().min(1, "Prompt is required.").describe("User prompt to run."),
32
29
  files: z
33
30
  .array(z.string())
34
31
  .default([])
35
- .describe('Optional file paths or glob patterns (like the CLI `--file`). Resolved relative to the MCP server working directory.'),
32
+ .describe("Optional file paths or glob patterns (like the CLI `--file`). Resolved relative to the MCP server working directory."),
36
33
  model: z
37
34
  .string()
38
35
  .optional()
39
- .describe('Single model name/label. Prefer setting `engine` explicitly to avoid default surprises.'),
36
+ .describe("Single model name/label. Prefer setting `engine` explicitly to avoid default surprises."),
40
37
  models: z
41
38
  .array(z.string())
42
39
  .optional()
43
- .describe('Multi-model fan-out (API engine only). Cannot be combined with browser automation.'),
40
+ .describe("Multi-model fan-out (API engine only). Cannot be combined with browser automation."),
44
41
  engine: z
45
- .enum(['api', 'browser'])
42
+ .enum(["api", "browser"])
46
43
  .optional()
47
- .describe('Execution engine. `api` uses OpenAI/other providers. `browser` automates the ChatGPT web UI (supports attachments and ChatGPT-only model labels).'),
44
+ .describe("Execution engine. `api` uses OpenAI/other providers. `browser` automates the ChatGPT web UI (supports attachments and ChatGPT-only model labels)."),
48
45
  browserModelLabel: z
49
46
  .string()
50
47
  .optional()
51
48
  .describe('Browser-only: explicit ChatGPT UI label to select (overrides model mapping). Example: "GPT-5.2 Thinking".'),
52
49
  browserAttachments: z
53
- .enum(['auto', 'never', 'always'])
50
+ .enum(["auto", "never", "always"])
54
51
  .optional()
55
52
  .describe('Browser-only: how to deliver `files`. Use "always" for real ChatGPT file uploads (including images/PDFs). Use "never" to paste file contents inline. "auto" chooses based on prompt size.'),
56
53
  browserBundleFiles: z
57
54
  .boolean()
58
55
  .optional()
59
- .describe('Browser-only: bundle many files into a single upload (helps with upload limits).'),
56
+ .describe("Browser-only: bundle many files into a single upload (helps with upload limits)."),
60
57
  browserThinkingTime: z
61
- .enum(['light', 'standard', 'extended', 'heavy'])
58
+ .enum(["light", "standard", "extended", "heavy"])
62
59
  .optional()
63
- .describe('Browser-only: set ChatGPT thinking time when supported by the chosen model.'),
60
+ .describe("Browser-only: set ChatGPT thinking time when supported by the chosen model."),
64
61
  browserKeepBrowser: z
65
62
  .boolean()
66
63
  .optional()
67
- .describe('Browser-only: keep Chrome running after completion (useful for debugging).'),
64
+ .describe("Browser-only: keep Chrome running after completion (useful for debugging)."),
68
65
  search: z
69
66
  .boolean()
70
67
  .optional()
71
- .describe('API-only: enable/disable the provider search tool (browser engine ignores this).'),
68
+ .describe("API-only: enable/disable the provider search tool (browser engine ignores this)."),
72
69
  slug: z
73
70
  .string()
74
71
  .optional()
75
- .describe('Optional human-friendly session id (used for later `oracle sessions` lookups).'),
72
+ .describe("Optional human-friendly session id (used for later `oracle sessions` lookups)."),
76
73
  };
77
74
  const consultModelSummaryShape = z.object({
78
75
  model: z.string(),
@@ -129,7 +126,7 @@ export function summarizeModelRunsForConsult(runs) {
129
126
  : undefined;
130
127
  return {
131
128
  model: run.model,
132
- status: run.status ?? 'unknown',
129
+ status: run.status ?? "unknown",
133
130
  startedAt: run.startedAt,
134
131
  completedAt: run.completedAt,
135
132
  usage: run.usage,
@@ -141,15 +138,15 @@ export function summarizeModelRunsForConsult(runs) {
141
138
  }
142
139
  export function buildConsultBrowserConfig({ userConfig, env, runModel, inputModel, browserModelLabel, browserThinkingTime, browserKeepBrowser, }) {
143
140
  const configuredBrowser = userConfig.browser ?? {};
144
- const envProfileDir = (env.ORACLE_BROWSER_PROFILE_DIR ?? '').trim();
141
+ const envProfileDir = (env.ORACLE_BROWSER_PROFILE_DIR ?? "").trim();
145
142
  const hasProfileDir = envProfileDir.length > 0;
146
143
  const preferredLabel = (browserModelLabel ?? inputModel)?.trim();
147
- const isChatGptModel = runModel.startsWith('gpt-') && !runModel.includes('codex');
144
+ const isChatGptModel = runModel.startsWith("gpt-") && !runModel.includes("codex");
148
145
  const desiredModelLabel = isChatGptModel
149
146
  ? mapModelToBrowserLabel(runModel)
150
147
  : resolveBrowserModelLabel(preferredLabel, runModel);
151
148
  const configuredUrl = configuredBrowser.chatgptUrl ?? configuredBrowser.url ?? CHATGPT_URL;
152
- const manualLogin = hasProfileDir ? true : configuredBrowser.manualLogin ?? false;
149
+ const manualLogin = hasProfileDir ? true : (configuredBrowser.manualLogin ?? false);
153
150
  return {
154
151
  ...configuredBrowser,
155
152
  url: configuredUrl,
@@ -159,20 +156,22 @@ export function buildConsultBrowserConfig({ userConfig, env, runModel, inputMode
159
156
  hideWindow: configuredBrowser.hideWindow ?? false,
160
157
  keepBrowser: browserKeepBrowser ?? configuredBrowser.keepBrowser ?? false,
161
158
  manualLogin,
162
- manualLoginProfileDir: manualLogin ? ((envProfileDir || configuredBrowser.manualLoginProfileDir) ?? null) : null,
159
+ manualLoginProfileDir: manualLogin
160
+ ? ((envProfileDir || configuredBrowser.manualLoginProfileDir) ?? null)
161
+ : null,
163
162
  thinkingTime: browserThinkingTime ?? configuredBrowser.thinkingTime,
164
163
  desiredModel: desiredModelLabel || mapModelToBrowserLabel(runModel),
165
164
  };
166
165
  }
167
166
  export function registerConsultTool(server) {
168
- server.registerTool('consult', {
169
- title: 'Run an oracle session',
167
+ server.registerTool("consult", {
168
+ title: "Run an oracle session",
170
169
  description: 'Run a one-shot Oracle session (API or ChatGPT browser automation). Use `files` to attach project context. For browser-based image/file uploads, set `browserAttachments:"always"`. Sessions are stored under `ORACLE_HOME_DIR` (shared with the CLI).',
171
170
  // Cast to any to satisfy SDK typings across differing Zod versions.
172
171
  inputSchema: consultInputShape,
173
172
  outputSchema: consultOutputShape,
174
173
  }, async (input) => {
175
- const textContent = (text) => [{ type: 'text', text }];
174
+ const textContent = (text) => [{ type: "text", text }];
176
175
  const { prompt, files, model, models, engine, search, browserModelLabel, browserAttachments, browserBundleFiles, browserThinkingTime, browserKeepBrowser, slug, } = consultInputSchema.parse(input);
177
176
  const { config: userConfig } = await loadUserConfig();
178
177
  const { runOptions, resolvedEngine } = mapConsultToRunOptions({
@@ -189,15 +188,17 @@ export function registerConsultTool(server) {
189
188
  });
190
189
  const cwd = process.cwd();
191
190
  const resolvedRemote = resolveRemoteServiceConfig({ userConfig, env: process.env });
192
- const browserGuard = ensureBrowserAvailable(resolvedEngine, { remoteHost: resolvedRemote.host });
193
- if (resolvedEngine === 'browser' && browserGuard) {
191
+ const browserGuard = ensureBrowserAvailable(resolvedEngine, {
192
+ remoteHost: resolvedRemote.host,
193
+ });
194
+ if (resolvedEngine === "browser" && browserGuard) {
194
195
  return {
195
196
  isError: true,
196
197
  content: textContent(browserGuard),
197
198
  };
198
199
  }
199
200
  let browserDeps;
200
- if (resolvedEngine === 'browser' && resolvedRemote.host) {
201
+ if (resolvedEngine === "browser" && resolvedRemote.host) {
201
202
  if (!resolvedRemote.token) {
202
203
  return {
203
204
  isError: true,
@@ -205,11 +206,14 @@ export function registerConsultTool(server) {
205
206
  };
206
207
  }
207
208
  browserDeps = {
208
- executeBrowser: createRemoteBrowserExecutor({ host: resolvedRemote.host, token: resolvedRemote.token }),
209
+ executeBrowser: createRemoteBrowserExecutor({
210
+ host: resolvedRemote.host,
211
+ token: resolvedRemote.token,
212
+ }),
209
213
  };
210
214
  }
211
215
  let browserConfig;
212
- if (resolvedEngine === 'browser') {
216
+ if (resolvedEngine === "browser") {
213
217
  browserConfig = buildConsultBrowserConfig({
214
218
  userConfig,
215
219
  env: process.env,
@@ -235,10 +239,10 @@ export function registerConsultTool(server) {
235
239
  }, cwd, notifications);
236
240
  const logWriter = sessionStore.createLogWriter(sessionMeta.id);
237
241
  // Best-effort: emit MCP logging notifications for live chunks but never block the run.
238
- const sendLog = (text, level = 'info') => server.server
242
+ const sendLog = (text, level = "info") => server.server
239
243
  .sendLoggingMessage(LoggingMessageNotificationParamsSchema.parse({
240
244
  level,
241
- data: { text, bytes: Buffer.byteLength(text, 'utf8') },
245
+ data: { text, bytes: Buffer.byteLength(text, "utf8") },
242
246
  }))
243
247
  .catch(() => { });
244
248
  // Stream logs to both the session log and MCP logging notifications, but avoid buffering in memory
@@ -250,7 +254,7 @@ export function registerConsultTool(server) {
250
254
  };
251
255
  const write = (chunk) => {
252
256
  logWriter.writeChunk(chunk);
253
- sendLog(chunk, 'debug');
257
+ sendLog(chunk, "debug");
254
258
  return true;
255
259
  };
256
260
  try {
@@ -284,11 +288,11 @@ export function registerConsultTool(server) {
284
288
  const logTail = await readSessionLogTail(sessionMeta.id, 4000);
285
289
  const modelsSummary = summarizeModelRunsForConsult(finalMeta.models);
286
290
  return {
287
- content: textContent([summary, logTail || '(log empty)'].join('\n').trim()),
291
+ content: textContent([summary, logTail || "(log empty)"].join("\n").trim()),
288
292
  structuredContent: {
289
293
  sessionId: sessionMeta.id,
290
294
  status: finalMeta.status,
291
- output: logTail ?? '',
295
+ output: logTail ?? "",
292
296
  models: modelsSummary,
293
297
  },
294
298
  };
@@ -1,15 +1,15 @@
1
- import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import fs from 'node:fs/promises';
3
- import { sessionStore } from '../../sessionStore.js';
1
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import fs from "node:fs/promises";
3
+ import { sessionStore } from "../../sessionStore.js";
4
4
  // URIs:
5
5
  // - oracle-session://<id>/metadata
6
6
  // - oracle-session://<id>/log
7
7
  // - oracle-session://<id>/request
8
8
  export function registerSessionResources(server) {
9
- const template = new ResourceTemplate('oracle-session://{id}/{kind}', { list: undefined });
10
- server.registerResource('oracle-session', template, {
11
- title: 'oracle session resources',
12
- description: 'Read stored session metadata, log, or request payload.',
9
+ const template = new ResourceTemplate("oracle-session://{id}/{kind}", { list: undefined });
10
+ server.registerResource("oracle-session", template, {
11
+ title: "oracle session resources",
12
+ description: "Read stored session metadata, log, or request payload.",
13
13
  }, async (uri, variables) => {
14
14
  const idRaw = variables?.id;
15
15
  const kindRaw = variables?.kind;
@@ -17,10 +17,10 @@ export function registerSessionResources(server) {
17
17
  const id = Array.isArray(idRaw) ? idRaw[0] : idRaw;
18
18
  const kind = Array.isArray(kindRaw) ? kindRaw[0] : kindRaw;
19
19
  if (!id || !kind) {
20
- throw new Error('Missing id or kind');
20
+ throw new Error("Missing id or kind");
21
21
  }
22
22
  switch (kind) {
23
- case 'metadata': {
23
+ case "metadata": {
24
24
  const metadata = await sessionStore.readSession(id);
25
25
  if (!metadata) {
26
26
  throw new Error(`Session "${id}" not found.`);
@@ -34,7 +34,7 @@ export function registerSessionResources(server) {
34
34
  ],
35
35
  };
36
36
  }
37
- case 'log': {
37
+ case "log": {
38
38
  const log = await sessionStore.readLog(id);
39
39
  return {
40
40
  contents: [
@@ -45,7 +45,7 @@ export function registerSessionResources(server) {
45
45
  ],
46
46
  };
47
47
  }
48
- case 'request': {
48
+ case "request": {
49
49
  const request = await sessionStore.readRequest(id);
50
50
  if (request) {
51
51
  return {
@@ -58,7 +58,7 @@ export function registerSessionResources(server) {
58
58
  };
59
59
  }
60
60
  const paths = await sessionStore.getPaths(id);
61
- const raw = await fs.readFile(paths.request, 'utf8');
61
+ const raw = await fs.readFile(paths.request, "utf8");
62
62
  return {
63
63
  contents: [
64
64
  {
@@ -1,21 +1,24 @@
1
- import { z } from 'zod';
2
- import { sessionStore } from '../../sessionStore.js';
3
- import { sessionsInputSchema } from '../types.js';
1
+ import { z } from "zod";
2
+ import { sessionStore } from "../../sessionStore.js";
3
+ import { sessionsInputSchema } from "../types.js";
4
4
  const sessionsInputShape = {
5
5
  id: z
6
6
  .string()
7
7
  .optional()
8
- .describe('Session id or slug. If set, returns a single session (use detail:true to include metadata/request).'),
9
- hours: z.number().optional().describe('Look back this many hours when listing sessions (default: 24).'),
10
- limit: z.number().optional().describe('Maximum sessions to return when listing (default: 100).'),
8
+ .describe("Session id or slug. If set, returns a single session (use detail:true to include metadata/request)."),
9
+ hours: z
10
+ .number()
11
+ .optional()
12
+ .describe("Look back this many hours when listing sessions (default: 24)."),
13
+ limit: z.number().optional().describe("Maximum sessions to return when listing (default: 100)."),
11
14
  includeAll: z
12
15
  .boolean()
13
16
  .optional()
14
- .describe('Include sessions outside the time window when listing (mirrors `oracle status --all`).'),
17
+ .describe("Include sessions outside the time window when listing (mirrors `oracle status --all`)."),
15
18
  detail: z
16
19
  .boolean()
17
20
  .optional()
18
- .describe('When id is set, include session metadata + stored request + full log text.'),
21
+ .describe("When id is set, include session metadata + stored request + full log text."),
19
22
  };
20
23
  const sessionsOutputShape = {
21
24
  entries: z
@@ -38,14 +41,14 @@ const sessionsOutputShape = {
38
41
  .optional(),
39
42
  };
40
43
  export function registerSessionsTool(server) {
41
- server.registerTool('sessions', {
42
- title: 'List or fetch oracle sessions',
43
- description: 'Inspect Oracle session history stored under `ORACLE_HOME_DIR` (shared with the CLI). List recent sessions or fetch one by id/slug (optionally including metadata + request + log).',
44
+ server.registerTool("sessions", {
45
+ title: "List or fetch oracle sessions",
46
+ description: "Inspect Oracle session history stored under `ORACLE_HOME_DIR` (shared with the CLI). List recent sessions or fetch one by id/slug (optionally including metadata + request + log).",
44
47
  inputSchema: sessionsInputShape,
45
48
  outputSchema: sessionsOutputShape,
46
49
  }, async (input) => {
47
- const textContent = (text) => [{ type: 'text', text }];
48
- const { id, hours = 24, limit = 100, includeAll = false, detail = false } = sessionsInputSchema.parse(input);
50
+ const textContent = (text) => [{ type: "text", text }];
51
+ const { id, hours = 24, limit = 100, includeAll = false, detail = false, } = sessionsInputSchema.parse(input);
49
52
  if (id) {
50
53
  if (!detail) {
51
54
  const metadata = await sessionStore.readSession(id);
@@ -53,7 +56,7 @@ export function registerSessionsTool(server) {
53
56
  throw new Error(`Session "${id}" not found.`);
54
57
  }
55
58
  return {
56
- content: textContent(`${metadata.createdAt} | ${metadata.status} | ${metadata.model ?? 'n/a'} | ${metadata.id}`),
59
+ content: textContent(`${metadata.createdAt} | ${metadata.status} | ${metadata.model ?? "n/a"} | ${metadata.id}`),
57
60
  structuredContent: {
58
61
  entries: [
59
62
  {
@@ -81,12 +84,18 @@ export function registerSessionsTool(server) {
81
84
  };
82
85
  }
83
86
  const metas = await sessionStore.listSessions();
84
- const { entries, truncated, total } = sessionStore.filterSessions(metas, { hours, includeAll, limit });
87
+ const { entries, truncated, total } = sessionStore.filterSessions(metas, {
88
+ hours,
89
+ includeAll,
90
+ limit,
91
+ });
85
92
  return {
86
93
  content: [
87
94
  {
88
- type: 'text',
89
- text: entries.map((entry) => `${entry.createdAt} | ${entry.status} | ${entry.model ?? 'n/a'} | ${entry.id}`).join('\n'),
95
+ type: "text",
96
+ text: entries
97
+ .map((entry) => `${entry.createdAt} | ${entry.status} | ${entry.model ?? "n/a"} | ${entry.id}`)
98
+ .join("\n"),
90
99
  },
91
100
  ],
92
101
  structuredContent: {
@@ -1,14 +1,14 @@
1
- import { z } from 'zod';
1
+ import { z } from "zod";
2
2
  export const consultInputSchema = z.object({
3
- prompt: z.string().min(1, 'Prompt is required.'),
3
+ prompt: z.string().min(1, "Prompt is required."),
4
4
  files: z.array(z.string()).default([]),
5
5
  model: z.string().optional(),
6
6
  models: z.array(z.string()).optional(),
7
- engine: z.enum(['api', 'browser']).optional(),
7
+ engine: z.enum(["api", "browser"]).optional(),
8
8
  browserModelLabel: z.string().optional(),
9
- browserAttachments: z.enum(['auto', 'never', 'always']).optional(),
9
+ browserAttachments: z.enum(["auto", "never", "always"]).optional(),
10
10
  browserBundleFiles: z.boolean().optional(),
11
- browserThinkingTime: z.enum(['light', 'standard', 'extended', 'heavy']).optional(),
11
+ browserThinkingTime: z.enum(["light", "standard", "extended", "heavy"]).optional(),
12
12
  browserKeepBrowser: z.boolean().optional(),
13
13
  search: z.boolean().optional(),
14
14
  slug: z.string().optional(),
@@ -1,25 +1,33 @@
1
- import { resolveRunOptionsFromConfig } from '../cli/runOptions.js';
2
- import { Launcher } from 'chrome-launcher';
1
+ import { resolveRunOptionsFromConfig } from "../cli/runOptions.js";
2
+ import { Launcher } from "chrome-launcher";
3
3
  export function mapConsultToRunOptions({ prompt, files, model, models, engine, search, browserAttachments, browserBundleFiles, userConfig, env = process.env, }) {
4
4
  // Normalize CLI-style inputs through the shared resolver so config/env defaults apply,
5
5
  // then overlay MCP-only overrides such as explicit search toggles.
6
6
  const mergedModels = Array.isArray(models) && models.length > 0
7
7
  ? [model, ...models].filter((entry) => Boolean(entry?.trim()))
8
8
  : models;
9
- const result = resolveRunOptionsFromConfig({ prompt, files, model, models: mergedModels, engine, userConfig, env });
10
- if (typeof search === 'boolean') {
9
+ const result = resolveRunOptionsFromConfig({
10
+ prompt,
11
+ files,
12
+ model,
13
+ models: mergedModels,
14
+ engine,
15
+ userConfig,
16
+ env,
17
+ });
18
+ if (typeof search === "boolean") {
11
19
  result.runOptions.search = search;
12
20
  }
13
21
  if (browserAttachments) {
14
22
  result.runOptions.browserAttachments = browserAttachments;
15
23
  }
16
- if (typeof browserBundleFiles === 'boolean') {
24
+ if (typeof browserBundleFiles === "boolean") {
17
25
  result.runOptions.browserBundleFiles = browserBundleFiles;
18
26
  }
19
27
  return result;
20
28
  }
21
29
  export function ensureBrowserAvailable(engine, options) {
22
- if (engine !== 'browser') {
30
+ if (engine !== "browser") {
23
31
  return null;
24
32
  }
25
33
  const remoteHost = options?.remoteHost?.trim() || process.env.ORACLE_REMOTE_HOST?.trim();
@@ -31,7 +39,7 @@ export function ensureBrowserAvailable(engine, options) {
31
39
  }
32
40
  const found = Launcher.getFirstInstallation();
33
41
  if (!found) {
34
- return 'Browser engine unavailable: no Chrome installation found and CHROME_PATH is unset.';
42
+ return "Browser engine unavailable: no Chrome installation found and CHROME_PATH is unset.";
35
43
  }
36
44
  return null;
37
45
  }
@@ -1,8 +1,8 @@
1
- import { APIConnectionError, APIConnectionTimeoutError } from 'openai';
2
- import chalk from 'chalk';
3
- import { formatElapsed } from './format.js';
4
- import { startHeartbeat } from '../heartbeat.js';
5
- import { OracleResponseError, OracleTransportError, describeTransportError, toTransportError, } from './errors.js';
1
+ import { APIConnectionError, APIConnectionTimeoutError } from "openai";
2
+ import chalk from "chalk";
3
+ import { formatElapsed } from "./format.js";
4
+ import { startHeartbeat } from "../heartbeat.js";
5
+ import { OracleResponseError, OracleTransportError, describeTransportError, toTransportError, } from "./errors.js";
6
6
  const BACKGROUND_POLL_INTERVAL_MS = 5000;
7
7
  const BACKGROUND_RETRY_BASE_MS = 3000;
8
8
  const BACKGROUND_RETRY_MAX_MS = 15000;
@@ -18,10 +18,10 @@ export async function executeBackgroundResponse(params) {
18
18
  throw transportError;
19
19
  }
20
20
  if (!initialResponse || !initialResponse.id) {
21
- throw new OracleResponseError('API did not return a response ID for the background run.', initialResponse);
21
+ throw new OracleResponseError("API did not return a response ID for the background run.", initialResponse);
22
22
  }
23
23
  const responseId = initialResponse.id;
24
- log(chalk.dim(`API scheduled background response ${responseId} (status=${initialResponse.status ?? 'unknown'}). Monitoring up to ${Math.round(maxWaitMs / 60000)} minutes for completion...`));
24
+ log(chalk.dim(`API scheduled background response ${responseId} (status=${initialResponse.status ?? "unknown"}). Monitoring up to ${Math.round(maxWaitMs / 60000)} minutes for completion...`));
25
25
  let heartbeatActive = false;
26
26
  let stopHeartbeat = null;
27
27
  const stopHeartbeatNow = () => {
@@ -66,29 +66,29 @@ async function pollBackgroundResponse(params) {
66
66
  let lastStatus = response.status;
67
67
  // biome-ignore lint/nursery/noUnnecessaryConditions: intentional polling loop.
68
68
  while (true) {
69
- const status = response.status ?? 'completed';
69
+ const status = response.status ?? "completed";
70
70
  // firstCycle toggles immediately; keep for clarity in logs.
71
71
  if (firstCycle) {
72
72
  firstCycle = false;
73
73
  log(chalk.dim(`API background response status=${status}. We'll keep retrying automatically.`));
74
74
  }
75
- else if (status !== lastStatus && status !== 'completed') {
75
+ else if (status !== lastStatus && status !== "completed") {
76
76
  log(chalk.dim(`API background response status=${status}.`));
77
77
  }
78
78
  lastStatus = status;
79
- if (status === 'completed') {
79
+ if (status === "completed") {
80
80
  return response;
81
81
  }
82
- if (status !== 'in_progress' && status !== 'queued') {
82
+ if (status !== "in_progress" && status !== "queued") {
83
83
  const detail = response.error?.message || response.incomplete_details?.reason || status;
84
84
  throw new OracleResponseError(`Response did not complete: ${detail}`, response);
85
85
  }
86
86
  if (now() - startMark >= maxWaitMs) {
87
- throw new OracleTransportError('client-timeout', 'Timed out waiting for API background response to finish.');
87
+ throw new OracleTransportError("client-timeout", "Timed out waiting for API background response to finish.");
88
88
  }
89
89
  await wait(BACKGROUND_POLL_INTERVAL_MS);
90
90
  if (now() - startMark >= maxWaitMs) {
91
- throw new OracleTransportError('client-timeout', 'Timed out waiting for API background response to finish.');
91
+ throw new OracleTransportError("client-timeout", "Timed out waiting for API background response to finish.");
92
92
  }
93
93
  const { response: nextResponse, reconnected } = await retrieveBackgroundResponseWithRetry({
94
94
  client,
@@ -100,7 +100,7 @@ async function pollBackgroundResponse(params) {
100
100
  log,
101
101
  });
102
102
  if (reconnected) {
103
- const nextStatus = nextResponse.status ?? 'in_progress';
103
+ const nextStatus = nextResponse.status ?? "in_progress";
104
104
  log(chalk.dim(`Reconnected to API background response (status=${nextStatus}). API is still working...`));
105
105
  }
106
106
  response = nextResponse;
@@ -125,7 +125,7 @@ async function retrieveBackgroundResponseWithRetry(params) {
125
125
  log(chalk.yellow(`${describeTransportError(transportError, maxWaitMs)} Retrying in ${formatElapsed(delay)}...`));
126
126
  await wait(delay);
127
127
  if (now() - startMark >= maxWaitMs) {
128
- throw new OracleTransportError('client-timeout', 'Timed out waiting for API background response to finish.');
128
+ throw new OracleTransportError("client-timeout", "Timed out waiting for API background response to finish.");
129
129
  }
130
130
  }
131
131
  }