@steipete/oracle 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/oracle-cli.js +65 -63
  3. package/dist/bin/oracle-mcp.js +0 -0
  4. package/dist/src/browser/actions/archiveConversation.js +31 -7
  5. package/dist/src/browser/actions/assistantResponse.js +43 -1
  6. package/dist/src/browser/actions/attachmentContext.js +239 -0
  7. package/dist/src/browser/actions/attachmentDataTransfer.js +19 -1
  8. package/dist/src/browser/actions/attachmentEvidence.js +78 -0
  9. package/dist/src/browser/actions/attachmentProgress.js +41 -0
  10. package/dist/src/browser/actions/attachmentPrompt.js +130 -0
  11. package/dist/src/browser/actions/attachments.js +306 -355
  12. package/dist/src/browser/actions/deepResearch.js +2 -1
  13. package/dist/src/browser/actions/modelSelection.js +13 -3
  14. package/dist/src/browser/actions/navigation.js +1 -1
  15. package/dist/src/browser/actions/promptComposer.js +361 -186
  16. package/dist/src/browser/actions/remoteFileTransfer.js +4 -2
  17. package/dist/src/browser/actions/thinkingStatus.js +2 -5
  18. package/dist/src/browser/actions/thinkingTime.js +119 -16
  19. package/dist/src/browser/attachRunning.js +46 -2
  20. package/dist/src/browser/attachmentValidation.js +53 -0
  21. package/dist/src/browser/cancellation.js +105 -0
  22. package/dist/src/browser/chatgptImages.js +2 -0
  23. package/dist/src/browser/chatgptThrottle.js +104 -0
  24. package/dist/src/browser/chromeLifecycle.js +267 -43
  25. package/dist/src/browser/config.js +13 -0
  26. package/dist/src/browser/detect.js +1 -1
  27. package/dist/src/browser/index.js +444 -261
  28. package/dist/src/browser/modelDisplay.js +9 -0
  29. package/dist/src/browser/pageActions.js +2 -2
  30. package/dist/src/browser/profileState.js +114 -9
  31. package/dist/src/browser/projectSourcesRunner.js +83 -15
  32. package/dist/src/browser/prompt.js +272 -113
  33. package/dist/src/browser/providers/chatgptDomProvider.js +2 -0
  34. package/dist/src/browser/reattach.js +10 -3
  35. package/dist/src/browser/sessionRunner.js +71 -11
  36. package/dist/src/browser/tabLeaseRegistry.js +414 -38
  37. package/dist/src/browser/utils.js +20 -2
  38. package/dist/src/cli/browserConfig.js +3 -1
  39. package/dist/src/cli/browserDefaults.js +10 -0
  40. package/dist/src/cli/browserTabs.js +12 -29
  41. package/dist/src/cli/detachedSession.js +69 -0
  42. package/dist/src/cli/dryRun.js +58 -48
  43. package/dist/src/cli/harvestIntegrity.js +140 -0
  44. package/dist/src/cli/serveBrowserConfig.js +24 -0
  45. package/dist/src/cli/sessionDisplay.js +9 -2
  46. package/dist/src/cli/sessionRunner.js +91 -7
  47. package/dist/src/config.js +1 -0
  48. package/dist/src/gemini-web/client.js +67 -21
  49. package/dist/src/gemini-web/executor.js +4 -0
  50. package/dist/src/gemini-web/http.js +15 -0
  51. package/dist/src/gemini-web/models.js +15 -11
  52. package/dist/src/mcp/server.js +11 -14
  53. package/dist/src/mcp/tools/chatgptImage.js +4 -4
  54. package/dist/src/mcp/tools/consult.js +84 -22
  55. package/dist/src/mcp/tools/projectSources.js +4 -6
  56. package/dist/src/mcp/tools/sessionResources.js +1 -1
  57. package/dist/src/mcp/tools/sessions.js +2 -2
  58. package/dist/src/mcp/tools/wait.js +182 -0
  59. package/dist/src/mcp/types.js +7 -0
  60. package/dist/src/oracle/errors.js +11 -0
  61. package/dist/src/oracle/providerRoutePlan.js +3 -2
  62. package/dist/src/remote/client.js +70 -6
  63. package/dist/src/remote/health.js +6 -2
  64. package/dist/src/remote/runSlots.js +83 -0
  65. package/dist/src/remote/server.js +345 -71
  66. package/dist/src/sessionManager.js +36 -1
  67. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  68. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  69. package/package.json +25 -23
  70. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  71. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  72. package/dist/bin/oracle.js +0 -683
  73. package/dist/docs-site/.nojekyll +0 -0
  74. package/dist/docs-site/CNAME +0 -1
  75. package/dist/docs-site/RELEASING.html +0 -410
  76. package/dist/docs-site/agents.html +0 -374
  77. package/dist/docs-site/anthropic.html +0 -368
  78. package/dist/docs-site/bridge.html +0 -400
  79. package/dist/docs-site/browser-mode.html +0 -588
  80. package/dist/docs-site/chromium-forks.html +0 -347
  81. package/dist/docs-site/cli-reference.html +0 -346
  82. package/dist/docs-site/configuration.html +0 -436
  83. package/dist/docs-site/favicon.svg +0 -14
  84. package/dist/docs-site/followup.html +0 -368
  85. package/dist/docs-site/gemini.html +0 -379
  86. package/dist/docs-site/grok.html +0 -325
  87. package/dist/docs-site/index.html +0 -360
  88. package/dist/docs-site/install.html +0 -335
  89. package/dist/docs-site/linux.html +0 -321
  90. package/dist/docs-site/llms.txt +0 -43
  91. package/dist/docs-site/manual-tests.html +0 -595
  92. package/dist/docs-site/mcp.html +0 -368
  93. package/dist/docs-site/multimodel.html +0 -364
  94. package/dist/docs-site/mythical-pro-agents.html +0 -360
  95. package/dist/docs-site/notifier.html +0 -338
  96. package/dist/docs-site/openai-endpoints.html +0 -387
  97. package/dist/docs-site/openrouter.html +0 -344
  98. package/dist/docs-site/quickstart.html +0 -369
  99. package/dist/docs-site/refactor/ux.html +0 -532
  100. package/dist/docs-site/sessions.html +0 -388
  101. package/dist/docs-site/social-card.png +0 -0
  102. package/dist/docs-site/social-card.svg +0 -79
  103. package/dist/docs-site/spec.html +0 -363
  104. package/dist/docs-site/testing.html +0 -320
  105. package/dist/docs-site/tui-debug.html +0 -326
  106. package/dist/docs-site/windows-work.html +0 -323
  107. package/dist/docs-site/windows.html +0 -320
  108. package/dist/markdansi/types/index.js +0 -4
  109. package/dist/oracle/bin/oracle-cli.js +0 -472
  110. package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
  111. package/dist/oracle/src/browser/actions/attachments.js +0 -82
  112. package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
  113. package/dist/oracle/src/browser/actions/navigation.js +0 -75
  114. package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
  115. package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
  116. package/dist/oracle/src/browser/config.js +0 -33
  117. package/dist/oracle/src/browser/constants.js +0 -40
  118. package/dist/oracle/src/browser/cookies.js +0 -210
  119. package/dist/oracle/src/browser/domDebug.js +0 -36
  120. package/dist/oracle/src/browser/index.js +0 -331
  121. package/dist/oracle/src/browser/pageActions.js +0 -5
  122. package/dist/oracle/src/browser/prompt.js +0 -88
  123. package/dist/oracle/src/browser/promptSummary.js +0 -20
  124. package/dist/oracle/src/browser/sessionRunner.js +0 -80
  125. package/dist/oracle/src/browser/types.js +0 -1
  126. package/dist/oracle/src/browser/utils.js +0 -62
  127. package/dist/oracle/src/browserMode.js +0 -1
  128. package/dist/oracle/src/cli/browserConfig.js +0 -44
  129. package/dist/oracle/src/cli/dryRun.js +0 -59
  130. package/dist/oracle/src/cli/engine.js +0 -17
  131. package/dist/oracle/src/cli/errorUtils.js +0 -9
  132. package/dist/oracle/src/cli/help.js +0 -70
  133. package/dist/oracle/src/cli/markdownRenderer.js +0 -15
  134. package/dist/oracle/src/cli/options.js +0 -103
  135. package/dist/oracle/src/cli/promptRequirement.js +0 -14
  136. package/dist/oracle/src/cli/rootAlias.js +0 -30
  137. package/dist/oracle/src/cli/sessionCommand.js +0 -77
  138. package/dist/oracle/src/cli/sessionDisplay.js +0 -270
  139. package/dist/oracle/src/cli/sessionRunner.js +0 -94
  140. package/dist/oracle/src/heartbeat.js +0 -43
  141. package/dist/oracle/src/oracle/client.js +0 -48
  142. package/dist/oracle/src/oracle/config.js +0 -29
  143. package/dist/oracle/src/oracle/errors.js +0 -101
  144. package/dist/oracle/src/oracle/files.js +0 -220
  145. package/dist/oracle/src/oracle/format.js +0 -33
  146. package/dist/oracle/src/oracle/fsAdapter.js +0 -7
  147. package/dist/oracle/src/oracle/oscProgress.js +0 -60
  148. package/dist/oracle/src/oracle/request.js +0 -48
  149. package/dist/oracle/src/oracle/run.js +0 -444
  150. package/dist/oracle/src/oracle/tokenStats.js +0 -39
  151. package/dist/oracle/src/oracle/types.js +0 -1
  152. package/dist/oracle/src/oracle.js +0 -9
  153. package/dist/oracle/src/sessionManager.js +0 -205
  154. package/dist/oracle/src/version.js +0 -39
  155. package/dist/scripts/chrome/browser-tools.js +0 -295
  156. package/dist/src/browser/chromeCookies.js +0 -312
  157. package/dist/src/browser/keytarShim.js +0 -56
  158. package/dist/src/browser/profileSync.js +0 -141
  159. package/dist/src/browser/windowsCookies.js +0 -219
