@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.
Files changed (70) hide show
  1. package/README.md +72 -337
  2. package/dist/bin/oracle-cli.js +185 -56
  3. package/dist/docs-site/.nojekyll +0 -0
  4. package/dist/docs-site/CNAME +1 -0
  5. package/dist/docs-site/RELEASING.html +410 -0
  6. package/dist/docs-site/agents.html +374 -0
  7. package/dist/docs-site/anthropic.html +368 -0
  8. package/dist/docs-site/bridge.html +416 -0
  9. package/dist/docs-site/browser-mode.html +594 -0
  10. package/dist/docs-site/chromium-forks.html +347 -0
  11. package/dist/docs-site/cli-reference.html +346 -0
  12. package/dist/docs-site/configuration.html +462 -0
  13. package/dist/docs-site/favicon.svg +14 -0
  14. package/dist/docs-site/followup.html +375 -0
  15. package/dist/docs-site/gemini.html +383 -0
  16. package/dist/docs-site/grok.html +325 -0
  17. package/dist/docs-site/index.html +360 -0
  18. package/dist/docs-site/install.html +335 -0
  19. package/dist/docs-site/linux.html +321 -0
  20. package/dist/docs-site/llms.txt +43 -0
  21. package/dist/docs-site/manual-tests.html +596 -0
  22. package/dist/docs-site/mcp.html +391 -0
  23. package/dist/docs-site/multimodel.html +364 -0
  24. package/dist/docs-site/mythical-pro-agents.html +360 -0
  25. package/dist/docs-site/notifier.html +338 -0
  26. package/dist/docs-site/openai-endpoints.html +410 -0
  27. package/dist/docs-site/openrouter.html +344 -0
  28. package/dist/docs-site/quickstart.html +369 -0
  29. package/dist/docs-site/refactor/ux.html +532 -0
  30. package/dist/docs-site/sessions.html +389 -0
  31. package/dist/docs-site/social-card.png +0 -0
  32. package/dist/docs-site/social-card.svg +79 -0
  33. package/dist/docs-site/spec.html +363 -0
  34. package/dist/docs-site/testing.html +320 -0
  35. package/dist/docs-site/tui-debug.html +326 -0
  36. package/dist/docs-site/windows-work.html +324 -0
  37. package/dist/docs-site/windows.html +320 -0
  38. package/dist/scripts/test-browser.js +1 -25
  39. package/dist/src/browser/actions/modelSelection.js +36 -34
  40. package/dist/src/browser/actions/navigation.js +8 -3
  41. package/dist/src/browser/actions/thinkingTime.js +96 -45
  42. package/dist/src/browser/chromeLifecycle.js +8 -37
  43. package/dist/src/browser/index.js +23 -8
  44. package/dist/src/browser/modelDisplay.js +67 -0
  45. package/dist/src/browser/reattach.js +14 -1
  46. package/dist/src/browser/recoverConversation.js +2 -1
  47. package/dist/src/browser/sessionRunner.js +9 -10
  48. package/dist/src/browser/wslHost.js +50 -0
  49. package/dist/src/cli/browserConfig.js +31 -11
  50. package/dist/src/cli/detach.js +21 -4
  51. package/dist/src/cli/dryRun.js +13 -2
  52. package/dist/src/cli/engine.js +2 -2
  53. package/dist/src/cli/options.js +5 -1
  54. package/dist/src/cli/sessionDisplay.js +60 -8
  55. package/dist/src/cli/sessionLifecycle.js +2 -1
  56. package/dist/src/cli/sessionRunner.js +110 -60
  57. package/dist/src/cli/sessionTable.js +5 -1
  58. package/dist/src/cli/tui/index.js +12 -4
  59. package/dist/src/duration.js +3 -0
  60. package/dist/src/gemini-web/client.js +19 -1
  61. package/dist/src/oracle/modelResolver.js +8 -1
  62. package/dist/src/oracle/request.js +9 -2
  63. package/dist/src/oracle/run.js +43 -3
  64. package/dist/src/oracle/thinkingTime.js +9 -3
  65. package/dist/src/sessionManager.js +41 -12
  66. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  67. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  68. package/package.json +17 -17
  69. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  70. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
