@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.
- package/LICENSE +1 -1
- package/README.md +61 -48
- package/dist/bin/oracle-cli.js +455 -402
- package/dist/bin/oracle-mcp.js +2 -2
- package/dist/bin/oracle.js +165 -279
- package/dist/scripts/agent-send.js +31 -31
- package/dist/scripts/check.js +6 -6
- package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
- package/dist/scripts/docs-list.js +30 -30
- package/dist/scripts/git-policy.js +25 -23
- package/dist/scripts/run-cli.js +8 -8
- package/dist/scripts/runner.js +203 -195
- package/dist/scripts/test-browser.js +21 -18
- package/dist/scripts/test-remote-chrome.js +20 -20
- package/dist/src/bridge/connection.js +18 -18
- package/dist/src/bridge/userConfigFile.js +7 -7
- package/dist/src/browser/actions/assistantResponse.js +149 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +275 -117
- package/dist/src/browser/actions/navigation.js +161 -137
- package/dist/src/browser/actions/promptComposer.js +100 -64
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/chromeLifecycle.js +62 -60
- package/dist/src/browser/config.js +34 -15
- package/dist/src/browser/constants.js +17 -12
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +62 -62
- package/dist/src/browser/domDebug.js +1 -1
- package/dist/src/browser/index.js +390 -295
- package/dist/src/browser/modelStrategy.js +1 -1
- package/dist/src/browser/pageActions.js +5 -5
- package/dist/src/browser/policies.js +16 -13
- package/dist/src/browser/profileState.js +44 -39
- package/dist/src/browser/prompt.js +72 -42
- package/dist/src/browser/promptSummary.js +5 -5
- package/dist/src/browser/providerDomFlow.js +1 -1
- package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
- package/dist/src/browser/providers/index.js +2 -2
- package/dist/src/browser/reattach.js +67 -34
- package/dist/src/browser/reattachHelpers.js +31 -26
- package/dist/src/browser/sessionRunner.js +37 -25
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +16 -16
- package/dist/src/cli/bridge/client.js +28 -20
- package/dist/src/cli/bridge/codexConfig.js +16 -16
- package/dist/src/cli/bridge/doctor.js +47 -39
- package/dist/src/cli/bridge/host.js +58 -56
- package/dist/src/cli/browserConfig.js +62 -48
- package/dist/src/cli/browserDefaults.js +27 -26
- package/dist/src/cli/bundleWarnings.js +1 -1
- package/dist/src/cli/clipboard.js +11 -2
- package/dist/src/cli/detach.js +2 -2
- package/dist/src/cli/dryRun.js +29 -25
- package/dist/src/cli/duplicatePromptGuard.js +3 -3
- package/dist/src/cli/engine.js +9 -9
- package/dist/src/cli/errorUtils.js +1 -1
- package/dist/src/cli/fileSize.js +3 -3
- package/dist/src/cli/format.js +2 -2
- package/dist/src/cli/help.js +28 -28
- package/dist/src/cli/hiddenAliases.js +3 -3
- package/dist/src/cli/markdownBundle.js +7 -7
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +127 -106
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/promptRequirement.js +2 -2
- package/dist/src/cli/renderOutput.js +1 -1
- package/dist/src/cli/rootAlias.js +1 -1
- package/dist/src/cli/runOptions.js +32 -28
- package/dist/src/cli/sessionCommand.js +31 -21
- package/dist/src/cli/sessionDisplay.js +95 -81
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +103 -93
- package/dist/src/cli/sessionTable.js +26 -23
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +139 -128
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +19 -15
- package/dist/src/gemini-web/client.js +76 -70
- package/dist/src/gemini-web/executionMode.js +6 -8
- package/dist/src/gemini-web/executor.js +98 -93
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/server.js +16 -12
- package/dist/src/mcp/tools/consult.js +51 -47
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +5 -5
- package/dist/src/mcp/utils.js +15 -7
- package/dist/src/oracle/background.js +15 -15
- package/dist/src/oracle/claude.js +53 -25
- package/dist/src/oracle/client.js +50 -41
- package/dist/src/oracle/config.js +96 -66
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +55 -46
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -33
- package/dist/src/oracle/logging.js +7 -7
- package/dist/src/oracle/markdown.js +28 -28
- package/dist/src/oracle/modelResolver.js +16 -16
- package/dist/src/oracle/multiModelRunner.js +12 -12
- package/dist/src/oracle/oscProgress.js +8 -8
- package/dist/src/oracle/promptAssembly.js +6 -3
- package/dist/src/oracle/request.js +16 -13
- package/dist/src/oracle/run.js +156 -134
- package/dist/src/oracle/runUtils.js +8 -5
- package/dist/src/oracle/tokenEstimate.js +6 -6
- package/dist/src/oracle/tokenStats.js +5 -5
- package/dist/src/oracle/tokenStringifier.js +5 -5
- package/dist/src/oracle.js +12 -12
- package/dist/src/oracleHome.js +3 -3
- package/dist/src/remote/client.js +25 -25
- package/dist/src/remote/health.js +20 -20
- package/dist/src/remote/remoteServiceConfig.js +9 -9
- package/dist/src/remote/server.js +129 -118
- package/dist/src/sessionManager.js +77 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +66 -62
- package/vendor/oracle-notifier/README.md +2 -0
- package/dist/markdansi/types/index.js +0 -4
- package/dist/oracle/bin/oracle-cli.js +0 -472
- package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
- package/dist/oracle/src/browser/actions/attachments.js +0 -82
- package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
- package/dist/oracle/src/browser/actions/navigation.js +0 -75
- package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
- package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
- package/dist/oracle/src/browser/config.js +0 -33
- package/dist/oracle/src/browser/constants.js +0 -40
- package/dist/oracle/src/browser/cookies.js +0 -210
- package/dist/oracle/src/browser/domDebug.js +0 -36
- package/dist/oracle/src/browser/index.js +0 -331
- package/dist/oracle/src/browser/pageActions.js +0 -5
- package/dist/oracle/src/browser/prompt.js +0 -88
- package/dist/oracle/src/browser/promptSummary.js +0 -20
- package/dist/oracle/src/browser/sessionRunner.js +0 -80
- package/dist/oracle/src/browser/types.js +0 -1
- package/dist/oracle/src/browser/utils.js +0 -62
- package/dist/oracle/src/browserMode.js +0 -1
- package/dist/oracle/src/cli/browserConfig.js +0 -44
- package/dist/oracle/src/cli/dryRun.js +0 -59
- package/dist/oracle/src/cli/engine.js +0 -17
- package/dist/oracle/src/cli/errorUtils.js +0 -9
- package/dist/oracle/src/cli/help.js +0 -70
- package/dist/oracle/src/cli/markdownRenderer.js +0 -15
- package/dist/oracle/src/cli/options.js +0 -103
- package/dist/oracle/src/cli/promptRequirement.js +0 -14
- package/dist/oracle/src/cli/rootAlias.js +0 -30
- package/dist/oracle/src/cli/sessionCommand.js +0 -77
- package/dist/oracle/src/cli/sessionDisplay.js +0 -270
- package/dist/oracle/src/cli/sessionRunner.js +0 -94
- package/dist/oracle/src/heartbeat.js +0 -43
- package/dist/oracle/src/oracle/client.js +0 -48
- package/dist/oracle/src/oracle/config.js +0 -29
- package/dist/oracle/src/oracle/errors.js +0 -101
- package/dist/oracle/src/oracle/files.js +0 -220
- package/dist/oracle/src/oracle/format.js +0 -33
- package/dist/oracle/src/oracle/fsAdapter.js +0 -7
- package/dist/oracle/src/oracle/oscProgress.js +0 -60
- package/dist/oracle/src/oracle/request.js +0 -48
- package/dist/oracle/src/oracle/run.js +0 -444
- package/dist/oracle/src/oracle/tokenStats.js +0 -39
- package/dist/oracle/src/oracle/types.js +0 -1
- package/dist/oracle/src/oracle.js +0 -9
- package/dist/oracle/src/sessionManager.js +0 -205
- package/dist/oracle/src/version.js +0 -39
- package/dist/scripts/chrome/browser-tools.js +0 -295
- package/dist/src/browser/profileSync.js +0 -141
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { getCliVersion } from
|
|
3
|
-
import { LoggingMessageNotificationParamsSchema } from
|
|
4
|
-
import { ensureBrowserAvailable, mapConsultToRunOptions } from
|
|
5
|
-
import { sessionStore } from
|
|
6
|
-
import { resolveRemoteServiceConfig } from
|
|
7
|
-
import { createRemoteBrowserExecutor } from
|
|
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
|
|
21
|
-
import { CHATGPT_URL } from
|
|
22
|
-
import { consultInputSchema } from
|
|
23
|
-
import { loadUserConfig } from
|
|
24
|
-
import { resolveNotificationSettings } from
|
|
25
|
-
import { mapModelToBrowserLabel, resolveBrowserModelLabel } from
|
|
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(
|
|
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(
|
|
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(
|
|
40
|
+
.describe("Multi-model fan-out (API engine only). Cannot be combined with browser automation."),
|
|
44
41
|
engine: z
|
|
45
|
-
.enum([
|
|
42
|
+
.enum(["api", "browser"])
|
|
46
43
|
.optional()
|
|
47
|
-
.describe(
|
|
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([
|
|
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(
|
|
56
|
+
.describe("Browser-only: bundle many files into a single upload (helps with upload limits)."),
|
|
60
57
|
browserThinkingTime: z
|
|
61
|
-
.enum([
|
|
58
|
+
.enum(["light", "standard", "extended", "heavy"])
|
|
62
59
|
.optional()
|
|
63
|
-
.describe(
|
|
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(
|
|
64
|
+
.describe("Browser-only: keep Chrome running after completion (useful for debugging)."),
|
|
68
65
|
search: z
|
|
69
66
|
.boolean()
|
|
70
67
|
.optional()
|
|
71
|
-
.describe(
|
|
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(
|
|
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 ??
|
|
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 ??
|
|
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(
|
|
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
|
|
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(
|
|
169
|
-
title:
|
|
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:
|
|
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, {
|
|
193
|
-
|
|
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 ===
|
|
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({
|
|
209
|
+
executeBrowser: createRemoteBrowserExecutor({
|
|
210
|
+
host: resolvedRemote.host,
|
|
211
|
+
token: resolvedRemote.token,
|
|
212
|
+
}),
|
|
209
213
|
};
|
|
210
214
|
}
|
|
211
215
|
let browserConfig;
|
|
212
|
-
if (resolvedEngine ===
|
|
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 =
|
|
242
|
+
const sendLog = (text, level = "info") => server.server
|
|
239
243
|
.sendLoggingMessage(LoggingMessageNotificationParamsSchema.parse({
|
|
240
244
|
level,
|
|
241
|
-
data: { text, bytes: Buffer.byteLength(text,
|
|
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,
|
|
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 ||
|
|
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
|
|
2
|
-
import fs from
|
|
3
|
-
import { sessionStore } from
|
|
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(
|
|
10
|
-
server.registerResource(
|
|
11
|
-
title:
|
|
12
|
-
description:
|
|
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(
|
|
20
|
+
throw new Error("Missing id or kind");
|
|
21
21
|
}
|
|
22
22
|
switch (kind) {
|
|
23
|
-
case
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
2
|
-
import { sessionStore } from
|
|
3
|
-
import { sessionsInputSchema } from
|
|
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(
|
|
9
|
-
hours: z
|
|
10
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
42
|
-
title:
|
|
43
|
-
description:
|
|
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:
|
|
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 ??
|
|
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, {
|
|
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:
|
|
89
|
-
text: entries
|
|
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: {
|
package/dist/src/mcp/types.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { z } from
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
export const consultInputSchema = z.object({
|
|
3
|
-
prompt: z.string().min(1,
|
|
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([
|
|
7
|
+
engine: z.enum(["api", "browser"]).optional(),
|
|
8
8
|
browserModelLabel: z.string().optional(),
|
|
9
|
-
browserAttachments: z.enum([
|
|
9
|
+
browserAttachments: z.enum(["auto", "never", "always"]).optional(),
|
|
10
10
|
browserBundleFiles: z.boolean().optional(),
|
|
11
|
-
browserThinkingTime: z.enum([
|
|
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(),
|
package/dist/src/mcp/utils.js
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
|
-
import { resolveRunOptionsFromConfig } from
|
|
2
|
-
import { Launcher } from
|
|
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({
|
|
10
|
-
|
|
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 ===
|
|
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 !==
|
|
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
|
|
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
|
|
2
|
-
import chalk from
|
|
3
|
-
import { formatElapsed } from
|
|
4
|
-
import { startHeartbeat } from
|
|
5
|
-
import { OracleResponseError, OracleTransportError, describeTransportError, toTransportError, } from
|
|
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(
|
|
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 ??
|
|
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 ??
|
|
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 !==
|
|
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 ===
|
|
79
|
+
if (status === "completed") {
|
|
80
80
|
return response;
|
|
81
81
|
}
|
|
82
|
-
if (status !==
|
|
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(
|
|
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(
|
|
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 ??
|
|
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(
|
|
128
|
+
throw new OracleTransportError("client-timeout", "Timed out waiting for API background response to finish.");
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
}
|