@steipete/oracle 0.16.1 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -337
- package/dist/bin/oracle-cli.js +185 -56
- package/dist/docs-site/.nojekyll +0 -0
- package/dist/docs-site/CNAME +1 -0
- package/dist/docs-site/RELEASING.html +410 -0
- package/dist/docs-site/agents.html +374 -0
- package/dist/docs-site/anthropic.html +368 -0
- package/dist/docs-site/bridge.html +416 -0
- package/dist/docs-site/browser-mode.html +594 -0
- package/dist/docs-site/chromium-forks.html +347 -0
- package/dist/docs-site/cli-reference.html +346 -0
- package/dist/docs-site/configuration.html +462 -0
- package/dist/docs-site/favicon.svg +14 -0
- package/dist/docs-site/followup.html +375 -0
- package/dist/docs-site/gemini.html +383 -0
- package/dist/docs-site/grok.html +325 -0
- package/dist/docs-site/index.html +360 -0
- package/dist/docs-site/install.html +335 -0
- package/dist/docs-site/linux.html +321 -0
- package/dist/docs-site/llms.txt +43 -0
- package/dist/docs-site/manual-tests.html +596 -0
- package/dist/docs-site/mcp.html +391 -0
- package/dist/docs-site/multimodel.html +364 -0
- package/dist/docs-site/mythical-pro-agents.html +360 -0
- package/dist/docs-site/notifier.html +338 -0
- package/dist/docs-site/openai-endpoints.html +410 -0
- package/dist/docs-site/openrouter.html +344 -0
- package/dist/docs-site/quickstart.html +369 -0
- package/dist/docs-site/refactor/ux.html +532 -0
- package/dist/docs-site/sessions.html +389 -0
- package/dist/docs-site/social-card.png +0 -0
- package/dist/docs-site/social-card.svg +79 -0
- package/dist/docs-site/spec.html +363 -0
- package/dist/docs-site/testing.html +320 -0
- package/dist/docs-site/tui-debug.html +326 -0
- package/dist/docs-site/windows-work.html +324 -0
- package/dist/docs-site/windows.html +320 -0
- package/dist/scripts/test-browser.js +1 -25
- package/dist/src/browser/actions/modelSelection.js +36 -34
- package/dist/src/browser/actions/navigation.js +8 -3
- package/dist/src/browser/actions/thinkingTime.js +96 -45
- package/dist/src/browser/chromeLifecycle.js +8 -37
- package/dist/src/browser/index.js +23 -8
- package/dist/src/browser/modelDisplay.js +67 -0
- package/dist/src/browser/reattach.js +14 -1
- package/dist/src/browser/recoverConversation.js +2 -1
- package/dist/src/browser/sessionRunner.js +9 -10
- package/dist/src/browser/wslHost.js +50 -0
- package/dist/src/cli/browserConfig.js +31 -11
- package/dist/src/cli/detach.js +21 -4
- package/dist/src/cli/dryRun.js +13 -2
- package/dist/src/cli/engine.js +2 -2
- package/dist/src/cli/options.js +5 -1
- package/dist/src/cli/sessionDisplay.js +60 -8
- package/dist/src/cli/sessionLifecycle.js +2 -1
- package/dist/src/cli/sessionRunner.js +110 -60
- package/dist/src/cli/sessionTable.js +5 -1
- package/dist/src/cli/tui/index.js +12 -4
- package/dist/src/duration.js +3 -0
- package/dist/src/gemini-web/client.js +19 -1
- package/dist/src/oracle/modelResolver.js +8 -1
- package/dist/src/oracle/request.js +9 -2
- package/dist/src/oracle/run.js +43 -3
- package/dist/src/oracle/thinkingTime.js +9 -3
- package/dist/src/sessionManager.js +41 -12
- 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 +17 -17
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
3
4
|
import { normalizeThinkingTimeLevel } from "../oracle/thinkingTime.js";
|
|
4
5
|
import { CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET } from "../browser/constants.js";
|
|
5
6
|
import { normalizeChatgptUrl } from "../browser/utils.js";
|
|
@@ -86,6 +87,7 @@ export async function buildBrowserConfig(options) {
|
|
|
86
87
|
const isChatGptModel = baseModel.startsWith("gpt-") && !baseModel.includes("codex");
|
|
87
88
|
const shouldUseOverride = !isChatGptModel && normalizedOverride.length > 0 && normalizedOverride !== baseModel;
|
|
88
89
|
const modelStrategy = normalizeBrowserModelStrategy(options.browserModelStrategy) ?? DEFAULT_MODEL_STRATEGY;
|
|
90
|
+
assertBrowserModelAvailable(options.model, modelStrategy);
|
|
89
91
|
const cookieNames = parseCookieNames(options.browserCookieNames ?? process.env.ORACLE_BROWSER_COOKIE_NAMES);
|
|
90
92
|
let inline = await resolveInlineCookies({
|
|
91
93
|
inlineArg: options.browserInlineCookies,
|
|
@@ -123,38 +125,38 @@ export async function buildBrowserConfig(options) {
|
|
|
123
125
|
url,
|
|
124
126
|
debugPort: selectBrowserPort(options),
|
|
125
127
|
timeoutMs: options.browserTimeout
|
|
126
|
-
?
|
|
128
|
+
? parseBrowserDuration(options.browserTimeout, "--browser-timeout", DEFAULT_BROWSER_TIMEOUT_MS)
|
|
127
129
|
: undefined,
|
|
128
130
|
inputTimeoutMs: options.browserInputTimeout
|
|
129
|
-
?
|
|
131
|
+
? parseBrowserDuration(options.browserInputTimeout, "--browser-input-timeout", DEFAULT_BROWSER_INPUT_TIMEOUT_MS)
|
|
130
132
|
: undefined,
|
|
131
133
|
attachmentTimeoutMs: options.browserAttachmentTimeout
|
|
132
|
-
?
|
|
134
|
+
? parseBrowserDuration(options.browserAttachmentTimeout, "--browser-attachment-timeout", DEFAULT_BROWSER_ATTACHMENT_TIMEOUT_MS)
|
|
133
135
|
: undefined,
|
|
134
136
|
assistantRecheckDelayMs: options.browserRecheckDelay
|
|
135
|
-
?
|
|
137
|
+
? parseBrowserDuration(options.browserRecheckDelay, "--browser-recheck-delay", 0)
|
|
136
138
|
: undefined,
|
|
137
139
|
assistantRecheckTimeoutMs: options.browserRecheckTimeout
|
|
138
|
-
?
|
|
140
|
+
? parseBrowserDuration(options.browserRecheckTimeout, "--browser-recheck-timeout", DEFAULT_BROWSER_RECHECK_TIMEOUT_MS)
|
|
139
141
|
: undefined,
|
|
140
142
|
reuseChromeWaitMs: options.browserReuseWait
|
|
141
|
-
?
|
|
143
|
+
? parseBrowserDuration(options.browserReuseWait, "--browser-reuse-wait", 0)
|
|
142
144
|
: undefined,
|
|
143
145
|
profileLockTimeoutMs: options.browserProfileLockTimeout
|
|
144
|
-
?
|
|
146
|
+
? parseBrowserDuration(options.browserProfileLockTimeout, "--browser-profile-lock-timeout", 0)
|
|
145
147
|
: undefined,
|
|
146
148
|
maxConcurrentTabs: parseMaxConcurrentTabs(options.browserMaxConcurrentTabs),
|
|
147
149
|
autoReattachDelayMs: options.browserAutoReattachDelay
|
|
148
|
-
?
|
|
150
|
+
? parseBrowserDuration(options.browserAutoReattachDelay, "--browser-auto-reattach-delay", 0)
|
|
149
151
|
: undefined,
|
|
150
152
|
autoReattachIntervalMs: options.browserAutoReattachInterval
|
|
151
|
-
?
|
|
153
|
+
? parseBrowserDuration(options.browserAutoReattachInterval, "--browser-auto-reattach-interval", 0)
|
|
152
154
|
: undefined,
|
|
153
155
|
autoReattachTimeoutMs: options.browserAutoReattachTimeout
|
|
154
|
-
?
|
|
156
|
+
? parseBrowserDuration(options.browserAutoReattachTimeout, "--browser-auto-reattach-timeout", DEFAULT_BROWSER_AUTO_REATTACH_TIMEOUT_MS)
|
|
155
157
|
: undefined,
|
|
156
158
|
cookieSyncWaitMs: options.browserCookieWait
|
|
157
|
-
?
|
|
159
|
+
? parseBrowserDuration(options.browserCookieWait, "--browser-cookie-wait", 0)
|
|
158
160
|
: undefined,
|
|
159
161
|
cookieSync: options.browserNoCookieSync ? false : undefined,
|
|
160
162
|
cookieNames,
|
|
@@ -178,6 +180,17 @@ export async function buildBrowserConfig(options) {
|
|
|
178
180
|
archiveConversations: options.browserArchive,
|
|
179
181
|
};
|
|
180
182
|
}
|
|
183
|
+
function assertBrowserModelAvailable(model, modelStrategy) {
|
|
184
|
+
if (modelStrategy !== "select")
|
|
185
|
+
return;
|
|
186
|
+
const normalized = normalizeChatGptModelForBrowser(model);
|
|
187
|
+
if (normalized !== "gpt-5.2" &&
|
|
188
|
+
normalized !== "gpt-5.2-instant" &&
|
|
189
|
+
normalized !== "gpt-5.2-thinking") {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
throw new Error(`Browser model "${model}" is retired because ChatGPT no longer offers GPT-5.2 base, Instant, or Thinking. Choose a current GPT-5.5/GPT-5.6 browser model, use --browser-model-strategy current to keep ChatGPT's active model, or use --engine api to retain the GPT-5.2 API alias.`);
|
|
193
|
+
}
|
|
181
194
|
function validateAttachRunningOptions(options, { attachRunning, hasInlineCookies, }) {
|
|
182
195
|
if (!attachRunning) {
|
|
183
196
|
return;
|
|
@@ -218,6 +231,13 @@ function parseMaxConcurrentTabs(raw) {
|
|
|
218
231
|
}
|
|
219
232
|
return Math.trunc(value);
|
|
220
233
|
}
|
|
234
|
+
function parseBrowserDuration(raw, optionName, fallbackMs) {
|
|
235
|
+
const parsed = parseDuration(raw, Number.NaN);
|
|
236
|
+
if (Number.isFinite(parsed))
|
|
237
|
+
return parsed;
|
|
238
|
+
console.log(chalk.yellow(`Warning: invalid ${optionName} duration "${raw}"; using fallback ${fallbackMs}ms.`));
|
|
239
|
+
return fallbackMs;
|
|
240
|
+
}
|
|
221
241
|
export function mapModelToBrowserLabel(model) {
|
|
222
242
|
const normalized = normalizeChatGptModelForBrowser(model);
|
|
223
243
|
// Iterate ordered array to find first match (most specific first)
|
package/dist/src/cli/detach.js
CHANGED
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
import { isProModel } from "../oracle/modelResolver.js";
|
|
2
2
|
export function shouldDetachSession({
|
|
3
3
|
// Params kept for policy tweaks.
|
|
4
|
-
engine, model, waitPreference, disableDetachEnv, }) {
|
|
4
|
+
engine, model, reasoningMode, waitPreference, disableDetachEnv, }) {
|
|
5
5
|
if (disableDetachEnv)
|
|
6
6
|
return false;
|
|
7
|
-
//
|
|
7
|
+
// Keep long local browser Pro work in a separate process even while the CLI
|
|
8
|
+
// stays attached to its session log. If the foreground stream is interrupted,
|
|
9
|
+
// the worker can still finish the browser run and persist the answer.
|
|
10
|
+
if (engine === "browser" && isProModel(model))
|
|
11
|
+
return true;
|
|
12
|
+
// For API runs, explicit --wait keeps execution in the foreground.
|
|
8
13
|
if (waitPreference)
|
|
9
14
|
return false;
|
|
10
|
-
//
|
|
11
|
-
if (isProModel(model) && engine === "api")
|
|
15
|
+
// Pro-tier API runs start detached by default.
|
|
16
|
+
if ((isProModel(model) || reasoningMode === "pro") && engine === "api")
|
|
12
17
|
return true;
|
|
13
18
|
return false;
|
|
14
19
|
}
|
|
20
|
+
export function stopDetachedWorker(workerPid, kill = process.kill) {
|
|
21
|
+
try {
|
|
22
|
+
kill(workerPid, "SIGTERM");
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ESRCH") {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
package/dist/src/cli/dryRun.js
CHANGED
|
@@ -5,6 +5,7 @@ import { assembleBrowserPrompt } 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";
|
|
8
|
+
import { formatBrowserModelTarget } from "../browser/modelDisplay.js";
|
|
8
9
|
export async function runDryRunSummary({ engine, runOptions, cwd, version, log, browserConfig, }, deps = {}) {
|
|
9
10
|
if (engine === "browser") {
|
|
10
11
|
await runBrowserDryRun({ runOptions, cwd, version, log, browserConfig }, deps);
|
|
@@ -45,7 +46,12 @@ async function runBrowserDryRun({ runOptions, cwd, version, log, browserConfig,
|
|
|
45
46
|
const assemblePromptImpl = deps.assembleBrowserPromptImpl ?? assembleBrowserPrompt;
|
|
46
47
|
const artifacts = await assemblePromptImpl(runOptions, { cwd });
|
|
47
48
|
const suffix = buildTokenEstimateSuffix(artifacts);
|
|
48
|
-
const
|
|
49
|
+
const displayModel = formatBrowserModelTarget({
|
|
50
|
+
model: runOptions.model,
|
|
51
|
+
desiredModel: browserConfig?.desiredModel,
|
|
52
|
+
modelStrategy: browserConfig?.modelStrategy,
|
|
53
|
+
});
|
|
54
|
+
const headerLine = `[dry-run] Oracle (${version}) would launch browser mode (${displayModel}) with ~${artifacts.estimatedInputTokens.toLocaleString()} tokens${suffix}.`;
|
|
49
55
|
log(chalk.cyan(headerLine));
|
|
50
56
|
logBrowserControlPlan(browserConfig, log, "dry-run");
|
|
51
57
|
logBrowserFollowUpSummary(runOptions.browserFollowUps, log, "dry-run");
|
|
@@ -95,7 +101,12 @@ export async function runBrowserPreview({ runOptions, cwd, version, previewMode,
|
|
|
95
101
|
const assemblePromptImpl = deps.assembleBrowserPromptImpl ?? assembleBrowserPrompt;
|
|
96
102
|
const artifacts = await assemblePromptImpl(runOptions, { cwd });
|
|
97
103
|
const suffix = buildTokenEstimateSuffix(artifacts);
|
|
98
|
-
const
|
|
104
|
+
const displayModel = formatBrowserModelTarget({
|
|
105
|
+
model: runOptions.model,
|
|
106
|
+
desiredModel: browserConfig?.desiredModel,
|
|
107
|
+
modelStrategy: browserConfig?.modelStrategy,
|
|
108
|
+
});
|
|
109
|
+
const headerLine = `[preview] Oracle (${version}) browser mode (${displayModel}) with ~${artifacts.estimatedInputTokens.toLocaleString()} tokens${suffix}.`;
|
|
99
110
|
log(chalk.cyan(headerLine));
|
|
100
111
|
logBrowserControlPlan(browserConfig, log, "preview");
|
|
101
112
|
logBrowserFollowUpSummary(runOptions.browserFollowUps, log, "preview");
|
package/dist/src/cli/engine.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isProModel } from "../oracle/modelResolver.js";
|
|
2
|
-
export function defaultWaitPreference(model, engine) {
|
|
2
|
+
export function defaultWaitPreference(model, engine, reasoningMode) {
|
|
3
3
|
// Pro-class API runs can take a long time; prefer non-blocking unless explicitly overridden.
|
|
4
|
-
if (engine === "api" && isProModel(model)) {
|
|
4
|
+
if (engine === "api" && (isProModel(model) || reasoningMode === "pro")) {
|
|
5
5
|
return false;
|
|
6
6
|
}
|
|
7
7
|
return true; // browser or non-pro models are fast enough to block by default
|
package/dist/src/cli/options.js
CHANGED
|
@@ -133,7 +133,7 @@ export function parseThinkingTimeOption(value) {
|
|
|
133
133
|
if (normalized) {
|
|
134
134
|
return normalized;
|
|
135
135
|
}
|
|
136
|
-
throw new InvalidArgumentError('Thinking time must be one of "light", "standard", "extended", "heavy", or a ChatGPT UI alias like "instant", "medium", "high", or "
|
|
136
|
+
throw new InvalidArgumentError('Thinking time must be one of "light", "standard", "extended", "extra-high", "heavy", or a ChatGPT UI alias like "instant", "medium", "high", or "xhigh".');
|
|
137
137
|
}
|
|
138
138
|
export function normalizeModelOption(value) {
|
|
139
139
|
return (value ?? "").trim();
|
|
@@ -187,6 +187,10 @@ export function resolveApiModel(modelValue) {
|
|
|
187
187
|
if (normalized.includes("/")) {
|
|
188
188
|
return normalized;
|
|
189
189
|
}
|
|
190
|
+
const gpt56Label = parseBrowserGpt56Label(normalized);
|
|
191
|
+
if (gpt56Label?.variant.split(" ").includes("pro")) {
|
|
192
|
+
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.");
|
|
193
|
+
}
|
|
190
194
|
if (normalized.includes("grok")) {
|
|
191
195
|
return "grok-4.1";
|
|
192
196
|
}
|
|
@@ -12,6 +12,7 @@ import { estimateTokenCount } from "../browser/utils.js";
|
|
|
12
12
|
import { formatSessionTableHeader, formatSessionTableRow, resolveSessionCost, } from "./sessionTable.js";
|
|
13
13
|
import { abbreviateResponseId, buildResponseOwnerIndex, resolveSessionLineage, } from "./sessionLineage.js";
|
|
14
14
|
import { formatSessionExecutionLabel } from "./sessionLifecycle.js";
|
|
15
|
+
import { formatBrowserModelSelectionEvidence, formatSessionBrowserModelWithRequestedKey, resolveSessionBrowserModelDisplayName, } from "../browser/modelDisplay.js";
|
|
15
16
|
const isTty = () => Boolean(process.stdout.isTTY);
|
|
16
17
|
const dim = (text) => (isTty() ? kleur.dim(text) : text);
|
|
17
18
|
export const MAX_RENDER_BYTES = 200_000;
|
|
@@ -185,6 +186,7 @@ export async function attachSession(sessionId, options) {
|
|
|
185
186
|
const isVerbose = Boolean(process.env.ORACLE_VERBOSE_RENDER);
|
|
186
187
|
const runtime = metadata.browser?.runtime;
|
|
187
188
|
const controllerAlive = isProcessAlive(runtime?.controllerPid);
|
|
189
|
+
const workerAlive = isProcessAlive(metadata.lifecycle?.workerPid);
|
|
188
190
|
const hasChromeDisconnect = metadata.response?.incompleteReason === "chrome-disconnected";
|
|
189
191
|
const hasIncompleteCapture = metadata.response?.incompleteReason === "incomplete-capture";
|
|
190
192
|
const statusAllowsReattach = metadata.status === "running" ||
|
|
@@ -204,6 +206,7 @@ export async function attachSession(sessionId, options) {
|
|
|
204
206
|
const canReattach = (statusAllowsReattach || completedDeepResearchPlaceholder) &&
|
|
205
207
|
metadata.mode === "browser" &&
|
|
206
208
|
hasFallbackSessionInfo &&
|
|
209
|
+
!workerAlive &&
|
|
207
210
|
(hasRecoverableConversation ||
|
|
208
211
|
runtime?.promptSubmitted ||
|
|
209
212
|
hasLiveChromeFallback ||
|
|
@@ -311,11 +314,17 @@ export async function attachSession(sessionId, options) {
|
|
|
311
314
|
const usage = run.usage
|
|
312
315
|
? ` tok=${formatTokenCount(run.usage.outputTokens ?? 0)}/${formatTokenCount(run.usage.totalTokens ?? 0)}`
|
|
313
316
|
: "";
|
|
314
|
-
|
|
317
|
+
const modelLabel = (metadata.mode ?? metadata.options?.mode) === "browser"
|
|
318
|
+
? formatSessionBrowserModelWithRequestedKey(metadata, run.model)
|
|
319
|
+
: run.model;
|
|
320
|
+
console.log(`- ${chalk.cyan(modelLabel)} — ${run.status}${usage}`);
|
|
315
321
|
}
|
|
316
322
|
}
|
|
317
323
|
else if (metadata.model) {
|
|
318
|
-
|
|
324
|
+
const modelLabel = (metadata.mode ?? metadata.options?.mode) === "browser"
|
|
325
|
+
? formatSessionBrowserModelWithRequestedKey(metadata)
|
|
326
|
+
: metadata.model;
|
|
327
|
+
console.log(`Model: ${modelLabel}`);
|
|
319
328
|
}
|
|
320
329
|
const browserEvidence = formatBrowserEvidence(metadata);
|
|
321
330
|
if (browserEvidence) {
|
|
@@ -391,6 +400,9 @@ export async function attachSession(sessionId, options) {
|
|
|
391
400
|
if (summary) {
|
|
392
401
|
console.log(`\n${chalk.green.bold(summary)}`);
|
|
393
402
|
}
|
|
403
|
+
if (options?.propagateFailure && metadata.status === "error") {
|
|
404
|
+
process.exitCode = 1;
|
|
405
|
+
}
|
|
394
406
|
return;
|
|
395
407
|
}
|
|
396
408
|
if (wantsRender) {
|
|
@@ -493,6 +505,48 @@ export async function attachSession(sessionId, options) {
|
|
|
493
505
|
}
|
|
494
506
|
}
|
|
495
507
|
}
|
|
508
|
+
if (options?.propagateFailure && latest.status === "error") {
|
|
509
|
+
process.exitCode = 1;
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
const controllerPid = latest.lifecycle?.workerPid ?? latest.browser?.runtime?.controllerPid;
|
|
514
|
+
if (latest.lifecycle?.detached && controllerPid && !isProcessAlive(controllerPid)) {
|
|
515
|
+
const settled = await sessionStore.readSession(sessionId);
|
|
516
|
+
if (!settled) {
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
if (settled.status === "completed" || settled.status === "partial") {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
await printNew();
|
|
523
|
+
flushRemainder();
|
|
524
|
+
const message = settled.status === "error"
|
|
525
|
+
? (settled.errorMessage ?? "Detached worker failed.")
|
|
526
|
+
: "Detached worker exited before the session reached a terminal state.";
|
|
527
|
+
const failure = {
|
|
528
|
+
category: "internal",
|
|
529
|
+
message,
|
|
530
|
+
};
|
|
531
|
+
if (settled.model) {
|
|
532
|
+
await sessionStore.updateModelRun(settled.id, settled.model, {
|
|
533
|
+
status: "error",
|
|
534
|
+
completedAt: new Date().toISOString(),
|
|
535
|
+
response: { status: "incomplete", incompleteReason: "incomplete-capture" },
|
|
536
|
+
error: failure,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
await sessionStore.updateSession(settled.id, {
|
|
540
|
+
status: "error",
|
|
541
|
+
completedAt: new Date().toISOString(),
|
|
542
|
+
errorMessage: message,
|
|
543
|
+
response: { status: "incomplete", incompleteReason: "incomplete-capture" },
|
|
544
|
+
error: failure,
|
|
545
|
+
});
|
|
546
|
+
console.log(chalk.yellow(`${message} Reattach via: ${settled.lifecycle?.reattachCommand}`));
|
|
547
|
+
if (options?.propagateFailure) {
|
|
548
|
+
process.exitCode = 1;
|
|
549
|
+
}
|
|
496
550
|
break;
|
|
497
551
|
}
|
|
498
552
|
await wait(1000);
|
|
@@ -555,11 +609,7 @@ export function formatBrowserEvidence(metadata) {
|
|
|
555
609
|
const lines = [];
|
|
556
610
|
const evidence = browser.modelSelection;
|
|
557
611
|
if (evidence) {
|
|
558
|
-
|
|
559
|
-
const resolved = evidence.resolvedLabel ?? "(unavailable)";
|
|
560
|
-
const strategy = evidence.strategy ?? "(default)";
|
|
561
|
-
const verified = evidence.verified ? "yes" : "no";
|
|
562
|
-
lines.push(`model requested=${requested}; resolved=${resolved}; status=${evidence.status}; strategy=${strategy}; verified=${verified}`);
|
|
612
|
+
lines.push(`model ${formatBrowserModelSelectionEvidence(evidence, metadata.model)}`);
|
|
563
613
|
}
|
|
564
614
|
for (const warning of browser.warnings ?? []) {
|
|
565
615
|
lines.push(`warning ${warning.code}: ${warning.message}`);
|
|
@@ -821,7 +871,9 @@ export function formatCompletionSummary(metadata, options = {}) {
|
|
|
821
871
|
if (!metadata.usage || metadata.elapsedMs == null) {
|
|
822
872
|
return null;
|
|
823
873
|
}
|
|
824
|
-
const modeLabel = metadata.mode
|
|
874
|
+
const modeLabel = (metadata.mode ?? metadata.options?.mode) === "browser"
|
|
875
|
+
? `${resolveSessionBrowserModelDisplayName(metadata)}[browser]`
|
|
876
|
+
: (metadata.model ?? "n/a");
|
|
825
877
|
const usage = metadata.usage;
|
|
826
878
|
const cost = resolveSessionCost(metadata);
|
|
827
879
|
const tokensDisplay = [
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export function buildSessionLifecycle({ engine, detached, reattachCommand, }) {
|
|
1
|
+
export function buildSessionLifecycle({ engine, detached, workerPid, reattachCommand, }) {
|
|
2
2
|
return {
|
|
3
3
|
engine,
|
|
4
4
|
execution: detached ? "background" : "foreground",
|
|
5
5
|
attached: !detached,
|
|
6
6
|
detached,
|
|
7
|
+
workerPid,
|
|
7
8
|
reattachCommand,
|
|
8
9
|
};
|
|
9
10
|
}
|
|
@@ -78,6 +78,15 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
78
78
|
cwd,
|
|
79
79
|
log,
|
|
80
80
|
}, runnerDeps);
|
|
81
|
+
await writeAssistantOutput(runOptions.writeOutputPath, result.answerText ?? "", log);
|
|
82
|
+
await sendSessionNotification({
|
|
83
|
+
sessionId: sessionMeta.id,
|
|
84
|
+
sessionName: sessionMeta.options?.slug ?? sessionMeta.id,
|
|
85
|
+
mode,
|
|
86
|
+
model: sessionMeta.model,
|
|
87
|
+
usage: result.usage,
|
|
88
|
+
characters: result.answerText?.length,
|
|
89
|
+
}, notificationSettings, log, result.answerText?.slice(0, 140));
|
|
81
90
|
if (modelForStatus) {
|
|
82
91
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
83
92
|
status: "completed",
|
|
@@ -103,15 +112,6 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
103
112
|
transport: undefined,
|
|
104
113
|
error: undefined,
|
|
105
114
|
});
|
|
106
|
-
await writeAssistantOutput(runOptions.writeOutputPath, result.answerText ?? "", log);
|
|
107
|
-
await sendSessionNotification({
|
|
108
|
-
sessionId: sessionMeta.id,
|
|
109
|
-
sessionName: sessionMeta.options?.slug ?? sessionMeta.id,
|
|
110
|
-
mode,
|
|
111
|
-
model: sessionMeta.model,
|
|
112
|
-
usage: result.usage,
|
|
113
|
-
characters: result.answerText?.length,
|
|
114
|
-
}, notificationSettings, log, result.answerText?.slice(0, 140));
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
const multiModels = Array.isArray(runOptions.models) ? runOptions.models.filter(Boolean) : [];
|
|
@@ -362,16 +362,6 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
362
362
|
if (result.mode !== "live") {
|
|
363
363
|
throw new Error("Unexpected preview result while running a session.");
|
|
364
364
|
}
|
|
365
|
-
await sessionStore.updateSession(sessionMeta.id, {
|
|
366
|
-
status: "completed",
|
|
367
|
-
completedAt: new Date().toISOString(),
|
|
368
|
-
usage: result.usage,
|
|
369
|
-
elapsedMs: result.elapsedMs,
|
|
370
|
-
errorMessage: undefined,
|
|
371
|
-
response: extractResponseMetadata(result.response),
|
|
372
|
-
transport: undefined,
|
|
373
|
-
error: undefined,
|
|
374
|
-
});
|
|
375
365
|
if (modelForStatus && singleModelOverride == null) {
|
|
376
366
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
377
367
|
status: "completed",
|
|
@@ -389,6 +379,16 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
389
379
|
usage: result.usage,
|
|
390
380
|
characters: answerText.length,
|
|
391
381
|
}, notificationSettings, log, answerText.slice(0, 140));
|
|
382
|
+
await sessionStore.updateSession(sessionMeta.id, {
|
|
383
|
+
status: "completed",
|
|
384
|
+
completedAt: new Date().toISOString(),
|
|
385
|
+
usage: result.usage,
|
|
386
|
+
elapsedMs: result.elapsedMs,
|
|
387
|
+
errorMessage: undefined,
|
|
388
|
+
response: extractResponseMetadata(result.response),
|
|
389
|
+
transport: undefined,
|
|
390
|
+
error: undefined,
|
|
391
|
+
});
|
|
392
392
|
}
|
|
393
393
|
catch (error) {
|
|
394
394
|
const message = formatError(error);
|
|
@@ -510,16 +510,60 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
510
510
|
const runtime = userError.details
|
|
511
511
|
?.runtime;
|
|
512
512
|
log(dim("Assistant response timed out; marking capture incomplete for reattach."));
|
|
513
|
+
const timeoutResponse = {
|
|
514
|
+
status: "incomplete",
|
|
515
|
+
incompleteReason: "incomplete-capture",
|
|
516
|
+
};
|
|
517
|
+
const timeoutError = {
|
|
518
|
+
category: userError.category,
|
|
519
|
+
message: userError.message,
|
|
520
|
+
details: userError.details,
|
|
521
|
+
};
|
|
522
|
+
const autoReattachIntervalMs = browserConfig?.autoReattachIntervalMs ?? 0;
|
|
523
|
+
const autoRuntime = runtime ?? currentBrowser?.runtime;
|
|
524
|
+
const willAutoReattach = autoReattachIntervalMs > 0 && Boolean(autoRuntime);
|
|
525
|
+
if (willAutoReattach) {
|
|
526
|
+
if (modelForStatus) {
|
|
527
|
+
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
528
|
+
status: "running",
|
|
529
|
+
completedAt: undefined,
|
|
530
|
+
response: timeoutResponse,
|
|
531
|
+
error: timeoutError,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
await sessionStore.updateSession(sessionMeta.id, {
|
|
535
|
+
status: "running",
|
|
536
|
+
completedAt: undefined,
|
|
537
|
+
errorMessage: message,
|
|
538
|
+
mode,
|
|
539
|
+
browser: {
|
|
540
|
+
...currentBrowser,
|
|
541
|
+
config: browserConfig,
|
|
542
|
+
runtime: autoRuntime,
|
|
543
|
+
},
|
|
544
|
+
response: timeoutResponse,
|
|
545
|
+
error: timeoutError,
|
|
546
|
+
});
|
|
547
|
+
const success = await autoReattachUntilComplete({
|
|
548
|
+
sessionMeta,
|
|
549
|
+
runtime: autoRuntime,
|
|
550
|
+
browserConfig,
|
|
551
|
+
browserMetadata: currentBrowser,
|
|
552
|
+
runOptions,
|
|
553
|
+
modelForStatus,
|
|
554
|
+
notificationSettings,
|
|
555
|
+
log,
|
|
556
|
+
});
|
|
557
|
+
if (success) {
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
513
561
|
if (modelForStatus) {
|
|
514
562
|
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
515
563
|
status: "error",
|
|
516
564
|
completedAt: new Date().toISOString(),
|
|
517
|
-
response:
|
|
518
|
-
error:
|
|
519
|
-
category: userError.category,
|
|
520
|
-
message: userError.message,
|
|
521
|
-
details: userError.details,
|
|
522
|
-
},
|
|
565
|
+
response: timeoutResponse,
|
|
566
|
+
error: timeoutError,
|
|
523
567
|
});
|
|
524
568
|
}
|
|
525
569
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
@@ -532,30 +576,9 @@ export async function performSessionRun({ sessionMeta, runOptions, mode, browser
|
|
|
532
576
|
config: browserConfig,
|
|
533
577
|
runtime: runtime ?? currentBrowser?.runtime,
|
|
534
578
|
},
|
|
535
|
-
response:
|
|
536
|
-
error:
|
|
537
|
-
category: userError.category,
|
|
538
|
-
message: userError.message,
|
|
539
|
-
details: userError.details,
|
|
540
|
-
},
|
|
579
|
+
response: timeoutResponse,
|
|
580
|
+
error: timeoutError,
|
|
541
581
|
});
|
|
542
|
-
const autoReattachIntervalMs = browserConfig?.autoReattachIntervalMs ?? 0;
|
|
543
|
-
if (autoReattachIntervalMs > 0) {
|
|
544
|
-
const autoRuntime = runtime ?? currentBrowser?.runtime;
|
|
545
|
-
const success = await autoReattachUntilComplete({
|
|
546
|
-
sessionMeta,
|
|
547
|
-
runtime: autoRuntime ?? undefined,
|
|
548
|
-
browserConfig,
|
|
549
|
-
browserMetadata: currentBrowser,
|
|
550
|
-
runOptions,
|
|
551
|
-
modelForStatus,
|
|
552
|
-
notificationSettings,
|
|
553
|
-
log,
|
|
554
|
-
});
|
|
555
|
-
if (success) {
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
582
|
logBrowserReattachGuidance(runtime ?? currentBrowser?.runtime);
|
|
560
583
|
return;
|
|
561
584
|
}
|
|
@@ -911,6 +934,7 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
911
934
|
}
|
|
912
935
|
attempt += 1;
|
|
913
936
|
log(dim(`Auto-reattach attempt ${attempt}...`));
|
|
937
|
+
let captureSucceeded = false;
|
|
914
938
|
try {
|
|
915
939
|
const reattachConfig = {
|
|
916
940
|
...browserConfig,
|
|
@@ -919,6 +943,7 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
919
943
|
const result = await resumeBrowserSession(runtime, reattachConfig, logger, {
|
|
920
944
|
promptPreview: sessionMeta.promptPreview,
|
|
921
945
|
});
|
|
946
|
+
captureSucceeded = true;
|
|
922
947
|
const answerText = result.answerMarkdown || result.answerText || "";
|
|
923
948
|
const outputTokens = estimateTokenCount(answerText);
|
|
924
949
|
const artifacts = await ensureSessionArtifacts({
|
|
@@ -947,6 +972,18 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
947
972
|
},
|
|
948
973
|
});
|
|
949
974
|
}
|
|
975
|
+
await writeAssistantOutput(runOptions.writeOutputPath, answerText, log);
|
|
976
|
+
await sendSessionNotification({
|
|
977
|
+
sessionId: sessionMeta.id,
|
|
978
|
+
sessionName: sessionMeta.options?.slug ?? sessionMeta.id,
|
|
979
|
+
mode: sessionMeta.mode ?? "browser",
|
|
980
|
+
model: sessionMeta.model ?? runOptions.model,
|
|
981
|
+
usage: {
|
|
982
|
+
inputTokens: 0,
|
|
983
|
+
outputTokens,
|
|
984
|
+
},
|
|
985
|
+
characters: answerText.length,
|
|
986
|
+
}, notificationSettings, log, answerText.slice(0, 140));
|
|
950
987
|
await sessionStore.updateSession(sessionMeta.id, {
|
|
951
988
|
status: "completed",
|
|
952
989
|
completedAt: new Date().toISOString(),
|
|
@@ -967,22 +1004,35 @@ async function autoReattachUntilComplete({ sessionMeta, runtime, browserConfig,
|
|
|
967
1004
|
error: undefined,
|
|
968
1005
|
transport: undefined,
|
|
969
1006
|
});
|
|
970
|
-
await writeAssistantOutput(runOptions.writeOutputPath, answerText, log);
|
|
971
|
-
await sendSessionNotification({
|
|
972
|
-
sessionId: sessionMeta.id,
|
|
973
|
-
sessionName: sessionMeta.options?.slug ?? sessionMeta.id,
|
|
974
|
-
mode: sessionMeta.mode ?? "browser",
|
|
975
|
-
model: sessionMeta.model ?? runOptions.model,
|
|
976
|
-
usage: {
|
|
977
|
-
inputTokens: 0,
|
|
978
|
-
outputTokens,
|
|
979
|
-
},
|
|
980
|
-
characters: answerText.length,
|
|
981
|
-
}, notificationSettings, log, answerText.slice(0, 140));
|
|
982
1007
|
log(kleur.green("Auto-reattach succeeded; session marked completed."));
|
|
983
1008
|
return true;
|
|
984
1009
|
}
|
|
985
1010
|
catch (error) {
|
|
1011
|
+
if (captureSucceeded) {
|
|
1012
|
+
const message = formatError(error);
|
|
1013
|
+
if (modelForStatus) {
|
|
1014
|
+
await sessionStore.updateModelRun(sessionMeta.id, modelForStatus, {
|
|
1015
|
+
status: "error",
|
|
1016
|
+
completedAt: new Date().toISOString(),
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
await sessionStore.updateSession(sessionMeta.id, {
|
|
1020
|
+
status: "error",
|
|
1021
|
+
completedAt: new Date().toISOString(),
|
|
1022
|
+
errorMessage: message,
|
|
1023
|
+
browser: {
|
|
1024
|
+
...browserMetadata,
|
|
1025
|
+
config: browserConfig,
|
|
1026
|
+
runtime,
|
|
1027
|
+
},
|
|
1028
|
+
response: { status: "error", incompleteReason: "incomplete-capture" },
|
|
1029
|
+
error: {
|
|
1030
|
+
category: "internal",
|
|
1031
|
+
message,
|
|
1032
|
+
},
|
|
1033
|
+
});
|
|
1034
|
+
throw error;
|
|
1035
|
+
}
|
|
986
1036
|
const message = error instanceof Error ? error.message : String(error);
|
|
987
1037
|
log(dim(`Auto-reattach attempt ${attempt} failed: ${message}`));
|
|
988
1038
|
}
|
|
@@ -3,6 +3,7 @@ import kleur from "kleur";
|
|
|
3
3
|
import { MODEL_CONFIGS } from "../oracle.js";
|
|
4
4
|
import { estimateUsdCost } from "tokentally";
|
|
5
5
|
import { formatSessionExecutionLabel } from "./sessionLifecycle.js";
|
|
6
|
+
import { resolveSessionBrowserModelDisplayName } from "../browser/modelDisplay.js";
|
|
6
7
|
const isRich = (rich) => rich ?? Boolean(process.stdout.isTTY && chalk.level > 0);
|
|
7
8
|
const dim = (text, rich) => (rich ? kleur.dim(text) : text);
|
|
8
9
|
export const STATUS_PAD = 9;
|
|
@@ -18,7 +19,10 @@ export function formatSessionTableHeader(rich) {
|
|
|
18
19
|
export function formatSessionTableRow(meta, options) {
|
|
19
20
|
const rich = isRich(options?.rich);
|
|
20
21
|
const status = colorStatus(meta.status ?? "unknown", rich);
|
|
21
|
-
const
|
|
22
|
+
const displayModel = (meta.mode ?? meta.options?.mode) === "browser"
|
|
23
|
+
? resolveSessionBrowserModelDisplayName(meta)
|
|
24
|
+
: (meta.model ?? "n/a");
|
|
25
|
+
const modelLabel = displayModel.padEnd(MODEL_PAD);
|
|
22
26
|
const model = rich ? chalk.white(modelLabel) : modelLabel;
|
|
23
27
|
const modeLabel = formatSessionExecutionLabel(meta).padEnd(MODE_PAD);
|
|
24
28
|
const mode = rich ? chalk.gray(modeLabel) : modeLabel;
|
|
@@ -15,6 +15,7 @@ import { resolveNotificationSettings } from "../notifier.js";
|
|
|
15
15
|
import { loadUserConfig } from "../../config.js";
|
|
16
16
|
import { resolveConfiguredMaxFileSizeBytes } from "../fileSize.js";
|
|
17
17
|
import { formatTokenCount } from "../../oracle/runUtils.js";
|
|
18
|
+
import { formatSessionBrowserModelWithRequestedKey, resolveSessionBrowserModelDisplayName, } from "../../browser/modelDisplay.js";
|
|
18
19
|
const isTty = () => Boolean(process.stdout.isTTY && chalk.level > 0);
|
|
19
20
|
const dim = (text) => (isTty() ? kleur.dim(text) : text);
|
|
20
21
|
const RECENT_WINDOW_HOURS = 24;
|
|
@@ -157,7 +158,7 @@ async function showSessionDetail(sessionId) {
|
|
|
157
158
|
console.clear();
|
|
158
159
|
printSessionHeader(meta);
|
|
159
160
|
if (meta.models && meta.models.length > 0) {
|
|
160
|
-
printModelSummaries(meta
|
|
161
|
+
printModelSummaries(meta);
|
|
161
162
|
}
|
|
162
163
|
const prompt = await readStoredPrompt(sessionId);
|
|
163
164
|
if (prompt) {
|
|
@@ -250,7 +251,10 @@ function printSessionHeader(meta) {
|
|
|
250
251
|
console.log(`${chalk.white("Status:")} ${meta.status}`);
|
|
251
252
|
console.log(`${chalk.white("Created:")} ${meta.createdAt}`);
|
|
252
253
|
if (meta.model) {
|
|
253
|
-
|
|
254
|
+
const modelLabel = (meta.mode ?? meta.options?.mode) === "browser"
|
|
255
|
+
? resolveSessionBrowserModelDisplayName(meta)
|
|
256
|
+
: meta.model;
|
|
257
|
+
console.log(`${chalk.white("Model:")} ${modelLabel}`);
|
|
254
258
|
}
|
|
255
259
|
const mode = meta.mode ?? meta.options?.mode;
|
|
256
260
|
if (mode) {
|
|
@@ -260,7 +264,8 @@ function printSessionHeader(meta) {
|
|
|
260
264
|
console.log(chalk.red(`Error: ${meta.errorMessage}`));
|
|
261
265
|
}
|
|
262
266
|
}
|
|
263
|
-
function printModelSummaries(
|
|
267
|
+
function printModelSummaries(meta) {
|
|
268
|
+
const models = meta.models ?? [];
|
|
264
269
|
if (models.length === 0) {
|
|
265
270
|
return;
|
|
266
271
|
}
|
|
@@ -269,7 +274,10 @@ function printModelSummaries(models) {
|
|
|
269
274
|
const usage = run.usage
|
|
270
275
|
? ` tok=${formatTokenCount(run.usage.outputTokens ?? 0)}/${formatTokenCount(run.usage.totalTokens ?? 0)}`
|
|
271
276
|
: "";
|
|
272
|
-
|
|
277
|
+
const modelLabel = (meta.mode ?? meta.options?.mode) === "browser"
|
|
278
|
+
? formatSessionBrowserModelWithRequestedKey(meta, run.model)
|
|
279
|
+
: run.model;
|
|
280
|
+
console.log(` - ${chalk.cyan(modelLabel)} — ${run.status}${usage}`);
|
|
273
281
|
}
|
|
274
282
|
console.log("");
|
|
275
283
|
}
|