@@ -21,7 +21,7 @@ import { applyHelpStyling } from "../src/cli/help.js";
21
21
  import { collectPaths, collectModelList, collectTextValues, parseFloatOption, parseIntOption, parseSearchOption, parseThinkingTimeOption, usesDefaultStatusFilters, resolvePreviewMode, normalizeModelOption, normalizeBaseUrl, resolveApiModel, inferModelFromLabel, parseHeartbeatOption, parseTimeoutOption, parseDurationOption, mergePathLikeOptions, dedupePathInputs, } from "../src/cli/options.js";
22
22
  import { copyToClipboard } from "../src/cli/clipboard.js";
23
23
  import { buildMarkdownBundle } from "../src/cli/markdownBundle.js";
24
- import { shouldDetachSession } from "../src/cli/detach.js";
24
+ import { shouldDetachSession, stopDetachedWorker } from "../src/cli/detach.js";
25
25
  import { applyHiddenAliases } from "../src/cli/hiddenAliases.js";
26
26
  import { isMediaFile } from "../src/browser/prompt.js";
27
27
  import { formatCompactNumber } from "../src/cli/format.js";
@@ -227,10 +227,19 @@ program
227
227
  .addOption(new Option("--copy-markdown", "Copy the assembled markdown bundle to the clipboard; pair with --render to print it too.").default(false))
228
228
  .addOption(new Option("--copy").hideHelp().default(false))
229
229
  .option("-s, --slug <words>", "Custom session slug (3-5 words).")
230
- .option("-m, --model <model>", 'Model to target (gpt-5.5-pro default). GPT-5.6 aliases: gpt-5.6 and gpt-5.6-sol (OpenAI API or ChatGPT browser). Also gpt-5.5, gpt-5.4-pro, gpt-5.4, gpt-5.1-pro, gpt-5-pro, gpt-5.1, gpt-5.1-codex API-only, gpt-5.2, gpt-5.2-instant, gpt-5.2-pro, gemini-3.1-flash-lite, gemini-3.5-flash, gemini-3.1-pro, legacy gemini-3-pro, claude-4.6-sonnet, claude-4.1-opus, or ChatGPT labels like "5.5 Pro" / "5.2 Thinking" for browser runs).', normalizeModelOption)
230
+ .option("-m, --model <model>", "Model to target (gpt-5.5-pro default). GPT-5.6 aliases gpt-5.6 and gpt-5.6-sol work with the OpenAI API or ChatGPT browser. Browser mode also supports current GPT-5.5/GPT-5.4 targets and legacy Pro aliases; retired GPT-5.2 base/Instant/Thinking aliases are API-only. Other API targets include gpt-5.1-codex, gpt-5.2, gpt-5.2-instant, Gemini, Claude, and custom model IDs.", normalizeModelOption)
231
231
  .addOption(new Option("--models <models>", 'Comma-separated API model list to query in parallel (e.g., "gpt-5.5-pro,gemini-3-pro").')
232
232
  .argParser(collectModelList)
233
233
  .default([]))
234
+ .addOption(new Option("--reasoning-effort <effort>", "Reasoning effort for GPT-5.6 API models.").choices([
235
+ "none",
236
+ "low",
237
+ "medium",
238
+ "high",
239
+ "xhigh",
240
+ "max",
241
+ ]))
242
+ .addOption(new Option("--reasoning-mode <mode>", 'Responses API reasoning execution mode for GPT-5.6 models ("standard" or "pro").').choices(["standard", "pro"]))
234
243
  .addOption(new Option("-e, --engine <mode>", "Execution engine (api | browser). Browser engine: GPT models automate ChatGPT; Gemini models use a cookie-based client for gemini.google.com. If omitted, oracle picks api when OPENAI_API_KEY is set, otherwise browser.").choices(["api", "browser"]))