package/README.md CHANGED
@@ -82,7 +82,7 @@ oracle --dry-run summary --files-report \
82
82
  --file "!**/*.test.ts"
83
83
  ```
84
84
 
85
- Generated text bundles include stable line numbers so answers can cite `path:line`. Binary and large browser inputs can be uploaded or bundled without converting their contents. The [CLI reference](docs/cli-reference.md) lists the file, size, output, and browser controls.
85
+ Generated text bundles include stable line numbers so answers can cite `path:line`. In browser mode, one uploaded text/source file stays native and multiple text/source files are packed into one bundle: flattened text for text-only `auto` uploads, or a ZIP when raw files are present or `--browser-bundle-format zip` is set. Native images and documents remain direct attachments when possible. The [CLI reference](docs/cli-reference.md) lists the file, size, output, and browser controls.
86
86
 
87
87
  ## Sessions and follow-ups
88
88
 
@@ -1,14 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import "dotenv/config";
3
- import { spawn } from "node:child_process";
4
3
  import { fileURLToPath } from "node:url";
5
4
  import { Command, Option } from "commander";
6
- // Allow `npx @steipete/oracle oracle-mcp` to resolve the MCP server even though npx runs the default binary.
7
- if (process.argv[2] === "oracle-mcp") {
8
- const { startMcpServer } = await import("../src/mcp/server.js");
9
- await startMcpServer();
10
- process.exit(0);
11
- }
12
5
  import { resolveEngine, defaultWaitPreference } from "../src/cli/engine.js";
13
6
  import { shouldRequirePrompt } from "../src/cli/promptRequirement.js";
14
7
  import { resolveDashPrompt } from "../src/cli/stdin.js";
@@ -22,6 +15,7 @@ import { collectPaths, collectModelList, collectTextValues, parseFloatOption, pa
22
15
  import { copyToClipboard } from "../src/cli/clipboard.js";
23
16
  import { buildMarkdownBundle } from "../src/cli/markdownBundle.js";
24
17
  import { shouldDetachSession, stopDetachedWorker } from "../src/cli/detach.js";
18
+ import { launchDetachedSession } from "../src/cli/detachedSession.js";
25
19
  import { applyHiddenAliases } from "../src/cli/hiddenAliases.js";
26
20
  import { isMediaFile } from "../src/browser/prompt.js";
27
21
  import { formatCompactNumber } from "../src/cli/format.js";
@@ -283,6 +277,7 @@ program
283
277
  .option("--render", "Alias for --render-markdown.", false)
284
278
  .option("--render-plain", "Render markdown without ANSI/highlighting (use plain text even in a TTY).", false)
285
279
  .option("--write-output <path>", "Write only the final assistant message to this file (overwrites; multi-model appends .<model> before the extension).")
280
+ .option("--write-artifacts", "Also export captured browser files beside --write-output without overwriting existing files (browser runs only).", false)
286
281
  .option("--allow-partial", "Exit 0 for multi-model runs when at least one model succeeds.", false)
287
282
  .addOption(new Option("--partial <mode>", "Multi-model failure policy (fail | ok).")
288
283
  .choices(["fail", "ok"])
@@ -314,6 +309,7 @@ program
314
309
  .addOption(new Option("--browser-url <url>", `Alias for --chatgpt-url (default ${CHATGPT_URL}).`).hideHelp())
315
310
  .addOption(new Option("--browser-timeout <ms|s|m>", "Maximum time to wait for an answer (default 1200s / 20m).").hideHelp())
316
311
  .addOption(new Option("--browser-input-timeout <ms|s|m>", "Maximum time to wait for the prompt textarea (default 60s).").hideHelp())
312
+ .addOption(new Option("--browser-approval-wait <duration>", "Wait for each Chrome remote-debugging approval prompt (default 20s; e.g. 5m).").env("ORACLE_BROWSER_APPROVAL_WAIT"))
317
313
  .addOption(new Option("--browser-attachment-timeout <ms|s|m>", "Maximum time to wait for attachment upload/readiness before clicking send (default 45s).").hideHelp())
318
314
  .addOption(new Option("--browser-recheck-delay <ms|s|m|h>", "After an assistant timeout, wait this long then revisit the conversation to retry capture.").hideHelp())
319
315
  .addOption(new Option("--browser-recheck-timeout <ms|s|m|h>", "Time budget for the delayed recheck attempt (default 120s).").hideHelp())
@@ -357,8 +353,8 @@ program
357
353
  .addOption(new Option("--remote-host <host:port>", "Delegate browser runs to a remote `oracle serve` instance."))
358
354
  .addOption(new Option("--remote-token <token>", "Access token for the remote `oracle serve` instance."))
359
355
  .addOption(new Option("--browser-inline-files", "Alias for --browser-attachments never (force pasting file contents inline).").default(false))
360
- .addOption(new Option("--browser-bundle-files", "Bundle all attachments into a single archive before uploading.").default(false))
361
- .addOption(new Option("--browser-bundle-format <format>", "Bundle format for browser uploads when files are bundled: auto (default), text, or zip.")
356
+ .addOption(new Option("--browser-bundle-files", "Force one browser upload bundle; auto/zip includes all resolved files. Multi-file text/source uploads already bundle by default (flattened text unless ZIP is selected).").default(false))
357
+ .addOption(new Option("--browser-bundle-format <format>", "Bundle format for browser uploads: auto (flattened text for text-only, ZIP when raw files are present), text, or zip.")
362
358
  .choices(["auto", "text", "zip"])
363
359
  .default("auto"))
364
360
  .addOption(new Option("--youtube <url>", "YouTube video URL to analyze (Gemini web/cookie mode only; uses your signed-in Chrome cookies for gemini.google.com)."))
@@ -367,6 +363,7 @@ program
367
363
  .addOption(new Option("--output <file>", "Output file path for image operations."))
368
364
  .addOption(new Option("--aspect <ratio>", "Aspect ratio for image generation: 16:9, 1:1, 4:3, 3:4 (Gemini web/cookie mode only)."))
369
365
  .addOption(new Option("--gemini-show-thoughts", "Display Gemini thinking process (Gemini web/cookie mode only).").default(false))
366
+ .option("--no-gemini-fallback", "Fail if the requested Gemini web model is unavailable instead of using Flash-Lite.")
370
367
  .option("--retain-hours <hours>", "Prune stored sessions older than this many hours before running (set 0 to disable).", parseFloatOption)
371
368
  .option("--force", "Force start a new session even if an identical prompt is already running.", false)
372
369
  .option("--debug-help", "Show the advanced/debug option set and exit.", false)
@@ -392,15 +389,26 @@ program
392
389
  .option("--host <address>", "Interface to bind (default 0.0.0.0).")
393
390
  .option("--port <number>", "Port to listen on (default random).", parseIntOption)
394
391
  .option("--token <value>", "Access token clients must provide (random if omitted).")
392
+ .option("--max-concurrent-runs <count>", "Opt into concurrent runs and FIFO queueing; clamped to the host browser tab cap (default remains single-flight HTTP 409).")
393
+ .option("--max-queued-runs <count>", "Waiting requests in opt-in queue mode (default 8; 0 disables waiting).")
395
394
  .option("--manual-login", "Use a dedicated Chrome profile for manual login (recommended when cookie sync is unavailable).", false)
396
395
  .option("--manual-login-profile-dir <path>", "Chrome profile directory for manual login (default ~/.oracle/browser-profile).")
397
396
  .option("--browser-cookie-sync", "Copy cookies from this host's live Chrome profile instead of using the dedicated profile.", false)
398
397
  .action(async (commandOptions) => {
399
398
  const { serveRemote } = await import("../src/remote/server.js");
399
+ const { buildServeBrowserConfig } = await import("../src/cli/serveBrowserConfig.js");
400
+ const { config } = await loadUserConfig();
400
401
  await serveRemote({
402
+ browserConfig: buildServeBrowserConfig(program.opts(), config),
401
403
  host: commandOptions.host,
402
404
  port: commandOptions.port,
403
405
  token: commandOptions.token,
406
+ maxConcurrentRuns: commandOptions.maxConcurrentRuns === undefined
407
+ ? undefined
408
+ : Number(commandOptions.maxConcurrentRuns),
409
+ maxQueuedRuns: commandOptions.maxQueuedRuns === undefined
410
+ ? undefined
411
+ : Number(commandOptions.maxQueuedRuns),
404
412
  manualLoginDefault: commandOptions.manualLogin,
405
413
  manualLoginProfileDir: commandOptions.manualLoginProfileDir,
406
414
  cookieSyncDefault: commandOptions.browserCookieSync,
@@ -713,6 +721,7 @@ function buildRunOptions(options, overrides = {}) {
713
721
  background: overrides.background ?? undefined,
714
722
  renderPlain: overrides.renderPlain ?? options.renderPlain ?? false,
715
723
  writeOutputPath: overrides.writeOutputPath ?? options.writeOutputPath,
724
+ writeArtifacts: overrides.writeArtifacts ?? options.writeArtifacts ?? false,
716
725
  };
717
726
  }
718
727
  function resolveApiProviderMode(options) {
@@ -952,6 +961,7 @@ function buildRunOptionsFromMetadata(metadata) {
952
961
  background: stored.background,
953
962
  renderPlain: stored.renderPlain,
954
963
  writeOutputPath: stored.writeOutputPath,
964
+ writeArtifacts: stored.writeArtifacts,
955
965
  };
956
966
  }
957
967
  function getSessionMode(metadata) {
@@ -1080,8 +1090,10 @@ async function runRootCommand(options) {
1080
1090
  return;
1081
1091
  }
1082
1092
  const retentionHours = typeof options.retainHours === "number" ? options.retainHours : undefined;
1083
- await sessionStore.ensureStorage();
1084
- await pruneOldSessions(retentionHours, (message) => console.log(chalk.dim(message)));
1093
+ if (!previewMode) {
1094
+ await sessionStore.ensureStorage();
1095
+ await pruneOldSessions(retentionHours, (message) => console.log(chalk.dim(message)));
1096
+ }
1085
1097
  if (providerMode === "openai") {
1086
1098
  if (hasExplicitAzureOption(optionUsesDefault)) {
1087
1099
  throw new Error("--provider openai/--no-azure cannot be combined with Azure options.");
@@ -1316,6 +1328,12 @@ async function runRootCommand(options) {
1316
1328
  }
1317
1329
  }
1318
1330
  const activeModel = resolvedOptions.model;
1331
+ if (options.writeArtifacts && engine !== "browser") {
1332
+ throw new Error("--write-artifacts requires --engine browser.");
1333
+ }
1334
+ if (options.writeArtifacts && !resolvedOptions.writeOutputPath) {
1335
+ throw new Error("--write-artifacts requires --write-output <path>.");
1336
+ }
1319
1337
  if (options.reasoningMode && engine !== "api") {
1320
1338
  throw new Error("--reasoning-mode requires --engine api.");
1321
1339
  }
@@ -1447,6 +1465,7 @@ async function runRootCommand(options) {
1447
1465
  outputPath: options.output,
1448
1466
  aspectRatio: options.aspect,
1449
1467
  showThoughts: options.geminiShowThoughts,
1468
+ allowModelFallback: options.geminiFallback,
1450
1469
  }),
1451
1470
  };
1452
1471
  console.log(chalk.dim("Using Gemini web client for browser automation"));
@@ -1516,6 +1535,7 @@ async function runRootCommand(options) {
1516
1535
  outputPath: options.output,
1517
1536
  aspectRatio: options.aspect,
1518
1537
  geminiShowThoughts: options.geminiShowThoughts,
1538
+ geminiAllowModelFallback: options.geminiFallback,
1519
1539
  }, process.cwd(), notifications);
1520
1540
  const liveRunOptions = {
1521
1541
  ...baseRunOptions,
@@ -1539,14 +1559,19 @@ async function runRootCommand(options) {
1539
1559
  });
1540
1560
  const workerPid = !detachAllowed
1541
1561
  ? undefined
1542
- : await launchDetachedSession(sessionMeta.id, async (pid) => {
1543
- lifecycle = buildSessionLifecycle({
1544
- engine,
1545
- detached: true,
1546
- workerPid: pid,
1547
- reattachCommand: `oracle session ${sessionMeta.id}`,
1548
- });
1549
- await sessionStore.updateSession(sessionMeta.id, { lifecycle });
1562
+ : await launchDetachedSession({
1563
+ sessionId: sessionMeta.id,
1564
+ cliEntrypoint: CLI_ENTRYPOINT,
1565
+ env: buildDetachedPerfTraceEnv(process.env, perfTraceArgs.value, sessionMeta.id),
1566
+ prepare: async (pid) => {
1567
+ lifecycle = buildSessionLifecycle({
1568
+ engine,
1569
+ detached: true,
1570
+ workerPid: pid,
1571
+ reattachCommand: `oracle session ${sessionMeta.id}`,
1572
+ });
1573
+ await sessionStore.updateSession(sessionMeta.id, { lifecycle });
1574
+ },
1550
1575
  }).catch((error) => {
1551
1576
  const message = error instanceof Error ? error.message : String(error);
1552
1577
  console.log(chalk.yellow(`Unable to detach session runner (${message}). Running inline...`));
@@ -1634,42 +1659,6 @@ async function runInteractiveSession(sessionMeta, runOptions, mode, browserConfi
1634
1659
  stream.end();
1635
1660
  }
1636
1661
  }
1637
- async function launchDetachedSession(sessionId, prepare) {
1638
- return new Promise((resolve, reject) => {
1639
- try {
1640
- const args = ["--", CLI_ENTRYPOINT, "--exec-session", sessionId];
1641
- const env = {
1642
- ...buildDetachedPerfTraceEnv(process.env, perfTraceArgs.value, sessionId),
1643
- ORACLE_DETACHED_START_GATE: "1",
1644
- };
1645
- const child = spawn(process.execPath, args, {
1646
- detached: true,
1647
- stdio: ["pipe", "ignore", "ignore"],
1648
- env,
1649
- });
1650
- child.once("error", reject);
1651
- child.once("spawn", async () => {
1652
- if (child.pid === undefined) {
1653
- reject(new Error("Detached session worker started without a process ID."));
1654
- return;
1655
- }
1656
- try {
1657
- await prepare(child.pid);
1658
- child.stdin.end("ready\n");
1659
- child.unref();
1660
- resolve(child.pid);
1661
- }
1662
- catch (error) {
1663
- child.kill();
1664
- reject(error);
1665
- }
1666
- });
1667
- }
1668
- catch (error) {
1669
- reject(error);
1670
- }
1671
- });
1672
- }
1673
1662
  async function waitForDetachedStartGate() {
1674
1663
  if (process.env.ORACLE_DETACHED_START_GATE !== "1") {
1675
1664
  return;
@@ -1794,6 +1783,7 @@ async function restartSession(sessionId, options) {
1794
1783
  outputPath: storedOptions.outputPath,
1795
1784
  aspectRatio: storedOptions.aspectRatio,
1796
1785
  showThoughts: storedOptions.geminiShowThoughts,
1786
+ allowModelFallback: storedOptions.geminiAllowModelFallback,
1797
1787
  }),
1798
1788
  };
1799
1789
  console.log(chalk.dim("Using Gemini web client for browser automation"));
@@ -1820,6 +1810,7 @@ async function restartSession(sessionId, options) {
1820
1810
  outputPath: storedOptions.outputPath,
1821
1811
  aspectRatio: storedOptions.aspectRatio,
1822
1812
  geminiShowThoughts: storedOptions.geminiShowThoughts,
1813
+ geminiAllowModelFallback: storedOptions.geminiAllowModelFallback,
1823
1814
  }, cwd, notifications, sessionId);
1824
1815
  const liveRunOptions = {
1825
1816
  ...runOptions,
@@ -1843,14 +1834,19 @@ async function restartSession(sessionId, options) {
1843
1834
  });
1844
1835
  const workerPid = !detachAllowed
1845
1836
  ? undefined
1846
- : await launchDetachedSession(sessionMeta.id, async (pid) => {
1847
- lifecycle = buildSessionLifecycle({
1848
- engine,
1849
- detached: true,
1850
- workerPid: pid,
1851
- reattachCommand: `oracle session ${sessionMeta.id}`,
1852
- });
1853
- await sessionStore.updateSession(sessionMeta.id, { lifecycle });
1837
+ : await launchDetachedSession({
1838
+ sessionId: sessionMeta.id,
1839
+ cliEntrypoint: CLI_ENTRYPOINT,
1840
+ env: buildDetachedPerfTraceEnv(process.env, perfTraceArgs.value, sessionMeta.id),
1841
+ prepare: async (pid) => {
1842
+ lifecycle = buildSessionLifecycle({
1843
+ engine,
1844
+ detached: true,
1845
+ workerPid: pid,
1846
+ reattachCommand: `oracle session ${sessionMeta.id}`,
1847
+ });
1848
+ await sessionStore.updateSession(sessionMeta.id, { lifecycle });
1849
+ },
1854
1850
  }).catch((error) => {
1855
1851
  const message = error instanceof Error ? error.message : String(error);
1856
1852
  console.log(chalk.yellow(`Unable to detach session runner (${message}). Running inline...`));
@@ -2038,6 +2034,12 @@ program.action(async function () {
2038
2034
  await runRootCommand(options);
2039
2035
  });
2040
2036
  async function main() {
2037
+ // npx runs the default binary; let the MCP transport own the alias lifetime.
2038
+ if (process.argv[2] === "oracle-mcp") {
2039
+ const { startMcpServer } = await import("../src/mcp/server.js");
2040
+ await startMcpServer();
2041
+ return;
2042
+ }
2041
2043
  if (perfTraceArgs.error) {
2042
2044
  console.error(`error: ${perfTraceArgs.error}`);
2043
2045
  console.error("(use --help for usage)");
File without changes
@@ -124,7 +124,9 @@ function buildArchiveConversationExpression() {
124
124
  label.includes('conversation options') ||
125
125
  label.includes('open menu') ||
126
126
  label.includes('więcej') ||
127
- label.includes('opcje')
127
+ label.includes('opcje') ||
128
+ label === 'その他' ||
129
+ label.includes('会話オプション')
128
130
  );
129
131
  const headerCandidates = labelled
130
132
  .filter(({ rect }) => rect.top < 180 && rect.right > window.innerWidth - 420)
@@ -144,8 +146,17 @@ function buildArchiveConversationExpression() {
144
146
  return candidates.find((element) => {
145
147
  const label = labelFor(element);
146
148
  if (!label) return false;
147
- if (label.includes('unarchive') || label.includes('restore')) return false;
148
- return label.includes('archive') || label.includes('archiwizuj');
149
+ if (
150
+ label.includes('unarchive') ||
151
+ label.includes('restore') ||
152
+ label.includes('アーカイブを解除')
153
+ ) return false;
154
+ return (
155
+ label.includes('archive') ||
156
+ label.includes('archiwizuj') ||
157
+ label === 'アーカイブ' ||
158
+ label.includes('アーカイブする')
159
+ );
149
160
  }) ?? null;
150
161
  };
151
162
  const findArchiveConfirmationButton = () => {
@@ -154,8 +165,18 @@ function buildArchiveConversationExpression() {
154
165
  return candidates.find((element) => {
155
166
  const label = labelFor(element);
156
167
  if (!label) return false;
157
- if (label.includes('unarchive') || label.includes('restore')) return false;
158
- return label === 'archive' || label === 'archiwizuj' || label.includes('archive conversation');
168
+ if (
169
+ label.includes('unarchive') ||
170
+ label.includes('restore') ||
171
+ label.includes('アーカイブを解除')
172
+ ) return false;
173
+ return (
174
+ label === 'archive' ||
175
+ label === 'archiwizuj' ||
176
+ label.includes('archive conversation') ||
177
+ label === 'アーカイブ' ||
178
+ label.includes('アーカイブする')
179
+ );
159
180
  }) ?? null;
160
181
  };
161
182
  const hasUnarchiveMenuItem = () => {
@@ -166,7 +187,8 @@ function buildArchiveConversationExpression() {
166
187
  label.includes('unarchive') ||
167
188
  label.includes('restore') ||
168
189
  label.includes('przywróć') ||
169
- label.includes('przywroc')
190
+ label.includes('przywroc') ||
191
+ label.includes('アーカイブを解除')
170
192
  );
171
193
  });
172
194
  };
@@ -180,7 +202,9 @@ function buildArchiveConversationExpression() {
180
202
  visibleText.includes('conversation archived') ||
181
203
  visibleText.includes('chat archived') ||
182
204
  visibleText.includes('zarchiwizowano') ||
183
- visibleText.includes('archiwum')
205
+ visibleText.includes('archiwum') ||
206
+ visibleText.includes('アーカイブしました') ||
207
+ visibleText.includes('アーカイブされました')
184
208
  );
185
209
  };
186
210
  const waitForArchiveConfirmation = async () => {
@@ -1,9 +1,10 @@
1
1
  import { ANSWER_SELECTORS, ASSISTANT_ROLE_SELECTOR, CONVERSATION_TURN_SELECTOR, COPY_BUTTON_SELECTOR, FINISHED_ACTIONS_SELECTOR, STOP_BUTTON_SELECTORS, } from "../constants.js";
2
2
  import { buildConversationTurnListExpression } from "../conversationTurns.js";
3
- import { buildThinkingActivePredicateJs, readThinkingActivity } from "./thinkingStatus.js";
3
+ import { buildThinkingActivePredicateJs, buildThinkingActivityDetailsPredicateJs, readThinkingActivity, } from "./thinkingStatus.js";
4
4
  import { delay } from "../utils.js";
5
5
  import { logDomFailure, logConversationSnapshot, buildConversationDebugExpression, } from "../domDebug.js";
6
6
  import { buildClickDispatcher } from "./domEvents.js";
7
+ import { BrowserAutomationError } from "../../oracle/errors.js";
7
8
  const ASSISTANT_POLL_TIMEOUT_ERROR = "assistant-response-watchdog-timeout";
8
9
  const STOP_CONTROL_SELECTOR = STOP_BUTTON_SELECTORS.join(", ");
9
10
  // Still used by the in-page settle heuristic's length buckets (see buildResponseObserverExpression).
@@ -148,6 +149,14 @@ export function isAnswerNowPlaceholderText(value) {
148
149
  }
149
150
  return sawGate && sawOwner;
150
151
  }
152
+ export function isRetryAssistantUiErrorText(value, retryVisible) {
153
+ if (!retryVisible || typeof value !== "string")
154
+ return false;
155
+ const text = value.toLowerCase().replace(/\s+/g, " ").trim();
156
+ return (text.includes("something went wrong") ||
157
+ text.includes("failed to generate") ||
158
+ text.includes("try again later"));
159
+ }
151
160
  // Exported: the two page expressions below and the tests all consume this one builder,
152
161
  // so the predicate cannot drift between the node side and the renderer.
153
162
  export function buildAnswerNowPlaceholderPredicateJs(fnName) {
@@ -227,6 +236,8 @@ export async function waitForAssistantResponse(Runtime, timeoutMs, logger, minTu
227
236
  throw error;
228
237
  }
229
238
  else if (source === "poll") {
239
+ evaluationPromise.catch(() => undefined);
240
+ await terminateRuntimeExecution(Runtime);
230
241
  throw error;
231
242
  }
232
243
  else if (source === "evaluation") {
@@ -399,6 +410,7 @@ async function parseAssistantEvaluationResult(_Runtime, evaluation, _logger) {
399
410
  result.value &&
400
411
  typeof result.value === "object" &&
401
412
  "text" in result.value) {
413
+ throwIfAssistantUiError(result.value);
402
414
  const html = typeof result.value.html === "string"
403
415
  ? (result.value.html ?? undefined)
404
416
  : undefined;
@@ -631,6 +643,7 @@ export function hasScopedCompletionProof(meta) {
631
643
  }
632
644
  export const buildCompletionVisibilityExpressionForTest = buildCompletionVisibilityExpression;
633
645
  function normalizeAssistantSnapshot(snapshot) {
646
+ throwIfAssistantUiError(snapshot);
634
647
  const text = snapshot?.text ? cleanAssistantText(snapshot.text) : "";
635
648
  if (!text.trim()) {
636
649
  return null;
@@ -655,6 +668,19 @@ function normalizeAssistantSnapshot(snapshot) {
655
668
  },
656
669
  };
657
670
  }
671
+ export function throwIfAssistantUiError(snapshot) {
672
+ if (snapshot?.uiError !== "temporary_unavailable")
673
+ return;
674
+ throw new BrowserAutomationError("ChatGPT could not generate the submitted assistant turn and offered Retry.", {
675
+ stage: "assistant-ui-error",
676
+ code: "chatgpt-ui-warning",
677
+ uiWarning: {
678
+ type: "temporary_unavailable",
679
+ message: "ChatGPT offered Retry for the submitted assistant turn.",
680
+ source: "assistant-turn",
681
+ },
682
+ });
683
+ }
658
684
  function isGeneratedImageAssistantAnswer(answer) {
659
685
  return Boolean(answer?.html?.includes("/backend-api/estuary/content?id=file_"));
660
686
  }
@@ -949,6 +975,8 @@ function buildAssistantExtractor(functionName) {
949
975
  const assistantLiteral = JSON.stringify(ASSISTANT_ROLE_SELECTOR);
950
976
  return `const ${functionName} = () => {
951
977
  ${buildClickDispatcher()}
978
+ ${buildThinkingActivityDetailsPredicateJs("readRetryThinkingActivity")}
979
+ const isRetryAssistantUiError = ${isRetryAssistantUiErrorText.toString()};
952
980
  const ASSISTANT_SELECTOR = ${assistantLiteral};
953
981
  const isAssistantTurn = (node) => {
954
982
  if (!(node instanceof HTMLElement)) return false;
@@ -1014,6 +1042,20 @@ function buildAssistantExtractor(functionName) {
1014
1042
  String(img?.src || '').includes('/backend-api/estuary/content?id=file_')
1015
1043
  );
1016
1044
  const normalizedText = String(text || '').toLowerCase().replace(/\\s+/g, ' ').trim();
1045
+ const retryVisible = Array.from(turn.querySelectorAll('button, [role="button"]')).some((button) => {
1046
+ const label = String(button.innerText || button.textContent || button.getAttribute?.('aria-label') || '')
1047
+ .toLowerCase()
1048
+ .replace(/\\s+/g, ' ')
1049
+ .trim();
1050
+ if (label !== 'retry' || !(button instanceof HTMLElement)) return false;
1051
+ const rect = button.getBoundingClientRect();
1052
+ const style = window.getComputedStyle(button);
1053
+ return rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden' &&
1054
+ !(style.opacity !== '' && Number(style.opacity) === 0);
1055
+ });
1056
+ if (isRetryAssistantUiError(text, retryVisible) && !readRetryThinkingActivity().strong) {
1057
+ return { text, html, messageId, turnId, turnIndex: index, uiError: 'temporary_unavailable' };
1058
+ }
1017
1059
  const imageOnlyChrome =
1018
1060
  !normalizedText ||
1019
1061
  normalizedText === 'edit' ||