@steipete/oracle 0.18.0 → 0.20.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/README.md +1 -1
- package/dist/bin/oracle-cli.js +76 -76
- package/dist/bin/oracle-mcp.js +0 -0
- package/dist/src/browser/actions/archiveConversation.js +31 -7
- package/dist/src/browser/actions/assistantResponse.js +43 -1
- package/dist/src/browser/actions/attachmentContext.js +239 -0
- package/dist/src/browser/actions/attachmentDataTransfer.js +19 -1
- package/dist/src/browser/actions/attachmentEvidence.js +78 -0
- package/dist/src/browser/actions/attachmentProgress.js +41 -0
- package/dist/src/browser/actions/attachmentPrompt.js +130 -0
- package/dist/src/browser/actions/attachments.js +306 -355
- package/dist/src/browser/actions/deepResearch.js +170 -45
- package/dist/src/browser/actions/modelSelection.js +91 -4
- package/dist/src/browser/actions/navigation.js +1 -1
- package/dist/src/browser/actions/promptComposer.js +364 -186
- package/dist/src/browser/actions/remoteFileTransfer.js +4 -2
- package/dist/src/browser/actions/thinkingStatus.js +2 -5
- package/dist/src/browser/actions/thinkingTime.js +156 -16
- package/dist/src/browser/actions/webSearch.js +96 -0
- package/dist/src/browser/attachRunning.js +46 -2
- package/dist/src/browser/attachmentValidation.js +53 -0
- package/dist/src/browser/cancellation.js +105 -0
- package/dist/src/browser/chatgptImages.js +2 -0
- package/dist/src/browser/chatgptThrottle.js +104 -0
- package/dist/src/browser/chromeLifecycle.js +272 -47
- package/dist/src/browser/config.js +14 -1
- package/dist/src/browser/detect.js +1 -1
- package/dist/src/browser/index.js +525 -274
- package/dist/src/browser/liveTabs.js +3 -4
- package/dist/src/browser/modelDisplay.js +9 -0
- package/dist/src/browser/pageActions.js +2 -2
- package/dist/src/browser/profileState.js +114 -9
- package/dist/src/browser/projectSourcesRunner.js +83 -15
- package/dist/src/browser/prompt.js +272 -113
- package/dist/src/browser/providers/chatgptDomProvider.js +3 -0
- package/dist/src/browser/reattach.js +31 -4
- package/dist/src/browser/recoveryTarget.js +131 -0
- package/dist/src/browser/sessionRunner.js +73 -11
- package/dist/src/browser/tabLeaseRegistry.js +434 -38
- package/dist/src/browser/targetClaim.js +54 -0
- package/dist/src/browser/utils.js +20 -2
- package/dist/src/cli/browserConfig.js +36 -4
- package/dist/src/cli/browserDefaults.js +10 -0
- package/dist/src/cli/browserTabs.js +15 -29
- package/dist/src/cli/detachedSession.js +69 -0
- package/dist/src/cli/dryRun.js +58 -48
- package/dist/src/cli/harvestIntegrity.js +140 -0
- package/dist/src/cli/options.js +15 -0
- package/dist/src/cli/recoveredBrowserHarvest.js +50 -0
- package/dist/src/cli/runOptions.js +19 -4
- package/dist/src/cli/serveBrowserConfig.js +24 -0
- package/dist/src/cli/sessionDisplay.js +13 -7
- package/dist/src/cli/sessionRunner.js +95 -12
- package/dist/src/config.js +1 -0
- package/dist/src/gemini-web/client.js +67 -21
- package/dist/src/gemini-web/executor.js +4 -0
- package/dist/src/gemini-web/http.js +15 -0
- package/dist/src/gemini-web/models.js +15 -11
- package/dist/src/mcp/server.js +11 -14
- package/dist/src/mcp/tools/chatgptImage.js +4 -4
- package/dist/src/mcp/tools/consult.js +86 -24
- package/dist/src/mcp/tools/projectSources.js +4 -6
- package/dist/src/mcp/tools/sessionResources.js +1 -1
- package/dist/src/mcp/tools/sessions.js +2 -2
- package/dist/src/mcp/tools/wait.js +182 -0
- package/dist/src/mcp/types.js +8 -1
- package/dist/src/oracle/config.js +14 -0
- package/dist/src/oracle/errors.js +11 -0
- package/dist/src/oracle/geminiModels.js +1 -0
- package/dist/src/oracle/providerRoutePlan.js +3 -2
- package/dist/src/oracle/run.js +27 -4
- package/dist/src/remote/client.js +73 -6
- package/dist/src/remote/health.js +6 -2
- package/dist/src/remote/runSlots.js +83 -0
- package/dist/src/remote/server.js +346 -71
- package/dist/src/sessionManager.js +36 -1
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/package.json +25 -23
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/dist/bin/oracle.js +0 -683
- package/dist/docs-site/.nojekyll +0 -0
- package/dist/docs-site/CNAME +0 -1
- package/dist/docs-site/RELEASING.html +0 -410
- package/dist/docs-site/agents.html +0 -374
- package/dist/docs-site/anthropic.html +0 -368
- package/dist/docs-site/bridge.html +0 -400
- package/dist/docs-site/browser-mode.html +0 -588
- package/dist/docs-site/chromium-forks.html +0 -347
- package/dist/docs-site/cli-reference.html +0 -346
- package/dist/docs-site/configuration.html +0 -436
- package/dist/docs-site/favicon.svg +0 -14
- package/dist/docs-site/followup.html +0 -368
- package/dist/docs-site/gemini.html +0 -379
- package/dist/docs-site/grok.html +0 -325
- package/dist/docs-site/index.html +0 -360
- package/dist/docs-site/install.html +0 -335
- package/dist/docs-site/linux.html +0 -321
- package/dist/docs-site/llms.txt +0 -43
- package/dist/docs-site/manual-tests.html +0 -595
- package/dist/docs-site/mcp.html +0 -368
- package/dist/docs-site/multimodel.html +0 -364
- package/dist/docs-site/mythical-pro-agents.html +0 -360
- package/dist/docs-site/notifier.html +0 -338
- package/dist/docs-site/openai-endpoints.html +0 -387
- package/dist/docs-site/openrouter.html +0 -344
- package/dist/docs-site/quickstart.html +0 -369
- package/dist/docs-site/refactor/ux.html +0 -532
- package/dist/docs-site/sessions.html +0 -388
- package/dist/docs-site/social-card.png +0 -0
- package/dist/docs-site/social-card.svg +0 -79
- package/dist/docs-site/spec.html +0 -363
- package/dist/docs-site/testing.html +0 -320
- package/dist/docs-site/tui-debug.html +0 -326
- package/dist/docs-site/windows-work.html +0 -323
- package/dist/docs-site/windows.html +0 -320
- 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/chromeCookies.js +0 -312
- package/dist/src/browser/keytarShim.js +0 -56
- package/dist/src/browser/profileSync.js +0 -141
- package/dist/src/browser/windowsCookies.js +0 -219
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
export { parseDuration } from "../duration.js";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { currentBrowserAbortSignal } from "./cancellation.js";
|
|
3
|
+
export function delay(ms, signal = currentBrowserAbortSignal()) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
if (signal?.aborted) {
|
|
6
|
+
reject(signal.reason);
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const timer = setTimeout(() => {
|
|
10
|
+
signal?.removeEventListener("abort", onAbort);
|
|
11
|
+
resolve();
|
|
12
|
+
}, ms);
|
|
13
|
+
const onAbort = () => {
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
signal?.removeEventListener("abort", onAbort);
|
|
16
|
+
reject(signal?.reason);
|
|
17
|
+
};
|
|
18
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
19
|
+
});
|
|
4
20
|
}
|
|
5
21
|
export function estimateTokenCount(text) {
|
|
6
22
|
if (!text) {
|
|
@@ -18,6 +34,8 @@ export async function withRetries(task, options = {}) {
|
|
|
18
34
|
return await task();
|
|
19
35
|
}
|
|
20
36
|
catch (error) {
|
|
37
|
+
if (error instanceof Error && ["AbortError", "BrowserRunCancelledError"].includes(error.name))
|
|
38
|
+
throw error;
|
|
21
39
|
if (attempt === retries) {
|
|
22
40
|
throw error;
|
|
23
41
|
}
|
|
@@ -5,6 +5,7 @@ import { normalizeThinkingTimeLevel } from "../oracle/thinkingTime.js";
|
|
|
5
5
|
import { CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET } from "../browser/constants.js";
|
|
6
6
|
import { normalizeChatgptUrl } from "../browser/utils.js";
|
|
7
7
|
import { parseDuration } from "../duration.js";
|
|
8
|
+
import { resolveBrowserApprovalWait } from "../browser/config.js";
|
|
8
9
|
import { normalizeBrowserModelStrategy } from "../browser/modelStrategy.js";
|
|
9
10
|
import { getOracleHomeDir } from "../oracleHome.js";
|
|
10
11
|
const DEFAULT_BROWSER_TIMEOUT_MS = 1_200_000;
|
|
@@ -23,6 +24,10 @@ const CURRENT_CHATGPT_PRO_ALIASES = new Set([
|
|
|
23
24
|
// The browser label is passed to the model picker which fuzzy-matches against ChatGPT's UI.
|
|
24
25
|
const BROWSER_MODEL_LABELS = [
|
|
25
26
|
// Most specific first (e.g., "gpt-5.2-thinking" before "gpt-5.2")
|
|
27
|
+
// GPT-6 (Astra) has no entry of its own in the ChatGPT picker: it is the "Latest" radio of the
|
|
28
|
+
// advanced view, and "GPT-6 Pro" is that radio with the power slider at Pro (composer pill "6 Pro").
|
|
29
|
+
["gpt-6-pro", "Latest"],
|
|
30
|
+
["gpt-6-astra", "Latest"],
|
|
26
31
|
["gpt-5.6-sol", "GPT-5.6 Sol"],
|
|
27
32
|
["gpt-5.6", "GPT-5.6 Sol"],
|
|
28
33
|
["gpt-5.5-pro", "GPT-5.5"],
|
|
@@ -46,6 +51,13 @@ const BROWSER_MODEL_LABELS = [
|
|
|
46
51
|
];
|
|
47
52
|
export function normalizeChatGptModelForBrowser(model) {
|
|
48
53
|
const normalized = model.toLowerCase();
|
|
54
|
+
// Browser-only alias: gpt-6-pro keeps its name so the Pro tier default survives (label "Latest").
|
|
55
|
+
if (isGpt6ProAlias(normalized)) {
|
|
56
|
+
return "gpt-6-pro";
|
|
57
|
+
}
|
|
58
|
+
if (isGpt6Alias(normalized)) {
|
|
59
|
+
return "gpt-6-astra";
|
|
60
|
+
}
|
|
49
61
|
if (!normalized.startsWith("gpt-") || normalized.includes("codex")) {
|
|
50
62
|
return model;
|
|
51
63
|
}
|
|
@@ -71,6 +83,18 @@ export function normalizeChatGptModelForBrowser(model) {
|
|
|
71
83
|
}
|
|
72
84
|
return model;
|
|
73
85
|
}
|
|
86
|
+
// Documented spellings only: gpt-6, gpt-6-astra, gpt-6-pro (plus their label forms such as
|
|
87
|
+
// "GPT-6 Pro") and "latest" map to ChatGPT's "Latest" model. Any other gpt-6-* id (gpt-6-codex,
|
|
88
|
+
// gpt-6-custom, ...) is not an alias and must pass through unchanged for custom/OpenRouter use.
|
|
89
|
+
const GPT6_ALIAS_PATTERN = /^gpt[-_ ]?6(?:[-_ ](?:astra|pro))?$/;
|
|
90
|
+
const GPT6_PRO_ALIAS_PATTERN = /^gpt[-_ ]?6[-_ ]pro$/;
|
|
91
|
+
export function isGpt6Alias(model) {
|
|
92
|
+
const normalized = model?.trim().toLowerCase() ?? "";
|
|
93
|
+
return normalized === "latest" || GPT6_ALIAS_PATTERN.test(normalized);
|
|
94
|
+
}
|
|
95
|
+
export function isGpt6ProAlias(model) {
|
|
96
|
+
return GPT6_PRO_ALIAS_PATTERN.test(model?.trim().toLowerCase() ?? "");
|
|
97
|
+
}
|
|
74
98
|
export function isCurrentChatGptProAlias(model) {
|
|
75
99
|
return CURRENT_CHATGPT_PRO_ALIASES.has(model?.trim().toLowerCase() ?? "");
|
|
76
100
|
}
|
|
@@ -79,7 +103,10 @@ export function resolveDefaultBrowserThinkingTime({ model, requestedModel, model
|
|
|
79
103
|
if (strategy !== "select")
|
|
80
104
|
return undefined;
|
|
81
105
|
const normalizedModel = normalizeChatGptModelForBrowser(model);
|
|
82
|
-
return isCurrentChatGptProAlias(requestedModel ?? model) ||
|
|
106
|
+
return isCurrentChatGptProAlias(requestedModel ?? model) ||
|
|
107
|
+
isGpt6ProAlias(requestedModel ?? model) ||
|
|
108
|
+
normalizedModel === "gpt-6-pro" ||
|
|
109
|
+
normalizedModel === "gpt-5.5-pro"
|
|
83
110
|
? "pro"
|
|
84
111
|
: undefined;
|
|
85
112
|
}
|
|
@@ -153,6 +180,7 @@ export async function buildBrowserConfig(options) {
|
|
|
153
180
|
inputTimeoutMs: options.browserInputTimeout
|
|
154
181
|
? parseBrowserDuration(options.browserInputTimeout, "--browser-input-timeout", DEFAULT_BROWSER_INPUT_TIMEOUT_MS)
|
|
155
182
|
: undefined,
|
|
183
|
+
approvalWaitMs: resolveBrowserApprovalWait(options.browserApprovalWait),
|
|
156
184
|
attachmentTimeoutMs: options.browserAttachmentTimeout
|
|
157
185
|
? parseBrowserDuration(options.browserAttachmentTimeout, "--browser-attachment-timeout", DEFAULT_BROWSER_ATTACHMENT_TIMEOUT_MS)
|
|
158
186
|
: undefined,
|
|
@@ -206,7 +234,9 @@ export async function buildBrowserConfig(options) {
|
|
|
206
234
|
remoteChrome,
|
|
207
235
|
browserTabRef: options.browserTab ?? undefined,
|
|
208
236
|
thinkingTime,
|
|
209
|
-
researchMode: options.browserResearch === "deep"
|
|
237
|
+
researchMode: options.browserResearch === "deep" || options.browserResearch === "search"
|
|
238
|
+
? options.browserResearch
|
|
239
|
+
: "off",
|
|
210
240
|
archiveConversations: options.browserArchive,
|
|
211
241
|
};
|
|
212
242
|
}
|
|
@@ -286,12 +316,14 @@ export function resolveBrowserModelLabel(input, model) {
|
|
|
286
316
|
return mapModelToBrowserLabel(model);
|
|
287
317
|
}
|
|
288
318
|
const normalizedInput = trimmed.toLowerCase();
|
|
289
|
-
if (normalizedInput === model.toLowerCase()
|
|
319
|
+
if (normalizedInput === model.toLowerCase() ||
|
|
320
|
+
(isGpt6Alias(normalizedInput) && (model === "gpt-6-astra" || model === "gpt-6-pro")) ||
|
|
321
|
+
(isGpt6ProAlias(normalizedInput) && model === "gpt-6-pro")) {
|
|
290
322
|
return mapModelToBrowserLabel(model);
|
|
291
323
|
}
|
|
292
324
|
return trimmed;
|
|
293
325
|
}
|
|
294
|
-
function parseRemoteChromeTarget(raw) {
|
|
326
|
+
export function parseRemoteChromeTarget(raw) {
|
|
295
327
|
const target = raw.trim();
|
|
296
328
|
if (!target) {
|
|
297
329
|
throw new Error("Invalid remote-chrome value: expected host:port but received an empty string.");
|
|
@@ -12,6 +12,13 @@ export function applyBrowserDefaultsFromConfig(options, config, getSource) {
|
|
|
12
12
|
const attachRunningRequested = options.browserAttachRunning === true ||
|
|
13
13
|
(isUnset("browserAttachRunning") && browser.attachRunning === true);
|
|
14
14
|
const currentModelRequestedByCli = options.browserModelStrategy === "current" && getSource("browserModelStrategy") === "cli";
|
|
15
|
+
if (!options.copyProfile &&
|
|
16
|
+
isUnset("remoteChrome") &&
|
|
17
|
+
options.remoteChrome === undefined &&
|
|
18
|
+
browser.remoteChrome) {
|
|
19
|
+
const { host, port } = browser.remoteChrome;
|
|
20
|
+
options.remoteChrome = `${host.includes(":") && !host.startsWith("[") ? `[${host}]` : host}:${port}`;
|
|
21
|
+
}
|
|
15
22
|
const configuredChatgptUrl = browser.chatgptUrl ?? browser.url;
|
|
16
23
|
const cliChatgptSet = options.chatgptUrl !== undefined || options.browserUrl !== undefined;
|
|
17
24
|
if (isUnset("chatgptUrl") && !cliChatgptSet && configuredChatgptUrl !== undefined) {
|
|
@@ -45,6 +52,9 @@ export function applyBrowserDefaultsFromConfig(options, config, getSource) {
|
|
|
45
52
|
if (isUnset("browserInputTimeout") && typeof browser.inputTimeoutMs === "number") {
|
|
46
53
|
options.browserInputTimeout = String(browser.inputTimeoutMs);
|
|
47
54
|
}
|
|
55
|
+
if (isUnset("browserApprovalWait") && typeof browser.approvalWaitMs === "number") {
|
|
56
|
+
options.browserApprovalWait = String(browser.approvalWaitMs);
|
|
57
|
+
}
|
|
48
58
|
if (isUnset("browserAttachmentTimeout") && typeof browser.attachmentTimeoutMs === "number") {
|
|
49
59
|
options.browserAttachmentTimeout = String(browser.attachmentTimeoutMs);
|
|
50
60
|
}
|
|
@@ -2,9 +2,11 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { sessionStore } from "../sessionStore.js";
|
|
5
|
-
import { collectChatGptTabs, DEFAULT_REMOTE_CHROME_HOST, DEFAULT_REMOTE_CHROME_PORT,
|
|
5
|
+
import { collectChatGptTabs, DEFAULT_REMOTE_CHROME_HOST, DEFAULT_REMOTE_CHROME_PORT, formatBrowserTabState, harvestChatGptTab, sessionMatchesTab, } from "../browser/liveTabs.js";
|
|
6
6
|
import { isRecoveredConversationHarvestReady, recoverConversationTab, } from "../browser/recoverConversation.js";
|
|
7
7
|
import { resolveOutputPath } from "./writeOutputPath.js";
|
|
8
|
+
import { persistBrowserHarvest } from "./harvestIntegrity.js";
|
|
9
|
+
import { completeOwnedBrowserHarvest } from "./recoveredBrowserHarvest.js";
|
|
8
10
|
const LIVE_POLL_MS = 2000;
|
|
9
11
|
const DEFAULT_STALL_THRESHOLD_MS = 60_000;
|
|
10
12
|
function isRecoverableMissingTabError(message) {
|
|
@@ -85,35 +87,17 @@ function resolveSessionTabRef(meta) {
|
|
|
85
87
|
export function resolveSessionTabRefForTest(meta) {
|
|
86
88
|
return resolveSessionTabRef(meta);
|
|
87
89
|
}
|
|
88
|
-
|
|
89
|
-
const hash = createHash("sha1")
|
|
90
|
-
.update(harvested.lastAssistantMarkdown ?? harvested.lastAssistantText ?? "")
|
|
91
|
-
.digest("hex");
|
|
92
|
-
const browser = {
|
|
93
|
-
...(meta.browser ?? {}),
|
|
94
|
-
harvest: {
|
|
95
|
-
targetId: harvested.targetId,
|
|
96
|
-
url: harvested.url,
|
|
97
|
-
conversationId: harvested.conversationId ?? extractConversationIdFromUrl(harvested.url),
|
|
98
|
-
harvestedAt: new Date().toISOString(),
|
|
99
|
-
assistantHash: hash,
|
|
100
|
-
state: harvested.state,
|
|
101
|
-
stopExists: harvested.stopExists,
|
|
102
|
-
sendExists: harvested.sendExists,
|
|
103
|
-
assistantCount: harvested.assistantCount,
|
|
104
|
-
currentModelLabel: harvested.currentModelLabel,
|
|
105
|
-
lastAssistantSnippet: harvested.lastAssistantSnippet,
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
await sessionStore.updateSession(sessionId, { browser });
|
|
109
|
-
}
|
|
110
|
-
function printHarvestSummary(sessionId, harvested) {
|
|
90
|
+
function printHarvestSummary(sessionId, harvested, integrity) {
|
|
111
91
|
console.log(chalk.bold(`Session: ${sessionId}`));
|
|
112
92
|
console.log(`Target: ${harvested.targetId}`);
|
|
113
93
|
console.log(`State: ${formatBrowserTabState(harvested)}`);
|
|
114
94
|
console.log(`Model: ${harvested.currentModelLabel || "(unknown)"}`);
|
|
115
95
|
console.log(`URL: ${harvested.url}`);
|
|
116
96
|
console.log(`Assistant turns: ${harvested.assistantCount}`);
|
|
97
|
+
console.log(`Capture identity: ${integrity.status}${integrity.explicitTarget ? " (explicit target)" : ""}`);
|
|
98
|
+
if (integrity.status === "mismatch") {
|
|
99
|
+
console.log(chalk.yellow("Explicit harvest target differs from the saved capture; original artifacts are unchanged."));
|
|
100
|
+
}
|
|
117
101
|
console.log(`Signals: stop=${harvested.stopExists ? "yes" : "no"} send=${harvested.sendExists ? "yes" : "no"}`);
|
|
118
102
|
if (harvested.lastUserSnippet) {
|
|
119
103
|
console.log(`Last user: ${harvested.lastUserSnippet}`);
|
|
@@ -207,8 +191,8 @@ export async function harvestSessionBrowserOutput(sessionId, options = {}) {
|
|
|
207
191
|
stallWindowMs: options.stallWindowMs,
|
|
208
192
|
});
|
|
209
193
|
}
|
|
210
|
-
await
|
|
211
|
-
printHarvestSummary(sessionId, harvested);
|
|
194
|
+
const integrity = await persistBrowserHarvest(sessionId, harvested, Boolean(options.browserTabRef));
|
|
195
|
+
printHarvestSummary(sessionId, harvested, integrity);
|
|
212
196
|
const output = harvested.lastAssistantMarkdown ?? harvested.lastAssistantText ?? "";
|
|
213
197
|
if (options.writeOutputPath) {
|
|
214
198
|
await maybeWriteHarvestOutput(options.writeOutputPath, meta.cwd ?? process.cwd(), output);
|
|
@@ -216,6 +200,7 @@ export async function harvestSessionBrowserOutput(sessionId, options = {}) {
|
|
|
216
200
|
if (!options.quietOutput && output) {
|
|
217
201
|
process.stdout.write(`${output}${output.endsWith("\n") ? "" : "\n"}`);
|
|
218
202
|
}
|
|
203
|
+
await completeOwnedBrowserHarvest(sessionId, harvested, integrity, (line) => console.log(line));
|
|
219
204
|
return harvested;
|
|
220
205
|
}
|
|
221
206
|
finally {
|
|
@@ -287,8 +272,8 @@ export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
|
|
|
287
272
|
const statusLine = `[${new Date().toISOString()}] state=${harvested.state} stop=${harvested.stopExists ? "yes" : "no"} ` +
|
|
288
273
|
`send=${harvested.sendExists ? "yes" : "no"} model=${harvested.currentModelLabel || "(unknown)"} ` +
|
|
289
274
|
`snippet=${snippet(harvested.lastAssistantSnippet || fullText, 160)}`;
|
|
275
|
+
await persistBrowserHarvest(sessionId, harvested, Boolean(options.browserTabRef));
|
|
290
276
|
console.log(statusLine);
|
|
291
|
-
await persistHarvest(sessionId, meta, harvested);
|
|
292
277
|
}
|
|
293
278
|
const derivedState = harvested.stopExists
|
|
294
279
|
? Date.now() - unchangedSince >= stallThresholdMs
|
|
@@ -304,8 +289,8 @@ export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
|
|
|
304
289
|
...harvested,
|
|
305
290
|
state: derivedState,
|
|
306
291
|
};
|
|
307
|
-
await
|
|
308
|
-
printHarvestSummary(sessionId, finalHarvest);
|
|
292
|
+
const integrity = await persistBrowserHarvest(sessionId, finalHarvest, Boolean(options.browserTabRef));
|
|
293
|
+
printHarvestSummary(sessionId, finalHarvest, integrity);
|
|
309
294
|
const output = finalHarvest.lastAssistantMarkdown ?? finalHarvest.lastAssistantText ?? "";
|
|
310
295
|
if (options.writeOutputPath) {
|
|
311
296
|
await maybeWriteHarvestOutput(options.writeOutputPath, meta.cwd ?? process.cwd(), output);
|
|
@@ -313,6 +298,7 @@ export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
|
|
|
313
298
|
if (output) {
|
|
314
299
|
process.stdout.write(`${output}${output.endsWith("\n") ? "" : "\n"}`);
|
|
315
300
|
}
|
|
301
|
+
await completeOwnedBrowserHarvest(sessionId, finalHarvest, integrity, (line) => console.log(line));
|
|
316
302
|
return finalHarvest;
|
|
317
303
|
}
|
|
318
304
|
await new Promise((resolve) => setTimeout(resolve, LIVE_POLL_MS));
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
export function resolveOracleCliEntrypoint(moduleUrl = import.meta.url) {
|
|
4
|
+
const extension = fileURLToPath(moduleUrl).endsWith(".ts") ? "ts" : "js";
|
|
5
|
+
return fileURLToPath(new URL(`../../bin/oracle-cli.${extension}`, moduleUrl));
|
|
6
|
+
}
|
|
7
|
+
export function buildDetachedSessionSpawnSpec({ sessionId, cliEntrypoint = resolveOracleCliEntrypoint(), env = process.env, nodeExecutable = process.execPath, }) {
|
|
8
|
+
return {
|
|
9
|
+
command: nodeExecutable,
|
|
10
|
+
args: ["--", cliEntrypoint, "--exec-session", sessionId],
|
|
11
|
+
options: {
|
|
12
|
+
detached: true,
|
|
13
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
14
|
+
env: {
|
|
15
|
+
...env,
|
|
16
|
+
ORACLE_DETACHED_START_GATE: "1",
|
|
17
|
+
},
|
|
18
|
+
windowsHide: true,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function launchDetachedSession({ sessionId, cliEntrypoint, env, nodeExecutable, prepare, spawnProcess = spawn, }) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
let child;
|
|
25
|
+
try {
|
|
26
|
+
const spec = buildDetachedSessionSpawnSpec({
|
|
27
|
+
sessionId,
|
|
28
|
+
cliEntrypoint,
|
|
29
|
+
env,
|
|
30
|
+
nodeExecutable,
|
|
31
|
+
});
|
|
32
|
+
child = spawnProcess(spec.command, spec.args, spec.options);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
reject(error);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
child.once("error", reject);
|
|
39
|
+
child.once("spawn", async () => {
|
|
40
|
+
if (child.pid === undefined) {
|
|
41
|
+
child.kill();
|
|
42
|
+
reject(new Error("Detached session worker started without a process ID."));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
await prepare(child.pid);
|
|
47
|
+
if (!child.stdin) {
|
|
48
|
+
throw new Error("Detached session worker started without a writable start gate.");
|
|
49
|
+
}
|
|
50
|
+
const startGate = child.stdin;
|
|
51
|
+
await new Promise((resolveGate, rejectGate) => {
|
|
52
|
+
startGate.once("error", rejectGate);
|
|
53
|
+
startGate.end("ready\n", (error) => {
|
|
54
|
+
if (error)
|
|
55
|
+
rejectGate(error);
|
|
56
|
+
else
|
|
57
|
+
resolveGate();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
child.unref();
|
|
61
|
+
resolve(child.pid);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
child.kill();
|
|
65
|
+
reject(error);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
package/dist/src/cli/dryRun.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { MODEL_CONFIGS, TOKENIZER_OPTIONS, DEFAULT_SYSTEM_PROMPT, buildPrompt, readFiles, getFileTokenStats, printFileTokenStats, } from "../oracle.js";
|
|
3
3
|
import { isKnownModel } from "../oracle/modelResolver.js";
|
|
4
|
-
import { assembleBrowserPrompt } from "../browser/prompt.js";
|
|
4
|
+
import { assembleBrowserPrompt, cleanupGeneratedBrowserBundles, } from "../browser/prompt.js";
|
|
5
5
|
import { buildTokenEstimateSuffix, formatAttachmentLabel } from "../browser/promptSummary.js";
|
|
6
6
|
import { buildCookiePlan } from "../browser/policies.js";
|
|
7
7
|
import { describeBrowserControlPlan, formatBrowserControlPlan } from "../browser/controlPlan.js";
|
|
@@ -45,19 +45,24 @@ async function runBrowserDryRun({ runOptions, cwd, version, log, browserConfig,
|
|
|
45
45
|
validateBrowserFollowUps(runOptions, browserConfig);
|
|
46
46
|
const assemblePromptImpl = deps.assembleBrowserPromptImpl ?? assembleBrowserPrompt;
|
|
47
47
|
const artifacts = await assemblePromptImpl(runOptions, { cwd });
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
try {
|
|
49
|
+
const suffix = buildTokenEstimateSuffix(artifacts);
|
|
50
|
+
const displayModel = formatBrowserModelTarget({
|
|
51
|
+
model: runOptions.model,
|
|
52
|
+
desiredModel: browserConfig?.desiredModel,
|
|
53
|
+
modelStrategy: browserConfig?.modelStrategy,
|
|
54
|
+
});
|
|
55
|
+
const headerLine = `[dry-run] Oracle (${version}) would launch browser mode (${displayModel}) with ~${artifacts.estimatedInputTokens.toLocaleString()} tokens${suffix}.`;
|
|
56
|
+
log(chalk.cyan(headerLine));
|
|
57
|
+
logBrowserControlPlan(browserConfig, log, "dry-run");
|
|
58
|
+
logBrowserFollowUpSummary(runOptions.browserFollowUps, log, "dry-run");
|
|
59
|
+
logBrowserCookieStrategy(browserConfig, log, "dry-run");
|
|
60
|
+
logBrowserArchivePolicy(browserConfig, log, "dry-run");
|
|
61
|
+
logBrowserFileSummary(artifacts, log, "dry-run");
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
await cleanupGeneratedBrowserBundles(artifacts);
|
|
65
|
+
}
|
|
61
66
|
}
|
|
62
67
|
function logBrowserControlPlan(browserConfig, log, label) {
|
|
63
68
|
const plan = describeBrowserControlPlan(browserConfig);
|
|
@@ -100,42 +105,47 @@ export async function runBrowserPreview({ runOptions, cwd, version, previewMode,
|
|
|
100
105
|
validateBrowserFollowUps(runOptions, browserConfig);
|
|
101
106
|
const assemblePromptImpl = deps.assembleBrowserPromptImpl ?? assembleBrowserPrompt;
|
|
102
107
|
const artifacts = await assemblePromptImpl(runOptions, { cwd });
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
desiredModel: browserConfig?.desiredModel,
|
|
107
|
-
modelStrategy: browserConfig?.modelStrategy,
|
|
108
|
-
});
|
|
109
|
-
const headerLine = `[preview] Oracle (${version}) browser mode (${displayModel}) with ~${artifacts.estimatedInputTokens.toLocaleString()} tokens${suffix}.`;
|
|
110
|
-
log(chalk.cyan(headerLine));
|
|
111
|
-
logBrowserControlPlan(browserConfig, log, "preview");
|
|
112
|
-
logBrowserFollowUpSummary(runOptions.browserFollowUps, log, "preview");
|
|
113
|
-
logBrowserCookieStrategy(browserConfig, log, "preview");
|
|
114
|
-
logBrowserFileSummary(artifacts, log, "preview");
|
|
115
|
-
if (previewMode === "json" || previewMode === "full") {
|
|
116
|
-
const attachmentSummary = artifacts.attachments.map((attachment) => ({
|
|
117
|
-
path: attachment.path,
|
|
118
|
-
displayPath: attachment.displayPath,
|
|
119
|
-
sizeBytes: attachment.sizeBytes,
|
|
120
|
-
}));
|
|
121
|
-
const previewPayload = {
|
|
108
|
+
try {
|
|
109
|
+
const suffix = buildTokenEstimateSuffix(artifacts);
|
|
110
|
+
const displayModel = formatBrowserModelTarget({
|
|
122
111
|
model: runOptions.model,
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
log
|
|
132
|
-
|
|
133
|
-
|
|
112
|
+
desiredModel: browserConfig?.desiredModel,
|
|
113
|
+
modelStrategy: browserConfig?.modelStrategy,
|
|
114
|
+
});
|
|
115
|
+
const headerLine = `[preview] Oracle (${version}) browser mode (${displayModel}) with ~${artifacts.estimatedInputTokens.toLocaleString()} tokens${suffix}.`;
|
|
116
|
+
log(chalk.cyan(headerLine));
|
|
117
|
+
logBrowserControlPlan(browserConfig, log, "preview");
|
|
118
|
+
logBrowserFollowUpSummary(runOptions.browserFollowUps, log, "preview");
|
|
119
|
+
logBrowserCookieStrategy(browserConfig, log, "preview");
|
|
120
|
+
logBrowserFileSummary(artifacts, log, "preview");
|
|
121
|
+
if (previewMode === "json" || previewMode === "full") {
|
|
122
|
+
const attachmentSummary = artifacts.attachments.map((attachment) => ({
|
|
123
|
+
path: attachment.path,
|
|
124
|
+
displayPath: attachment.displayPath,
|
|
125
|
+
sizeBytes: attachment.sizeBytes,
|
|
126
|
+
}));
|
|
127
|
+
const previewPayload = {
|
|
128
|
+
model: runOptions.model,
|
|
129
|
+
engine: "browser",
|
|
130
|
+
composerText: artifacts.composerText,
|
|
131
|
+
attachments: attachmentSummary,
|
|
132
|
+
inlineFileCount: artifacts.inlineFileCount,
|
|
133
|
+
bundled: artifacts.bundled,
|
|
134
|
+
tokenEstimate: artifacts.estimatedInputTokens,
|
|
135
|
+
browserFollowUps: runOptions.browserFollowUps ?? [],
|
|
136
|
+
};
|
|
137
|
+
log("");
|
|
138
|
+
log(chalk.bold("Preview JSON"));
|
|
139
|
+
log(JSON.stringify(previewPayload, null, 2));
|
|
140
|
+
}
|
|
141
|
+
if (previewMode === "full") {
|
|
142
|
+
log("");
|
|
143
|
+
log(chalk.bold("Composer Text"));
|
|
144
|
+
log(artifacts.composerText || chalk.dim("(empty prompt)"));
|
|
145
|
+
}
|
|
134
146
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
log(chalk.bold("Composer Text"));
|
|
138
|
-
log(artifacts.composerText || chalk.dim("(empty prompt)"));
|
|
147
|
+
finally {
|
|
148
|
+
await cleanupGeneratedBrowserBundles(artifacts);
|
|
139
149
|
}
|
|
140
150
|
}
|
|
141
151
|
function logBrowserFollowUpSummary(followUps, log, label) {
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { sessionStore } from "../sessionStore.js";
|
|
6
|
+
import { extractConversationIdFromUrl } from "../browser/liveTabs.js";
|
|
7
|
+
import { BrowserAutomationError } from "../oracle/errors.js";
|
|
8
|
+
const INTEGRITY_WARNING = "browser-harvest-integrity";
|
|
9
|
+
const INTEGRITY_MESSAGE = "Harvest or saved conversation identities conflict. Original artifacts were preserved; inspect browser.harvest.integrity before using the answer.";
|
|
10
|
+
async function readTranscriptConversation(filename) {
|
|
11
|
+
let file;
|
|
12
|
+
try {
|
|
13
|
+
const stat = await fs.lstat(filename);
|
|
14
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
15
|
+
return undefined;
|
|
16
|
+
file = await fs.open(filename, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
17
|
+
if (!(await file.stat()).isFile())
|
|
18
|
+
return undefined;
|
|
19
|
+
const buffer = Buffer.alloc(2_048);
|
|
20
|
+
const { bytesRead } = await file.read(buffer, 0, buffer.length, 0);
|
|
21
|
+
const match = buffer
|
|
22
|
+
.subarray(0, bytesRead)
|
|
23
|
+
.toString("utf8")
|
|
24
|
+
.match(/^# Oracle Browser Transcript\r?\n\r?\nConversation: ([^\r\n]+)\r?\n/);
|
|
25
|
+
return match ? extractConversationIdFromUrl(match[1]) : undefined;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
await file?.close();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export async function persistBrowserHarvest(sessionId, harvested, explicitTarget = false) {
|
|
35
|
+
// Harvesting can take time; compare against the latest saved capture, not the initial read.
|
|
36
|
+
const meta = await sessionStore.readSession(sessionId);
|
|
37
|
+
if (!meta)
|
|
38
|
+
throw new Error(`No session found with ID ${sessionId}.`);
|
|
39
|
+
const captured = [];
|
|
40
|
+
const unverifiedSources = [];
|
|
41
|
+
const add = (source, id) => {
|
|
42
|
+
if (id)
|
|
43
|
+
captured.push({ source, conversationId: id });
|
|
44
|
+
};
|
|
45
|
+
const addUrl = (source, url, expectsConversation = true) => {
|
|
46
|
+
if (!url)
|
|
47
|
+
return;
|
|
48
|
+
const id = extractConversationIdFromUrl(url);
|
|
49
|
+
add(source, id);
|
|
50
|
+
if (!id && expectsConversation)
|
|
51
|
+
unverifiedSources.push(source);
|
|
52
|
+
};
|
|
53
|
+
add("runtime", meta.browser?.runtime?.conversationId);
|
|
54
|
+
addUrl("runtime-url", meta.browser?.runtime?.tabUrl);
|
|
55
|
+
addUrl("archive", meta.browser?.archive?.conversationUrl);
|
|
56
|
+
for (const [index, artifact] of (meta.artifacts ?? []).entries()) {
|
|
57
|
+
addUrl(`artifact:${index}`, artifact.sourceUrl, artifact.kind === "transcript" || artifact.kind === "deep-research-report");
|
|
58
|
+
}
|
|
59
|
+
const artifactDir = path.join((await sessionStore.getPaths(sessionId)).dir, "artifacts");
|
|
60
|
+
const directoryStat = await fs.lstat(artifactDir).catch(() => null);
|
|
61
|
+
if (directoryStat?.isDirectory() && !directoryStat.isSymbolicLink()) {
|
|
62
|
+
const transcripts = new Map([[path.join(artifactDir, "transcript.md"), false]]);
|
|
63
|
+
for (const artifact of meta.artifacts ?? []) {
|
|
64
|
+
if (artifact.kind !== "transcript")
|
|
65
|
+
continue;
|
|
66
|
+
const candidate = path.isAbsolute(artifact.path)
|
|
67
|
+
? artifact.path
|
|
68
|
+
: path.resolve(artifactDir, "..", artifact.path);
|
|
69
|
+
// Stored paths must remain direct children of this session's artifact directory.
|
|
70
|
+
if (path.dirname(candidate) === artifactDir)
|
|
71
|
+
transcripts.set(candidate, true);
|
|
72
|
+
else
|
|
73
|
+
unverifiedSources.push("external-transcript-path");
|
|
74
|
+
}
|
|
75
|
+
for (const [filename, expected] of transcripts) {
|
|
76
|
+
const source = `transcript:${path.basename(filename)}`;
|
|
77
|
+
const id = await readTranscriptConversation(filename);
|
|
78
|
+
add(source, id);
|
|
79
|
+
if (!id && (expected || (await fs.lstat(filename).catch(() => null)))) {
|
|
80
|
+
unverifiedSources.push(source);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (directoryStat || meta.artifacts?.some((artifact) => artifact.kind === "transcript")) {
|
|
85
|
+
unverifiedSources.push("artifact-directory");
|
|
86
|
+
}
|
|
87
|
+
const observedConversationId = harvested.conversationId ?? extractConversationIdFromUrl(harvested.url);
|
|
88
|
+
const capturedIds = new Set(captured.map((entry) => entry.conversationId));
|
|
89
|
+
const previousIntegrity = meta.browser?.harvest?.integrity;
|
|
90
|
+
const mismatch = capturedIds.size > 1 ||
|
|
91
|
+
(observedConversationId
|
|
92
|
+
? captured.some((entry) => entry.conversationId !== observedConversationId)
|
|
93
|
+
: previousIntegrity?.status === "mismatch");
|
|
94
|
+
const integrity = {
|
|
95
|
+
status: mismatch
|
|
96
|
+
? "mismatch"
|
|
97
|
+
: observedConversationId && captured.length && unverifiedSources.length === 0
|
|
98
|
+
? "matched"
|
|
99
|
+
: "unverified",
|
|
100
|
+
observedConversationId,
|
|
101
|
+
captured,
|
|
102
|
+
unverifiedSources,
|
|
103
|
+
explicitTarget,
|
|
104
|
+
previousHarvestConversationId: meta.browser?.harvest?.conversationId ?? previousIntegrity?.previousHarvestConversationId,
|
|
105
|
+
};
|
|
106
|
+
const warnings = (meta.browser?.warnings ?? []).filter((warning) => warning.code !== INTEGRITY_WARNING);
|
|
107
|
+
if (mismatch) {
|
|
108
|
+
warnings.push({ code: INTEGRITY_WARNING, severity: "warning", message: INTEGRITY_MESSAGE });
|
|
109
|
+
}
|
|
110
|
+
await sessionStore.updateSession(sessionId, {
|
|
111
|
+
browser: {
|
|
112
|
+
...meta.browser,
|
|
113
|
+
warnings,
|
|
114
|
+
harvest: {
|
|
115
|
+
targetId: harvested.targetId,
|
|
116
|
+
url: harvested.url,
|
|
117
|
+
conversationId: observedConversationId,
|
|
118
|
+
harvestedAt: new Date().toISOString(),
|
|
119
|
+
assistantHash: createHash("sha1")
|
|
120
|
+
.update(harvested.lastAssistantMarkdown ?? harvested.lastAssistantText ?? "")
|
|
121
|
+
.digest("hex"),
|
|
122
|
+
state: harvested.state,
|
|
123
|
+
stopExists: harvested.stopExists,
|
|
124
|
+
sendExists: harvested.sendExists,
|
|
125
|
+
assistantCount: harvested.assistantCount,
|
|
126
|
+
currentModelLabel: harvested.currentModelLabel,
|
|
127
|
+
lastAssistantSnippet: harvested.lastAssistantSnippet,
|
|
128
|
+
integrity,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
if (mismatch && !explicitTarget) {
|
|
133
|
+
throw new BrowserAutomationError(INTEGRITY_MESSAGE, {
|
|
134
|
+
stage: "harvest-integrity",
|
|
135
|
+
code: "conversation-identity-mismatch",
|
|
136
|
+
integrity,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return integrity;
|
|
140
|
+
}
|
package/dist/src/cli/options.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isGpt6Alias, isGpt6ProAlias } from "./browserConfig.js";
|
|
1
2
|
import { InvalidArgumentError } from "commander";
|
|
2
3
|
import { parseDuration } from "../duration.js";
|
|
3
4
|
import path from "node:path";
|
|
@@ -187,6 +188,12 @@ export function resolveApiModel(modelValue) {
|
|
|
187
188
|
if (normalized.includes("/")) {
|
|
188
189
|
return normalized;
|
|
189
190
|
}
|
|
191
|
+
if (isGpt6ProAlias(normalized)) {
|
|
192
|
+
throw new InvalidArgumentError("GPT-6 Pro is an API reasoning mode, not a model slug. Use --model gpt-6-astra --reasoning-mode pro (or --engine browser --model gpt-6-pro).");
|
|
193
|
+
}
|
|
194
|
+
if (isGpt6Alias(normalized)) {
|
|
195
|
+
return "gpt-6-astra";
|
|
196
|
+
}
|
|
190
197
|
const gpt56Label = parseBrowserGpt56Label(normalized);
|
|
191
198
|
if (gpt56Label?.variant.split(" ").includes("pro")) {
|
|
192
199
|
throw new InvalidArgumentError("GPT-5.6 Pro is an API reasoning mode, not a model slug. Use --model gpt-5.6-sol --reasoning-mode pro.");
|
|
@@ -284,6 +291,14 @@ export function inferModelFromLabel(modelValue) {
|
|
|
284
291
|
if (normalized.includes("/")) {
|
|
285
292
|
return normalized;
|
|
286
293
|
}
|
|
294
|
+
// gpt-6 / gpt-6-pro / latest: ChatGPT's "Latest" model (GPT-6 Astra). The browser-only -pro alias
|
|
295
|
+
// is passed through so its Pro tier default survives (resolveDefaultBrowserThinkingTime).
|
|
296
|
+
if (isGpt6ProAlias(normalized)) {
|
|
297
|
+
return "gpt-6-pro";
|
|
298
|
+
}
|
|
299
|
+
if (isGpt6Alias(normalized)) {
|
|
300
|
+
return "gpt-6-astra";
|
|
301
|
+
}
|
|
287
302
|
if (normalized.includes("grok")) {
|
|
288
303
|
return "grok-4.1";
|
|
289
304
|
}
|