Skip to content

Commit 7bca360

Browse files
committed
fix: keep browser for in-flight runs + downgrade 5.1 pro API
1 parent 013d861 commit 7bca360

File tree

4 files changed

+30
-6
lines changed

4 files changed

+30
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Fixed
66
- Browser uploads: detect ChatGPT’s composer attachment chip (not echoed in the last user turn) to avoid false “Attachment did not appear” failures. Thanks Mariano Belinky (@mbelinky) for the fix.
7+
- Browser interruption: if the user/agent sends SIGINT/SIGTERM/SIGQUIT while the assistant response is still pending, Oracle leaves Chrome running, writes runtime hints, and logs how to reattach with `oracle session <slug>` instead of killing the browser mid-run.
78

89
## 0.5.3 — 2025-12-06
910

src/browser/chromeLifecycle.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ export function registerTerminationHooks(
4141
userDataDir: string,
4242
keepBrowser: boolean,
4343
logger: BrowserLogger,
44+
opts?: {
45+
/** Return true when the run is still in-flight (assistant response pending). */
46+
isInFlight?: () => boolean;
47+
/** Persist runtime hints so reattach can find the live Chrome. */
48+
emitRuntimeHint?: () => Promise<void>;
49+
},
4450
): () => void {
4551
const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGQUIT'];
4652
let handling: boolean | undefined;
@@ -50,13 +56,21 @@ export function registerTerminationHooks(
5056
return;
5157
}
5258
handling = true;
53-
if (keepBrowser) {
54-
logger(`Received ${signal}; leaving Chrome running for potential reattach`);
59+
const inFlight = opts?.isInFlight?.() ?? false;
60+
const leaveRunning = keepBrowser || inFlight;
61+
if (leaveRunning) {
62+
logger(`Received ${signal}; leaving Chrome running${inFlight ? ' (assistant response pending)' : ''}`);
5563
} else {
5664
logger(`Received ${signal}; terminating Chrome process`);
5765
}
5866
void (async () => {
59-
if (!keepBrowser) {
67+
if (leaveRunning) {
68+
// Ensure reattach hints are written before we exit.
69+
await opts?.emitRuntimeHint?.().catch(() => undefined);
70+
if (inFlight) {
71+
logger('Session still in flight; reattach with "oracle session <slug>" to continue.');
72+
}
73+
} else {
6074
try {
6175
await chrome.kill();
6276
} catch {

src/browser/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,10 @@ export async function runBrowserMode(options: BrowserRunOptions): Promise<Browse
136136
const chromeHost = (chrome as unknown as { host?: string }).host ?? '127.0.0.1';
137137
let removeTerminationHooks: (() => void) | null = null;
138138
try {
139-
removeTerminationHooks = registerTerminationHooks(chrome, userDataDir, effectiveKeepBrowser, logger);
139+
removeTerminationHooks = registerTerminationHooks(chrome, userDataDir, effectiveKeepBrowser, logger, {
140+
isInFlight: () => runStatus !== 'complete',
141+
emitRuntimeHint,
142+
});
140143
} catch {
141144
// ignore failure; cleanup still happens below
142145
}

src/oracle/run.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,15 +240,18 @@ export async function runOracle(options: RunOracleOptions, deps: RunOracleDeps =
240240
: DEFAULT_TIMEOUT_NON_PRO_MS / 1000
241241
: options.timeoutSeconds;
242242
const timeoutMs = timeoutSeconds * 1000;
243+
const apiModelFromConfig = modelConfig.apiModel ?? modelConfig.model;
244+
const modelDowngraded = apiModelFromConfig === 'gpt-5.1-pro';
245+
const resolvedApiModelId = modelDowngraded ? 'gpt-5-pro' : apiModelFromConfig;
243246
// Track the concrete model id we dispatch to (especially for Gemini preview aliases)
244247
const effectiveModelId =
245248
options.effectiveModelId ??
246249
(options.model.startsWith('gemini')
247250
? resolveGeminiModelId(options.model)
248-
: modelConfig.apiModel ?? modelConfig.model);
251+
: resolvedApiModelId);
249252
const headerModelLabel = richTty ? chalk.cyan(modelConfig.model) : modelConfig.model;
250253
const requestBody = buildRequestBody({
251-
modelConfig,
254+
modelConfig: { ...modelConfig, apiModel: resolvedApiModelId },
252255
systemPrompt,
253256
userPrompt: promptWithFiles,
254257
searchEnabled,
@@ -279,6 +282,9 @@ export async function runOracle(options: RunOracleOptions, deps: RunOracleDeps =
279282
if (baseUrl) {
280283
log(dim(`Base URL: ${formatBaseUrlForLog(baseUrl)}`));
281284
}
285+
if (modelDowngraded) {
286+
log(dim('gpt-5.1-pro is not yet available via API; sending request with gpt-5-pro instead.'));
287+
}
282288
if (options.background && !supportsBackground) {
283289
log(dim('Background runs are not supported for this model; streaming in foreground instead.'));
284290
}

0 commit comments

Comments
 (0)