235
244
  .addOption(new Option("--mode <mode>", "Alias for --engine (api | browser).")
236
245
  .choices(["api", "browser"])
@@ -330,7 +339,7 @@ program
330
339
  .addOption(new Option("--browser-hide-window", "Hide the Chrome window after launch (macOS headful only).").hideHelp())
331
340
  .addOption(new Option("--browser-keep-browser", "Keep Chrome running after completion.").hideHelp())
332
341
  .addOption(new Option("--browser-model-strategy <mode>", "ChatGPT model picker strategy: select (default) switches to the requested model, current keeps the active model, ignore skips the picker entirely.").choices(["select", "current", "ignore"]))
333
- .addOption(new Option("--browser-thinking-time <level>", "Thinking time intensity for Thinking/Pro models: light, standard, extended, heavy, or ChatGPT UI aliases.")
342
+ .addOption(new Option("--browser-thinking-time <level>", "Thinking time intensity for Thinking/Pro models: light, standard, extended, extra-high (Extra High), heavy (Pro), or ChatGPT UI aliases.")
334
343
  .argParser(parseThinkingTimeOption)
335
344
  .hideHelp())
336
345
  .addOption(new Option("--browser-research <mode>", "Browser research mode: deep activates ChatGPT Deep Research.").choices(["off", "deep"]))
@@ -659,6 +668,8 @@ function buildRunOptions(options, overrides = {}) {
659
668
  prompt: options.prompt,
660
669
  model: options.model,
661
670
  models: overrides.models ?? options.models,
671
+ reasoningEffort: overrides.reasoningEffort ?? options.reasoningEffort,
672
+ reasoningMode: overrides.reasoningMode ?? options.reasoningMode,
662
673
  previousResponseId: overrides.previousResponseId ?? options.previousResponseId,
663
674
  browserResumeConversationUrl: overrides.browserResumeConversationUrl ?? options.browserResumeConversationUrl,
664
675
  effectiveModelId: overrides.effectiveModelId ?? options.effectiveModelId ?? options.model,
@@ -902,6 +913,8 @@ function buildRunOptionsFromMetadata(metadata) {
902
913
  browserResumeConversationUrl: stored.browserResumeConversationUrl,
903
914
  effectiveModelId: stored.effectiveModelId ?? stored.model,
904
915
  modelOverrides: stored.modelOverrides,
916
+ reasoningEffort: stored.reasoningEffort,
917
+ reasoningMode: stored.reasoningMode,
905
918
  file: stored.file ?? [],
906
919
  maxFileSizeBytes: stored.maxFileSizeBytes,
907
920
  slug: stored.slug,
@@ -929,6 +942,8 @@ function buildRunOptionsFromMetadata(metadata) {
929
942
  browserInlineFiles: stored.browserInlineFiles,
930
943
  browserBundleFiles: stored.browserBundleFiles,
931
944
  browserBundleFormat: stored.browserBundleFormat,
945
+ generateImage: stored.generateImage,
946
+ outputPath: stored.outputPath,
932
947
  browserFollowUps: stored.browserFollowUps,
933
948
  background: stored.background,
934
949
  renderPlain: stored.renderPlain,
@@ -1213,6 +1228,7 @@ async function runRootCommand(options) {
1213
1228
  return;
1214
1229
  }
1215
1230
  if (options.execSession) {
1231
+ await waitForDetachedStartGate();
1216
1232
  await executeSession(options.execSession);
1217
1233
  return;
1218
1234
  }
@@ -1296,6 +1312,12 @@ async function runRootCommand(options) {
1296
1312
  }
1297
1313
  }
1298
1314
  const activeModel = resolvedOptions.model;
1315
+ if (options.reasoningMode && engine !== "api") {
1316
+ throw new Error("--reasoning-mode requires --engine api.");
1317
+ }
1318
+ if (options.reasoningEffort && engine !== "api") {
1319
+ throw new Error("--reasoning-effort requires --engine api.");
1320
+ }
1299
1321
  const browserFollowUpCount = options.browserFollowUp?.filter((entry) => entry.trim().length > 0).length ?? 0;
1300
1322
  if (engine !== "browser" && browserFollowUpCount > 0) {
1301
1323
  throw new Error("--browser-follow-up requires --engine browser.");
@@ -1445,13 +1467,16 @@ async function runRootCommand(options) {
1445
1467
  }, {});
1446
1468
  return;
1447
1469
  }
1448
- // Decide whether to block until completion:
1470
+ // Decide whether the original CLI stays attached until completion:
1449
1471
  // - explicit --wait / --no-wait wins
1450
- // - otherwise block for fast models (gpt-5.1, browser) and detach by default for pro API runs
1472
+ // - otherwise stay attached for fast models and browser runs
1473
+ // Local Pro browser work may still use a detached worker so CLI interruption
1474
+ // cannot terminate the browser controller.
1451
1475
  let waitPreference = resolveWaitFlag({
1452
1476
  waitFlag: options.wait,
1453
1477
  model: activeModel,
1454
1478
  engine,
1479
+ reasoningMode: resolvedOptions.reasoningMode,
1455
1480
  });
1456
1481
  if (remoteHost && waitPreference === false) {
1457
1482
  console.log(chalk.dim("Remote browser runs require --wait; ignoring --no-wait."));
@@ -1498,22 +1523,34 @@ async function runRootCommand(options) {
1498
1523
  : shouldDetachSession({
1499
1524
  engine,
1500
1525
  model: activeModel,
1526
+ reasoningMode: resolvedOptions.reasoningMode,
1501
1527
  waitPreference,
1502
1528
  disableDetachEnv,
1503
1529
  });
1504
- const detached = !detachAllowed
1505
- ? false
1506
- : await launchDetachedSession(sessionMeta.id).catch((error) => {
1507
- const message = error instanceof Error ? error.message : String(error);
1508
- console.log(chalk.yellow(`Unable to detach session runner (${message}). Running inline...`));
1509
- return false;
1510
- });
1511
- const lifecycle = buildSessionLifecycle({
1530
+ let lifecycle = buildSessionLifecycle({
1512
1531
  engine,
1513
- detached,
1532
+ detached: false,
1514
1533
  reattachCommand: `oracle session ${sessionMeta.id}`,
1515
1534
  });
1516
- await sessionStore.updateSession(sessionMeta.id, { lifecycle });
1535
+ const workerPid = !detachAllowed
1536
+ ? undefined
1537
+ : await launchDetachedSession(sessionMeta.id, async (pid) => {
1538
+ lifecycle = buildSessionLifecycle({
1539
+ engine,
1540
+ detached: true,
1541
+ workerPid: pid,
1542
+ reattachCommand: `oracle session ${sessionMeta.id}`,
1543
+ });
1544
+ await sessionStore.updateSession(sessionMeta.id, { lifecycle });
1545
+ }).catch((error) => {
1546
+ const message = error instanceof Error ? error.message : String(error);
1547
+ console.log(chalk.yellow(`Unable to detach session runner (${message}). Running inline...`));
1548
+ return undefined;
1549
+ });
1550
+ const detached = workerPid !== undefined;
1551
+ if (!detached) {
1552
+ await sessionStore.updateSession(sessionMeta.id, { lifecycle });
1553
+ }
1517
1554
  const sessionWithLifecycle = { ...sessionMeta, lifecycle };
1518
1555
  if (!waitPreference) {
1519
1556
  if (!detached) {
@@ -1533,8 +1570,7 @@ async function runRootCommand(options) {
1533
1570
  }
1534
1571
  if (detached) {
1535
1572
  console.log(chalk.blue(`Reattach via: oracle session ${sessionMeta.id}`));
1536
- const { attachSession } = await import("../src/cli/sessionDisplay.js");
1537
- await attachSession(sessionMeta.id, { suppressMetadata: true });
1573
+ await attachToDetachedSession(sessionMeta.id, workerPid);
1538
1574
  }
1539
1575
  }
1540
1576
  async function runInteractiveSession(sessionMeta, runOptions, mode, browserConfig, showReattachHint = true, notifications, userConfig, suppressSummary = false, browserDeps, cwd = process.cwd()) {
@@ -1593,20 +1629,35 @@ async function runInteractiveSession(sessionMeta, runOptions, mode, browserConfi
1593
1629
  stream.end();
1594
1630
  }
1595
1631
  }
1596
- async function launchDetachedSession(sessionId) {
1632
+ async function launchDetachedSession(sessionId, prepare) {
1597
1633
  return new Promise((resolve, reject) => {
1598
1634
  try {
1599
1635
  const args = ["--", CLI_ENTRYPOINT, "--exec-session", sessionId];
1600
- const env = buildDetachedPerfTraceEnv(process.env, perfTraceArgs.value, sessionId);
1636
+ const env = {
1637
+ ...buildDetachedPerfTraceEnv(process.env, perfTraceArgs.value, sessionId),
1638
+ ORACLE_DETACHED_START_GATE: "1",
1639
+ };
1601
1640
  const child = spawn(process.execPath, args, {
1602
1641
  detached: true,
1603
- stdio: "ignore",
1642
+ stdio: ["pipe", "ignore", "ignore"],
1604
1643
  env,
1605
1644
  });
1606
1645
  child.once("error", reject);
1607
- child.once("spawn", () => {
1608
- child.unref();
1609
- resolve(true);
1646
+ child.once("spawn", async () => {
1647
+ if (child.pid === undefined) {
1648
+ reject(new Error("Detached session worker started without a process ID."));
1649
+ return;
1650
+ }
1651
+ try {
1652
+ await prepare(child.pid);
1653
+ child.stdin.end("ready\n");
1654
+ child.unref();
1655
+ resolve(child.pid);
1656
+ }
1657
+ catch (error) {
1658
+ child.kill();
1659
+ reject(error);
1660
+ }
1610
1661
  });
1611
1662
  }
1612
1663
  catch (error) {
@@ -1614,6 +1665,49 @@ async function launchDetachedSession(sessionId) {
1614
1665
  }
1615
1666
  });
1616
1667
  }
1668
+ async function waitForDetachedStartGate() {
1669
+ if (process.env.ORACLE_DETACHED_START_GATE !== "1") {
1670
+ return;
1671
+ }
1672
+ const chunks = [];
1673
+ await new Promise((resolve, reject) => {
1674
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
1675
+ process.stdin.once("end", resolve);
1676
+ process.stdin.once("error", reject);
1677
+ process.stdin.resume();
1678
+ });
1679
+ if (Buffer.concat(chunks).toString("utf8") !== "ready\n") {
1680
+ throw new Error("Detached session worker start gate closed before lifecycle handoff.");
1681
+ }
1682
+ }
1683
+ async function attachToDetachedSession(sessionId, workerPid) {
1684
+ let cancelled = false;
1685
+ const cancelWorker = () => {
1686
+ cancelled = true;
1687
+ try {
1688
+ stopDetachedWorker(workerPid);
1689
+ }
1690
+ catch (error) {
1691
+ const message = error instanceof Error ? error.message : String(error);
1692
+ console.error(chalk.red(`Unable to stop detached worker ${workerPid}: ${message}`));
1693
+ }
1694
+ };
1695
+ process.once("SIGINT", cancelWorker);
1696
+ try {
1697
+ const { attachSession } = await import("../src/cli/sessionDisplay.js");
1698
+ await attachSession(sessionId, {
1699
+ suppressMetadata: true,
1700
+ renderPrompt: false,
1701
+ propagateFailure: true,
1702
+ });
1703
+ }
1704
+ finally {
1705
+ process.off("SIGINT", cancelWorker);
1706
+ if (cancelled) {
1707
+ process.exitCode = 130;
1708
+ }
1709
+ }
1710
+ }
1617
1711
  async function restartSession(sessionId, options) {
1618
1712
  const metadata = await sessionStore.readSession(sessionId);
1619
1713
  if (!metadata) {
@@ -1657,6 +1751,7 @@ async function restartSession(sessionId, options) {
1657
1751
  storedPreference: storedOptions.waitPreference,
1658
1752
  model: runOptions.model,
1659
1753
  engine,
1754
+ reasoningMode: runOptions.reasoningMode,
1660
1755
  });
1661
1756
  const remoteConfig = resolveRemoteServiceConfig({
1662
1757
  cliHost: options.remoteHost,
@@ -1732,22 +1827,34 @@ async function restartSession(sessionId, options) {
1732
1827
  : shouldDetachSession({
1733
1828
  engine,
1734
1829
  model: runOptions.model,
1830
+ reasoningMode: runOptions.reasoningMode,
1735
1831
  waitPreference,
1736
1832
  disableDetachEnv,
1737
1833
  });
1738
- const detached = !detachAllowed
1739
- ? false
1740
- : await launchDetachedSession(sessionMeta.id).catch((error) => {
1741
- const message = error instanceof Error ? error.message : String(error);
1742
- console.log(chalk.yellow(`Unable to detach session runner (${message}). Running inline...`));
1743
- return false;
1744
- });
1745
- const lifecycle = buildSessionLifecycle({
1834
+ let lifecycle = buildSessionLifecycle({
1746
1835
  engine,
1747
- detached,
1836
+ detached: false,
1748
1837
  reattachCommand: `oracle session ${sessionMeta.id}`,
1749
1838
  });
1750
- await sessionStore.updateSession(sessionMeta.id, { lifecycle });
1839
+ const workerPid = !detachAllowed
1840
+ ? undefined
1841
+ : await launchDetachedSession(sessionMeta.id, 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
+ }).catch((error) => {
1850
+ const message = error instanceof Error ? error.message : String(error);
1851
+ console.log(chalk.yellow(`Unable to detach session runner (${message}). Running inline...`));
1852
+ return undefined;
1853
+ });
1854
+ const detached = workerPid !== undefined;
1855
+ if (!detached) {
1856
+ await sessionStore.updateSession(sessionMeta.id, { lifecycle });
1857
+ }
1751
1858
  const sessionWithLifecycle = { ...sessionMeta, lifecycle };
1752
1859
  if (!waitPreference) {
1753
1860
  if (!detached) {
@@ -1767,24 +1874,27 @@ async function restartSession(sessionId, options) {
1767
1874
  }
1768
1875
  if (detached) {
1769
1876
  console.log(chalk.blue(`Reattach via: oracle session ${sessionMeta.id}`));
1770
- const { attachSession } = await import("../src/cli/sessionDisplay.js");
1771
- await attachSession(sessionMeta.id, { suppressMetadata: true });
1877
+ await attachToDetachedSession(sessionMeta.id, workerPid);
1772
1878
  }
1773
1879
  }
1774
1880
  async function executeSession(sessionId) {
1775
- const metadata = await sessionStore.readSession(sessionId);
1776
- if (!metadata) {
1777
- console.error(chalk.red(`No session found with ID ${sessionId}`));
1778
- process.exitCode = 1;
1779
- return;
1780
- }
1781
- const runOptions = buildRunOptionsFromMetadata(metadata);
1782
- const sessionMode = getSessionMode(metadata);
1783
- const browserConfig = getBrowserConfigFromMetadata(metadata);
1784
- const { logLine, writeChunk, stream } = sessionStore.createLogWriter(sessionId);
1785
- const userConfig = (await loadUserConfig()).config;
1786
- const notifications = deriveNotificationSettingsFromMetadata(metadata, process.env, userConfig.notify);
1881
+ let metadata = null;
1882
+ let writer = null;
1787
1883
  try {
1884
+ metadata = await sessionStore.readSession(sessionId);
1885
+ if (!metadata) {
1886
+ throw new Error(`No session found with ID ${sessionId}`);
1887
+ }
1888
+ if (process.env.ORACLE_DETACHED_START_GATE === "1" &&
1889
+ metadata.lifecycle?.workerPid !== process.pid) {
1890
+ throw new Error("Detached session worker started without a matching lifecycle handoff.");
1891
+ }
1892
+ const runOptions = buildRunOptionsFromMetadata(metadata);
1893
+ const sessionMode = getSessionMode(metadata);
1894
+ const browserConfig = getBrowserConfigFromMetadata(metadata);
1895
+ writer = sessionStore.createLogWriter(sessionId);
1896
+ const userConfig = (await loadUserConfig()).config;
1897
+ const notifications = deriveNotificationSettingsFromMetadata(metadata, process.env, userConfig.notify);
1788
1898
  const { performSessionRun } = await import("../src/cli/sessionRunner.js");
1789
1899
  await performSessionRun({
1790
1900
  sessionMeta: metadata,
@@ -1792,17 +1902,36 @@ async function executeSession(sessionId) {
1792
1902
  mode: sessionMode,
1793
1903
  browserConfig,
1794
1904
  cwd: metadata.cwd ?? process.cwd(),
1795
- log: logLine,
1796
- write: writeChunk,
1905
+ log: writer.logLine,
1906
+ write: writer.writeChunk,
1797
1907
  version: VERSION,
1798
1908
  notifications,
1799
1909
  });
1800
1910
  }
1801
- catch {
1802
- // Errors are already logged to the session log; keep quiet to mirror stored-session behavior.
1911
+ catch (error) {
1912
+ process.exitCode = 1;
1913
+ const message = error instanceof Error ? error.message : String(error);
1914
+ if (!metadata) {
1915
+ console.error(chalk.red(message));
1916
+ return;
1917
+ }
1918
+ writer?.logLine(`ERROR: Detached session worker failed: ${message}`);
1919
+ const latest = await sessionStore.readSession(sessionId).catch(() => null);
1920
+ if (latest && !["completed", "partial", "error"].includes(latest.status)) {
1921
+ await sessionStore.updateSession(sessionId, {
1922
+ status: "error",
1923
+ completedAt: new Date().toISOString(),
1924
+ errorMessage: message,
1925
+ response: { status: "error" },
1926
+ error: {
1927
+ category: "internal",
1928
+ message,
1929
+ },
1930
+ });
1931
+ }
1803
1932
  }
1804
1933
  finally {
1805
- stream.end();
1934
+ writer?.stream.end();
1806
1935
  }
1807
1936
  }
1808
1937
  function printDebugHelp(cliName) {
@@ -1871,21 +2000,21 @@ function printDebugOptionGroup(entries) {
1871
2000
  console.log(` ${label}${description}`);
1872
2001
  });
1873
2002
  }
1874
- function resolveWaitFlag({ waitFlag, model, engine, }) {
2003
+ function resolveWaitFlag({ waitFlag, model, engine, reasoningMode, }) {
1875
2004
  if (waitFlag === true)
1876
2005
  return true;
1877
2006
  if (waitFlag === false)
1878
2007
  return false;
1879
- return defaultWaitPreference(model, engine);
2008
+ return defaultWaitPreference(model, engine, reasoningMode);
1880
2009
  }
1881
- function resolveRestartWaitPreference({ waitFlag, storedPreference, model, engine, }) {
2010
+ function resolveRestartWaitPreference({ waitFlag, storedPreference, model, engine, reasoningMode, }) {
1882
2011
  if (waitFlag === true)
1883
2012
  return true;
1884
2013
  if (waitFlag === false)
1885
2014
  return false;
1886
2015
  if (typeof storedPreference === "boolean")
1887
2016
  return storedPreference;
1888
- return defaultWaitPreference(model, engine);
2017
+ return defaultWaitPreference(model, engine, reasoningMode);
1889
2018
  }
1890
2019
  function resolveEffectiveModelIdForRun(model, stored) {
1891
2020
  if (stored)
File without changes
@@ -0,0 +1 @@
1
+ askoracle.sh