@steipete/oracle 0.16.1 → 0.17.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 (69) hide show
  1. package/README.md +72 -337
  2. package/dist/bin/oracle-cli.js +184 -55
  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 +39 -3
  42. package/dist/src/browser/chromeLifecycle.js +2 -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 +4 -0
  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/sessionManager.js +41 -12
  65. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  66. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  67. package/package.json +15 -15
  68. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  69. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
@@ -0,0 +1,320 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Windows compatibility notes — oracle</title>
7
+ <meta name="description" content="Windows compatibility notes — oracle CLI documentation.">
8
+ <link rel="canonical" href="https://askoracle.sh/windows.html">
9
+ <meta property="og:type" content="website">
10
+ <meta property="og:site_name" content="oracle">
11
+ <meta property="og:title" content="Windows compatibility notes — oracle">
12
+ <meta property="og:description" content="Windows compatibility notes — oracle CLI documentation.">
13
+ <meta property="og:url" content="https://askoracle.sh/windows.html">
14
+ <meta property="og:image" content="https://askoracle.sh/social-card.png">
15
+ <meta property="og:image:width" content="1200">
16
+ <meta property="og:image:height" content="630">
17
+ <meta name="twitter:card" content="summary_large_image">
18
+ <meta name="twitter:title" content="Windows compatibility notes — oracle">
19
+ <meta name="twitter:description" content="Windows compatibility notes — oracle CLI documentation.">
20
+ <meta name="twitter:image" content="https://askoracle.sh/social-card.png">
21
+ <link rel="icon" href="favicon.svg" type="image/svg+xml">
22
+ <link rel="preconnect" href="https://fonts.googleapis.com">
23
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
24
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
25
+ <script>(function(){var s;try{s=localStorage.getItem('theme')}catch(e){}var d=window.matchMedia&&matchMedia('(prefers-color-scheme: dark)').matches;document.documentElement.dataset.theme=s||(d?'dark':'light')})();</script>
26
+ <style>
27
+ :root{
28
+ --ink:#0e0a1f;
29
+ --text:#1d1832;
30
+ --muted:#6b6582;
31
+ --subtle:#9c95b5;
32
+ --bg:#fafaff;
33
+ --paper:#ffffff;
34
+ --accent:#7c3aed;
35
+ --accent-soft:rgba(124,58,237,.10);
36
+ --accent-strong:#5b21b6;
37
+ --accent-2:#0ea5e9;
38
+ --accent-glow:#a78bfa;
39
+ --line:#e7e3f0;
40
+ --line-soft:#f2eff8;
41
+ --code-bg:#0c0a1a;
42
+ --code-fg:#e9e6f5;
43
+ --code-inline-fg:#1c1734;
44
+ --pill-border:#dcd5ec;
45
+ --shadow-card:0 4px 14px rgba(14,10,31,.08);
46
+ --scrollbar:#cbc4dc;
47
+ }
48
+ :root[data-theme="dark"]{
49
+ --ink:#f5f2ff;
50
+ --text:#cec7e4;
51
+ --muted:#9189a8;
52
+ --subtle:#6a6383;
53
+ --bg:#0a0815;
54
+ --paper:#15102a;
55
+ --accent:#a78bfa;
56
+ --accent-soft:rgba(167,139,250,.18);
57
+ --accent-strong:#c4b5fd;
58
+ --accent-2:#38bdf8;
59
+ --accent-glow:#c4b5fd;
60
+ --line:#272140;
61
+ --line-soft:#1c1734;
62
+ --code-bg:#06040d;
63
+ --code-fg:#e9e6f5;
64
+ --code-inline-fg:#e9e6f5;
65
+ --pill-border:#2c2549;
66
+ --shadow-card:0 4px 18px rgba(0,0,0,.45);
67
+ --scrollbar:#3a3358;
68
+ }
69
+ :root{color-scheme:light}
70
+ :root[data-theme="dark"]{color-scheme:dark}
71
+ *{box-sizing:border-box}
72
+ html{scroll-behavior:smooth;scroll-padding-top:24px}
73
+ body{margin:0;background:var(--bg);color:var(--text);font-family:"Inter",ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;line-height:1.65;overflow-x:hidden;-webkit-font-smoothing:antialiased;font-feature-settings:"cv02","cv03","cv04","cv11";transition:background-color .18s,color .18s}
74
+ ::selection{background:var(--accent);color:#fff}
75
+ a{color:var(--accent);text-decoration:none;transition:color .12s}
76
+ a:hover{text-decoration:underline;text-underline-offset:.2em}
77
+ .shell{display:grid;grid-template-columns:268px minmax(0,1fr);min-height:100vh}
78
+ .sidebar{position:sticky;top:0;height:100vh;overflow:auto;padding:24px 22px;background:var(--paper);border-right:1px solid var(--line);scrollbar-width:thin;scrollbar-color:var(--line) transparent;transition:background-color .18s,border-color .18s}
79
+ .sidebar::-webkit-scrollbar{width:6px}
80
+ .sidebar::-webkit-scrollbar-thumb{background:var(--line);border-radius:6px}
81
+ .sidebar-head{display:flex;align-items:center;gap:10px;margin-bottom:24px}
82
+ .brand{display:flex;align-items:center;gap:11px;color:var(--ink);text-decoration:none;flex:1;min-width:0}
83
+ .brand:hover{text-decoration:none}
84
+ .brand .mark{flex:0 0 30px;width:30px;height:30px;border-radius:50%;background:radial-gradient(circle at 32% 32%,#fff 0,#e0d4ff 22%,#7c3aed 60%,#1e1240 100%);box-shadow:0 0 0 1px rgba(124,58,237,.35),0 6px 18px rgba(124,58,237,.35);position:relative}
85
+ .brand .mark::after{content:"";position:absolute;left:50%;top:50%;width:9px;height:9px;border-radius:50%;background:#0c0820;transform:translate(-50%,-50%)}
86
+ .brand strong{display:block;font-size:1.05rem;line-height:1.1;font-weight:600;letter-spacing:0;color:var(--ink)}
87
+ .brand small{display:block;color:var(--muted);font-size:.74rem;margin-top:3px;font-weight:400}
88
+ .theme-toggle{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:34px;height:34px;border-radius:8px;border:1px solid var(--line);background:var(--paper);color:var(--muted);cursor:pointer;padding:0;transition:border-color .15s,color .15s,background-color .15s,transform .12s}
89
+ .theme-toggle:hover{border-color:var(--ink);color:var(--ink)}
90
+ .theme-toggle:active{transform:scale(.94)}
91
+ .theme-toggle svg{width:16px;height:16px;display:block}
92
+ .theme-icon-sun{display:none}
93
+ :root[data-theme="dark"] .theme-icon-sun{display:block}
94
+ :root[data-theme="dark"] .theme-icon-moon{display:none}
95
+ .search{display:block;margin:0 0 22px}
96
+ .search span{display:block;color:var(--muted);font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:0;margin-bottom:7px}
97
+ .search input{width:100%;border:1px solid var(--line);background:var(--paper);border-radius:8px;padding:9px 12px;font:inherit;font-size:.9rem;color:var(--text);outline:none;transition:border-color .15s,box-shadow .15s,background-color .18s}
98
+ .search input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
99
+ nav section{margin:0 0 18px}
100
+ nav h2{font-size:.68rem;color:var(--muted);text-transform:uppercase;letter-spacing:0;margin:0 0 6px;font-weight:600}
101
+ .nav-link{display:block;color:var(--text);text-decoration:none;border-radius:6px;padding:5px 10px;margin:1px 0;font-size:.9rem;line-height:1.4;transition:background .12s,color .12s}
102
+ .nav-link:hover{background:var(--line-soft);color:var(--ink);text-decoration:none}
103
+ .nav-link.active{background:var(--accent-soft);color:var(--accent);font-weight:600}
104
+ main{min-width:0;padding:32px clamp(20px,4.5vw,56px) 80px;max-width:1180px;margin:0 auto;width:100%}
105
+ .hero{display:flex;align-items:flex-end;justify-content:space-between;gap:22px;border-bottom:1px solid var(--line);padding:8px 0 22px;margin-bottom:8px;flex-wrap:wrap}
106
+ .hero-text{min-width:0;flex:1 1 320px}
107
+ .eyebrow{margin:0 0 8px;color:var(--muted);font-weight:600;text-transform:uppercase;letter-spacing:0;font-size:.7rem}
108
+ .hero h1{font-size:2.25rem;line-height:1.1;letter-spacing:0;margin:0;font-weight:700;color:var(--ink)}
109
+ .hero-meta{display:flex;gap:8px;flex:0 0 auto;flex-wrap:wrap}
110
+ .repo,.edit,.btn-ghost{border:1px solid var(--line);color:var(--text);text-decoration:none;border-radius:7px;padding:6px 11px;font-weight:500;font-size:.83rem;background:var(--paper);transition:border-color .15s,color .15s,background .15s}
111
+ .repo:hover,.edit:hover,.btn-ghost:hover{border-color:var(--ink);color:var(--ink);text-decoration:none}
112
+ .edit{color:var(--muted)}
113
+ .home-hero{position:relative;padding:24px 0 32px;margin-bottom:8px;border-bottom:1px solid var(--line)}
114
+ .home-hero>*{position:relative;z-index:1}
115
+ .home-hero h1{font-size:3.4rem;line-height:1.04;letter-spacing:0;margin:0 0 .35em;font-weight:700;color:var(--ink);background:linear-gradient(135deg,var(--ink) 0,#7c3aed 60%,#0ea5e9 100%);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
116
+ :root[data-theme="dark"] .home-hero h1{background:linear-gradient(135deg,#f5f2ff 0,#c4b5fd 50%,#7dd3fc 100%);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
117
+ .home-hero .lede{font-size:1.18rem;line-height:1.55;color:var(--text);margin:0 0 1.2em;max-width:62ch}
118
+ .home-cta{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:0 0 18px}
119
+ .home-cta .btn{display:inline-flex;align-items:center;gap:7px;border-radius:8px;padding:10px 16px;font-weight:600;font-size:.92rem;text-decoration:none;transition:background .15s,border-color .15s,color .15s,transform .12s}
120
+ .home-cta .btn-primary{background:linear-gradient(135deg,var(--accent),var(--accent-strong));color:#fff;border:1px solid var(--accent);box-shadow:0 4px 14px rgba(124,58,237,.35)}
121
+ .home-cta .btn-primary:hover{background:var(--accent-strong);border-color:var(--accent-strong);text-decoration:none;transform:translateY(-1px)}
122
+ .home-cta .btn-ghost{padding:10px 16px}
123
+ .home-install{display:flex;align-items:center;gap:12px;background:var(--code-bg);color:var(--code-fg);border-radius:8px;padding:10px 10px 10px 16px;font:500 .9rem/1.2 "JetBrains Mono","SF Mono",ui-monospace,monospace;max-width:34em;border:1px solid #1f1840}
124
+ .home-install .prompt{color:#6f63a3;user-select:none;flex:0 0 auto}
125
+ .home-install code{flex:1;background:transparent;border:0;color:var(--code-fg);font:inherit;padding:0;white-space:pre;overflow:hidden;text-overflow:ellipsis}
126
+ .home-install .copy{flex:0 0 auto;background:rgba(255,255,255,.08);color:var(--code-fg);border:1px solid rgba(255,255,255,.16);border-radius:6px;padding:5px 11px;font:500 .72rem/1 "Inter",sans-serif;cursor:pointer;transition:background .15s,border-color .15s}
127
+ .home-install .copy:hover{background:rgba(255,255,255,.16)}
128
+ .home-install .copy.copied{background:var(--accent);border-color:var(--accent)}
129
+ .home-services{display:flex;flex-wrap:wrap;gap:6px;margin:6px 0 18px}
130
+ .home-services span{display:inline-block;padding:3px 10px;border:1px solid var(--pill-border);border-radius:999px;font-size:.78rem;color:var(--muted);background:var(--paper)}
131
+ .home-services span.pill-pro{background:var(--accent-soft);color:var(--accent);border-color:transparent;font-weight:600}
132
+ .home-visual{display:block;width:min(100%,620px);height:auto;margin:22px 0 0;border-radius:8px;border:1px solid var(--line);box-shadow:var(--shadow-card);background:var(--paper)}
133
+ .doc-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:48px;margin-top:24px}
134
+ .doc-grid-home{margin-top:8px}
135
+ @media(min-width:1180px){.doc-grid{grid-template-columns:minmax(0,72ch) 200px;justify-content:start}.doc-grid-home{grid-template-columns:minmax(0,76ch);justify-content:start}}
136
+ .doc{min-width:0;max-width:72ch;overflow-wrap:break-word}
137
+ .doc-home{max-width:76ch}
138
+ .doc h1{font-size:2.6rem;line-height:1.08;letter-spacing:0;margin:0 0 .4em;font-weight:700;color:var(--ink)}
139
+ body:not(.home) .doc>h1:first-child{display:none}
140
+ .doc h2{font-size:1.45rem;line-height:1.2;margin:2em 0 .5em;font-weight:600;letter-spacing:0;color:var(--ink);position:relative}
141
+ .doc h3{font-size:1.1rem;margin:1.7em 0 .35em;position:relative;font-weight:600;color:var(--ink);letter-spacing:0}
142
+ .doc h4{font-size:.98rem;margin:1.4em 0 .25em;color:var(--ink);position:relative;font-weight:600}
143
+ .doc h2:first-child,.doc h3:first-child,.doc h4:first-child{margin-top:.2em}
144
+ .doc :is(h2,h3,h4) .anchor{position:absolute;left:-1.05em;top:0;color:var(--subtle);opacity:0;text-decoration:none;font-weight:400;padding-right:.3em;transition:opacity .12s,color .12s}
145
+ .doc :is(h2,h3,h4):hover .anchor{opacity:.7}
146
+ .doc :is(h2,h3,h4) .anchor:hover{opacity:1;color:var(--accent);text-decoration:none}
147
+ .doc p{margin:0 0 1.05em}
148
+ .doc ul,.doc ol{padding-left:1.3rem;margin:0 0 1.15em}
149
+ .doc li{margin:.25em 0}
150
+ .doc li>p{margin:0 0 .4em}
151
+ .doc strong{font-weight:600;color:var(--ink)}
152
+ .doc em{font-style:italic}
153
+ .doc code{font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;font-size:.84em;background:var(--line-soft);border:1px solid var(--line);border-radius:5px;padding:.08em .35em;color:var(--code-inline-fg)}
154
+ .doc pre{position:relative;overflow:auto;background:var(--code-bg);color:var(--code-fg);border-radius:8px;padding:14px 18px;margin:1.3em 0;font-size:.85em;line-height:1.6;scrollbar-width:thin;scrollbar-color:#3a3358 transparent;border:1px solid #221c40}
155
+ .doc pre.shiki{background:var(--code-bg)!important;color:var(--code-fg)!important}
156
+ .doc pre::-webkit-scrollbar{height:8px;width:8px}
157
+ .doc pre::-webkit-scrollbar-thumb{background:#3a3358;border-radius:8px}
158
+ .doc pre code{display:block;background:transparent;border:0;color:inherit;padding:0;font-size:1em;white-space:pre}
159
+ .doc pre code .line{display:block;min-height:1.6em}
160
+ .doc pre .copy{position:absolute;top:8px;right:8px;background:rgba(255,255,255,.06);color:var(--code-fg);border:1px solid rgba(255,255,255,.16);border-radius:6px;padding:3px 9px;font:500 .7rem/1 "Inter",sans-serif;cursor:pointer;opacity:0;transition:opacity .15s,background .15s,border-color .15s}
161
+ .doc pre:hover .copy,.doc pre .copy:focus{opacity:1}
162
+ .doc pre .copy:hover{background:rgba(255,255,255,.12)}
163
+ .doc pre .copy.copied{background:var(--accent);border-color:var(--accent);opacity:1}
164
+ .doc blockquote{margin:1.4em 0;padding:10px 16px;border-left:3px solid var(--accent);background:var(--accent-soft);border-radius:0 8px 8px 0;color:var(--text)}
165
+ .doc blockquote p:last-child{margin-bottom:0}
166
+ .doc table{width:100%;border-collapse:collapse;margin:1.2em 0;font-size:.92em}
167
+ .doc th,.doc td{border-bottom:1px solid var(--line);padding:9px 10px;text-align:left;vertical-align:top}
168
+ .doc th{font-weight:600;color:var(--ink);background:var(--line-soft);border-bottom:1px solid var(--line)}
169
+ .doc hr{border:0;border-top:1px solid var(--line);margin:2.2em 0}
170
+ .toc{position:sticky;top:24px;align-self:start;font-size:.84rem;padding-left:14px;border-left:1px solid var(--line);max-height:calc(100vh - 48px);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--line) transparent}
171
+ .toc::-webkit-scrollbar{width:5px}
172
+ .toc::-webkit-scrollbar-thumb{background:var(--line);border-radius:5px}
173
+ .toc h2{font-size:.66rem;color:var(--muted);text-transform:uppercase;letter-spacing:0;margin:0 0 10px;font-weight:600}
174
+ .toc a{display:block;color:var(--muted);text-decoration:none;padding:4px 0 4px 10px;line-height:1.35;border-left:2px solid transparent;margin-left:-12px;transition:color .12s,border-color .12s}
175
+ .toc a:hover{color:var(--ink);text-decoration:none}
176
+ .toc a.active{color:var(--accent);border-left-color:var(--accent);font-weight:500}
177
+ .toc-l3{padding-left:22px!important;font-size:.94em}
178
+ @media(max-width:1179px){.toc{display:none}}
179
+ .page-nav{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:48px;border-top:1px solid var(--line);padding-top:20px}
180
+ .page-nav>a{display:block;border:1px solid var(--line);background:var(--paper);border-radius:9px;padding:13px 16px;text-decoration:none;color:var(--text);transition:border-color .15s,transform .15s,box-shadow .15s,background-color .18s}
181
+ .page-nav>a:hover{border-color:var(--accent);text-decoration:none;color:var(--ink)}
182
+ .page-nav small{display:block;color:var(--muted);font-size:.7rem;text-transform:uppercase;letter-spacing:0;margin-bottom:5px;font-weight:600}
183
+ .page-nav span{display:block;font-weight:600;line-height:1.3;color:var(--ink)}
184
+ .page-nav-prev{text-align:left}
185
+ .page-nav-next{text-align:right;grid-column:2}
186
+ .page-nav-prev:only-child{grid-column:1}
187
+ .nav-toggle{display:none;position:fixed;top:14px;right:14px;top:calc(14px + env(safe-area-inset-top, 0px));right:calc(14px + env(safe-area-inset-right, 0px));z-index:20;width:40px;height:40px;border-radius:9px;background:var(--paper);border:1px solid var(--line);color:var(--ink);cursor:pointer;padding:10px 9px;flex-direction:column;align-items:stretch;justify-content:space-between;box-shadow:var(--shadow-card)}
188
+ .nav-toggle span{display:block;width:100%;height:2px;flex:0 0 2px;background:currentColor;border-radius:2px;transition:transform .2s,opacity .2s}
189
+ .nav-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(8px) rotate(45deg)}
190
+ .nav-toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
191
+ .nav-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-8px) rotate(-45deg)}
192
+ @media(max-width:900px){
193
+ .shell{display:block}
194
+ .sidebar{position:fixed;inset:0 30% 0 0;max-width:320px;height:100vh;z-index:15;transform:translateX(-100%);transition:transform .25s ease,background-color .18s,border-color .18s;box-shadow:0 18px 40px rgba(0,0,0,.18);background:var(--paper);pointer-events:none}
195
+ .sidebar.open{transform:translateX(0);pointer-events:auto}
196
+ .nav-toggle{display:flex}
197
+ main{padding:64px 18px 56px}
198
+ .hero{padding-top:6px}
199
+ .hero h1{font-size:1.8rem}
200
+ .home-hero h1{font-size:2.45rem}
201
+ .doc h1{font-size:2.1rem}
202
+ .hero-meta{width:100%;justify-content:flex-start}
203
+ .home-hero{padding-top:8px}
204
+ .home-visual{margin-top:18px}
205
+ .doc{padding:0}
206
+ .doc-grid{margin-top:18px;gap:24px}
207
+ .doc :is(h2,h3,h4) .anchor{display:none}
208
+ }
209
+ @media(max-width:520px){
210
+ main{padding:60px 14px 48px}
211
+ .doc pre{margin-left:-14px;margin-right:-14px;border-radius:0;border-left:0;border-right:0}
212
+ .home-install{flex-wrap:wrap}
213
+ }
214
+ </style>
215
+ </head>
216
+ <body>
217
+ <button class="nav-toggle" type="button" aria-label="Toggle navigation" aria-expanded="false">
218
+ <span aria-hidden="true"></span><span aria-hidden="true"></span><span aria-hidden="true"></span>
219
+ </button>
220
+ <div class="shell">
221
+ <aside class="sidebar">
222
+ <div class="sidebar-head">
223
+ <a class="brand" href="./" aria-label="oracle docs home">
224
+ <span class="mark" aria-hidden="true"></span>
225
+ <span><strong>oracle</strong><small>askoracle.sh</small></span>
226
+ </a>
227
+ <button class="theme-toggle" type="button" aria-label="Toggle dark mode" aria-pressed="false" data-theme-toggle>
228
+ <svg class="theme-icon-moon" viewBox="0 0 20 20" aria-hidden="true"><path d="M14.6 12.1A6.5 6.5 0 0 1 7.4 2.7a6.5 6.5 0 1 0 7.2 9.4z" fill="currentColor"/></svg>
229
+ <svg class="theme-icon-sun" viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="3.4" fill="currentColor"/><g stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><line x1="10" y1="2" x2="10" y2="4"/><line x1="10" y1="16" x2="10" y2="18"/><line x1="2" y1="10" x2="4" y2="10"/><line x1="16" y1="10" x2="18" y2="10"/><line x1="4.2" y1="4.2" x2="5.6" y2="5.6"/><line x1="14.4" y1="14.4" x2="15.8" y2="15.8"/><line x1="4.2" y1="15.8" x2="5.6" y2="14.4"/><line x1="14.4" y1="5.6" x2="15.8" y2="4.2"/></g></svg>
230
+ </button>
231
+ </div>
232
+ <label class="search"><span>Search</span><input id="doc-search" type="search" placeholder="browser, mcp, sessions"></label>
233
+ <nav><section><h2>Start</h2><a class="nav-link" href="./">Overview</a><a class="nav-link" href="install.html">Install</a><a class="nav-link" href="quickstart.html">Quickstart</a><a class="nav-link" href="configuration.html">Local configuration (JSON5)</a></section><section><h2>Mythical Pro Agents</h2><a class="nav-link" href="mythical-pro-agents.html">Mythical Pro Agents</a><a class="nav-link" href="openai-endpoints.html">OpenAI-Compatible Endpoints</a><a class="nav-link" href="anthropic.html">Anthropic (Claude) Integration Plan</a><a class="nav-link" href="gemini.html">Gemini Integration</a><a class="nav-link" href="openrouter.html">OpenRouter</a><a class="nav-link" href="grok.html">Grok 4.1 (xAI) Support</a></section><section><h2>Browser Mode</h2><a class="nav-link" href="browser-mode.html">Browser Mode</a><a class="nav-link" href="chromium-forks.html">Chromium-based browsers (Chromium, Edge, Brave variants)</a><a class="nav-link" href="linux.html">Linux Notes</a><a class="nav-link active" href="windows.html">Windows compatibility notes</a><a class="nav-link" href="windows-work.html">Windows Work</a><a class="nav-link" href="bridge.html">Bridge (Windows-hosted ChatGPT session → Linux clients)</a></section><section><h2>Agents &amp; MCP</h2><a class="nav-link" href="agents.html">Coding Agents</a><a class="nav-link" href="mcp.html">MCP Server</a></section><section><h2>Workflows</h2><a class="nav-link" href="sessions.html">Sessions</a><a class="nav-link" href="followup.html">Followups &amp; Lineage</a><a class="nav-link" href="multimodel.html">Multi-Model Execution</a></section><section><h2>Reference</h2><a class="nav-link" href="cli-reference.html">CLI Reference</a><a class="nav-link" href="spec.html">Spec</a><a class="nav-link" href="notifier.html">Session completion notifications</a><a class="nav-link" href="tui-debug.html">TUI navigation crash notes (2025-11-19)</a><a class="nav-link" href="manual-tests.html">Manual Test Suite (Browser Mode + Live API)</a><a class="nav-link" href="testing.html">Testing quickstart</a><a class="nav-link" href="RELEASING.html">Release Checklist (npm + Homebrew)</a></section></nav>
234
+ </aside>
235
+ <main>
236
+ <header class="hero">
237
+ <div class="hero-text">
238
+ <p class="eyebrow">Browser Mode</p>
239
+ <h1>Windows compatibility notes</h1>
240
+ </div>
241
+ <div class="hero-meta">
242
+ <a class="repo" href="https://github.com/steipete/oracle" rel="noopener">GitHub</a>
243
+ <a class="edit" href="https://github.com/steipete/oracle/edit/main/docs/windows.md" rel="noopener">Edit page</a>
244
+ </div>
245
+ </header>
246
+ <div class="doc-grid">
247
+ <article class="doc"><h1 id="windows-compatibility-notes">Windows compatibility notes</h1>
248
+ <p>Keep this in sync as we learn more. Read this before doing browser runs on Windows.</p>
249
+ <ul>
250
+ <li>Browser engine is enabled on Windows now, but automation is flakier than macOS. If it fails, rerun with <code>--engine api --wait</code> or use <code>--remote-chrome</code> to point at a logged-in Chrome with remote debugging.</li>
251
+ <li>Cookies: cookie sync is disabled by default on Windows because ChatGPT cookies are app-bound (<code>v20</code>) and fail decryption. Use <code>--browser-manual-login</code> to reuse a persistent automation profile and sign in once (skips cookie copy entirely). Inline cookies remain available (<code>--browser-inline-cookies(-file)</code> / <code>ORACLE_BROWSER_COOKIES_JSON</code>).</li>
252
+ <li>Manual login flow: run with <code>--browser-manual-login</code> and sign into chatgpt.com in the opened Chrome; Oracle waits until the session is active. For initial login/setup or debugging, add <code>--browser-keep-browser</code> to keep the window open after the run; otherwise Oracle closes Chrome but preserves the profile at <code>~/.oracle/browser-profile</code> (override with <code>ORACLE_BROWSER_PROFILE_DIR</code> or <code>browser.manualLoginProfileDir</code> in <code>~/.oracle/config.json</code>). If that automation Chrome is already running with remote debugging enabled (DevToolsActivePort present), reuse it instead of relaunching by pointing Oracle at it via <code>--remote-chrome &lt;host:port&gt;</code>.</li>
253
+ <li>Cookie paths: preferred path is <code>%LOCALAPPDATA%\\Google\\Chrome\\User Data\\&lt;Profile&gt;\\Network\\Cookies</code>. If that errors, try the top-level <code>Cookies</code> file or supply the exact path via <code>--browser-cookie-path</code>.</li>
254
+ <li>mcporter chrome-devtools: requires a valid <code>CHROME_DEVTOOLS_URL</code> from a live session; otherwise calls will fail.</li>
255
+ <li>agent-scripts helpers (<code>runner</code>, <code>scripts/committer</code>) are bash-based and may fail under PowerShell/CMD; run commands directly if they misbehave.</li>
256
+ </ul><nav class="page-nav" aria-label="Pager"><a class="page-nav-prev" href="linux.html"><small>Previous</small><span>Linux Notes</span></a><a class="page-nav-next" href="windows-work.html"><small>Next</small><span>Windows Work</span></a></nav></article>
257
+
258
+ </div>
259
+ </main>
260
+ </div>
261
+ <script>
262
+ const themeRoot=document.documentElement;
263
+ function applyTheme(mode){themeRoot.dataset.theme=mode;document.querySelectorAll('[data-theme-toggle]').forEach(b=>b.setAttribute('aria-pressed',mode==='dark'?'true':'false'))}
264
+ function storedTheme(){try{return localStorage.getItem('theme')}catch(e){return null}}
265
+ function persistTheme(mode){try{localStorage.setItem('theme',mode)}catch(e){}}
266
+ applyTheme(themeRoot.dataset.theme==='dark'?'dark':'light');
267
+ document.querySelectorAll('[data-theme-toggle]').forEach(btn=>{btn.addEventListener('click',()=>{const next=themeRoot.dataset.theme==='dark'?'light':'dark';applyTheme(next);persistTheme(next)})});
268
+ const systemDark=window.matchMedia&&matchMedia('(prefers-color-scheme: dark)');
269
+ function onSystemChange(e){if(storedTheme())return;applyTheme(e.matches?'dark':'light')}
270
+ if(systemDark){if(systemDark.addEventListener)systemDark.addEventListener('change',onSystemChange);else if(systemDark.addListener)systemDark.addListener(onSystemChange)}
271
+ const sidebar=document.querySelector('.sidebar');
272
+ const toggle=document.querySelector('.nav-toggle');
273
+ const mobileNav=window.matchMedia('(max-width: 900px)');
274
+ const sidebarFocusable='a[href],button,input,select,textarea,[tabindex]';
275
+ function setSidebarFocusable(enabled){
276
+ sidebar?.querySelectorAll(sidebarFocusable).forEach((el)=>{
277
+ if(enabled){
278
+ if(el.dataset.sidebarTabindex!==undefined){
279
+ if(el.dataset.sidebarTabindex)el.setAttribute('tabindex',el.dataset.sidebarTabindex);
280
+ else el.removeAttribute('tabindex');
281
+ delete el.dataset.sidebarTabindex;
282
+ }
283
+ }else if(el.dataset.sidebarTabindex===undefined){
284
+ el.dataset.sidebarTabindex=el.getAttribute('tabindex')??'';
285
+ el.setAttribute('tabindex','-1');
286
+ }
287
+ });
288
+ }
289
+ function setSidebarOpen(open){
290
+ if(!sidebar||!toggle)return;
291
+ sidebar.classList.toggle('open',open);
292
+ toggle.setAttribute('aria-expanded',open?'true':'false');
293
+ if(mobileNav.matches){
294
+ sidebar.inert=!open;
295
+ if(open)sidebar.removeAttribute('aria-hidden');
296
+ else sidebar.setAttribute('aria-hidden','true');
297
+ setSidebarFocusable(open);
298
+ }else{
299
+ sidebar.inert=false;
300
+ sidebar.removeAttribute('aria-hidden');
301
+ setSidebarFocusable(true);
302
+ }
303
+ }
304
+ setSidebarOpen(false);
305
+ toggle?.addEventListener('click',()=>setSidebarOpen(!sidebar?.classList.contains('open')));
306
+ document.addEventListener('click',(e)=>{if(!sidebar?.classList.contains('open'))return;if(sidebar.contains(e.target)||toggle?.contains(e.target))return;setSidebarOpen(false)});
307
+ document.addEventListener('keydown',(e)=>{if(e.key==='Escape')setSidebarOpen(false)});
308
+ const syncSidebarForViewport=()=>setSidebarOpen(sidebar?.classList.contains('open')??false);
309
+ if(mobileNav.addEventListener)mobileNav.addEventListener('change',syncSidebarForViewport);
310
+ else mobileNav.addListener?.(syncSidebarForViewport);
311
+ const input=document.getElementById('doc-search');
312
+ input?.addEventListener('input',()=>{const q=input.value.trim().toLowerCase();document.querySelectorAll('nav section').forEach(sec=>{let any=false;sec.querySelectorAll('.nav-link').forEach(a=>{const m=!q||a.textContent.toLowerCase().includes(q);a.style.display=m?'block':'none';if(m)any=true});sec.style.display=any?'block':'none'})});
313
+ function attachCopy(target,getText){const btn=document.createElement('button');btn.type='button';btn.className='copy';btn.textContent='Copy';btn.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(getText());btn.textContent='Copied';btn.classList.add('copied');setTimeout(()=>{btn.textContent='Copy';btn.classList.remove('copied')},1400)}catch{btn.textContent='Failed';setTimeout(()=>{btn.textContent='Copy'},1400)}});target.appendChild(btn)}
314
+ document.querySelectorAll('.doc pre').forEach(pre=>attachCopy(pre,()=>pre.querySelector('code')?.textContent??''));
315
+ document.querySelectorAll('.home-install').forEach(el=>attachCopy(el,()=>el.querySelector('code')?.textContent??''));
316
+ const tocLinks=document.querySelectorAll('.toc a');
317
+ if(tocLinks.length){const map=new Map();tocLinks.forEach(a=>{const id=a.getAttribute('href').slice(1);const el=document.getElementById(id);if(el)map.set(el,a)});const setActive=l=>{tocLinks.forEach(x=>x.classList.remove('active'));l.classList.add('active')};const obs=new IntersectionObserver(entries=>{const visible=entries.filter(e=>e.isIntersecting).sort((a,b)=>a.boundingClientRect.top-b.boundingClientRect.top);if(visible.length){const link=map.get(visible[0].target);if(link)setActive(link)}},{rootMargin:'-15% 0px -65% 0px',threshold:0});map.forEach((_,el)=>obs.observe(el))}
318
+ </script>
319
+ </body>
320
+ </html>
@@ -7,8 +7,7 @@
7
7
  */
8
8
  import { setTimeout as sleep } from "node:timers/promises";
9
9
  import { launch } from "chrome-launcher";
10
- import os from "node:os";
11
- import { readFileSync } from "node:fs";
10
+ import { isWsl, resolveWslHost } from "../src/browser/wslHost.js";
12
11
  const DEFAULT_PORT = 45871;
13
12
  const port = normalizePort(process.env.ORACLE_BROWSER_PORT ?? process.env.ORACLE_BROWSER_DEBUG_PORT) ??
14
13
  DEFAULT_PORT;
@@ -22,29 +21,6 @@ function normalizePort(raw) {
22
21
  return null;
23
22
  return value;
24
23
  }
25
- function isWsl() {
26
- if (process.platform !== "linux")
27
- return false;
28
- if (process.env.WSL_DISTRO_NAME)
29
- return true;
30
- return os.release().toLowerCase().includes("microsoft");
31
- }
32
- function resolveWslHost() {
33
- if (!isWsl())
34
- return null;
35
- try {
36
- const resolv = readFileSync("/etc/resolv.conf", "utf8");
37
- for (const line of resolv.split("\n")) {
38
- const match = line.match(/^nameserver\s+([0-9.]+)/);
39
- if (match?.[1])
40
- return match[1];
41
- }
42
- }
43
- catch {
44
- // ignore
45
- }
46
- return null;
47
- }
48
24
  function firewallHint(host, devtoolsPort) {
49
25
  if (!isWsl())
50
26
  return null;
@@ -37,17 +37,18 @@ export async function ensureModelSelection(Runtime, desiredModel, logger, strate
37
37
  case "already-selected":
38
38
  case "switched": {
39
39
  const observedLabel = result.label?.trim() || null;
40
- const label = observedLabel || (strategy === "current" ? null : desiredModel);
41
- if (strategy !== "current") {
42
- assertResolvedModelSelection(desiredModel, observedLabel ?? "");
40
+ if (strategy !== "current" && observedLabel !== null) {
41
+ assertResolvedModelSelection(desiredModel, observedLabel);
43
42
  }
44
- logger(`Model picker: ${label ?? "current model (label unavailable)"}`);
43
+ logger(`Model picker: ${observedLabel ?? "current model (label unavailable)"}`);
45
44
  return {
46
45
  requestedModel: desiredModel,
47
- resolvedLabel: label,
46
+ // A picker target is intent, not observed UI evidence. Keep it separate from the
47
+ // resolved label so display code cannot turn a fallback into a claimed selection.
48
+ resolvedLabel: observedLabel,
48
49
  strategy,
49
50
  status: result.status,
50
- verified: strategy !== "current",
51
+ verified: strategy !== "current" && observedLabel !== null,
51
52
  source: "chatgpt-model-picker",
52
53
  capturedAt: new Date().toISOString(),
53
54
  };
@@ -291,12 +292,10 @@ function buildModelSelectionExpression(targetModel, strategy) {
291
292
  const getComposerModelLabel = () =>
292
293
  (document.querySelector(COMPOSER_MODEL_SIGNAL_SELECTOR)?.textContent ?? '').trim();
293
294
  const readComposerModelSignal = () => normalizeText(getComposerModelLabel());
294
- const isIntelligenceEffortLabel = (label) =>
295
- label === 'instant' ||
295
+ const isEffortOnlyLabel = (label) =>
296
296
  label === 'medium' ||
297
297
  label === 'high' ||
298
298
  label === 'extra high' ||
299
- label === 'pro' ||
300
299
  label === 'extended' ||
301
300
  label === 'standard' ||
302
301
  label === 'heavy' ||
@@ -377,7 +376,6 @@ function buildModelSelectionExpression(targetModel, strategy) {
377
376
  !configuredVersionLabel.includes(desiredModelVariant) &&
378
377
  !configuredVariant.includes(desiredModelVariant)
379
378
  ) return false;
380
- if (desiredModelVariant === 'sol' && labelHasProWord(configuredVariant)) return false;
381
379
  if (wantsPro) return labelHasProWord(configuredVariant);
382
380
  if (wantsInstant) return configuredVariant.includes('instant');
383
381
  if (wantsThinking) {
@@ -385,10 +383,11 @@ function buildModelSelectionExpression(targetModel, strategy) {
385
383
  }
386
384
  return true;
387
385
  };
388
- const getResolvedLabel = (fallback) => {
386
+ const getResolvedLabel = (observedOptionLabel = '') => {
389
387
  if (configuredSelectionMatchesTarget()) {
390
388
  const variant = getConfiguredVariantLabel();
391
389
  const version = formatModelOptionLabel(getConfiguredVersionLabel());
390
+ if (desiredModelVariant === 'sol' && labelHasProWord(normalizeText(variant))) return version;
392
391
  return [variant, version].filter(Boolean).join(' ');
393
392
  }
394
393
  const composerLabel = getComposerModelLabel();
@@ -399,6 +398,7 @@ function buildModelSelectionExpression(targetModel, strategy) {
399
398
  versionFromLabel(normalizedComposerLabel) === desiredVersion &&
400
399
  normalizedComposerLabel.split(' ').includes(desiredModelVariant)
401
400
  ) {
401
+ if (desiredModelVariant === 'sol' && hasProComposerPill()) return PRIMARY_LABEL;
402
402
  return withProPillSignal(composerLabel);
403
403
  }
404
404
  const buttonLabel = getButtonLabel();
@@ -409,23 +409,21 @@ function buildModelSelectionExpression(targetModel, strategy) {
409
409
  versionFromLabel(normalizedButton) === desiredVersion &&
410
410
  normalizedButton.split(' ').includes(desiredModelVariant)
411
411
  ) {
412
+ if (desiredModelVariant === 'sol' && hasProComposerPill()) return PRIMARY_LABEL;
412
413
  return withProPillSignal(buttonLabel);
413
414
  }
414
- const fallbackLabel = formatModelOptionLabel(fallback);
415
- const normalizedFallback = normalizeText(fallbackLabel);
416
- if (
417
- desiredVersion &&
418
- desiredModelVariant &&
419
- versionFromLabel(normalizedFallback) === desiredVersion &&
420
- normalizedFallback.split(' ').includes(desiredModelVariant)
421
- ) {
422
- return fallbackLabel;
423
- }
424
- if (composerLabel) return withProPillSignal(composerLabel);
425
- if (fallbackLabel && !wantsPro && isIntelligenceEffortLabel(normalizedButton)) {
426
- return fallbackLabel;
427
- }
428
- return withProPillSignal(buttonLabel || fallbackLabel || fallback);
415
+ const observedLabel = (label) => {
416
+ const formatted = formatModelOptionLabel(label);
417
+ return formatted && !isEffortOnlyLabel(normalizeText(formatted))
418
+ ? withProPillSignal(formatted)
419
+ : '';
420
+ };
421
+ return (
422
+ observedLabel(observedOptionLabel) ||
423
+ observedLabel(composerLabel) ||
424
+ observedLabel(buttonLabel) ||
425
+ (wantsPro && hasProComposerPill() ? 'Pro' : '')
426
+ );
429
427
  };
430
428
  if (MODEL_STRATEGY === 'current') {
431
429
  const currentLabel = getResolvedLabel('') || null;
@@ -443,7 +441,6 @@ function buildModelSelectionExpression(targetModel, strategy) {
443
441
  if (configuredSelectionMatchesTarget()) return true;
444
442
  const normalizedLabel = normalizeText(getButtonLabel());
445
443
  if (!normalizedLabel) return false;
446
- if (desiredModelVariant === 'sol' && hasProComposerPill()) return false;
447
444
  if (wantsThinking && !wantsPro && hasProComposerPill()) return false;
448
445
  if (isTargetGpt55VisibleAlias(normalizedLabel)) return true;
449
446
  if (
@@ -490,7 +487,11 @@ function buildModelSelectionExpression(targetModel, strategy) {
490
487
  }
491
488
  if (wantsThinking && !normalizedLabel.includes('thinking')) return false;
492
489
  // Also reject if button has variants we DON'T want
493
- if (!wantsPro && normalizedLabel.includes(' pro')) return false;
490
+ if (
491
+ !wantsPro &&
492
+ normalizedLabel.includes(' pro') &&
493
+ !(desiredModelVariant === 'sol' && hasProComposerPill())
494
+ ) return false;
494
495
  if (!wantsInstant && normalizedLabel.includes('instant')) return false;
495
496
  if (!wantsThinking && normalizedLabel.includes('thinking')) return false;
496
497
  return true;
@@ -504,13 +505,14 @@ function buildModelSelectionExpression(targetModel, strategy) {
504
505
  if (!signal) {
505
506
  return COMPOSER_SIGNAL_ALLOW_BLANK;
506
507
  }
507
- if (desiredModelVariant === 'sol' && hasProComposerPill()) {
508
- return false;
509
- }
510
508
  if (wantsPro && labelHasLegacyProVersion(signal)) {
511
509
  return false;
512
510
  }
513
- if (COMPOSER_SIGNAL_EXCLUDES.some((token) => token && signal.includes(token))) {
511
+ if (COMPOSER_SIGNAL_EXCLUDES.some((token) =>
512
+ token &&
513
+ signal.includes(token) &&
514
+ !(token === 'pro' && desiredModelVariant === 'sol' && hasProComposerPill())
515
+ )) {
514
516
  return false;
515
517
  }
516
518
  if (COMPOSER_SIGNAL_INCLUDES.length === 0) {
@@ -541,7 +543,7 @@ function buildModelSelectionExpression(targetModel, strategy) {
541
543
  };
542
544
 
543
545
  if (activeSelectionMatchesTarget()) {
544
- return { status: 'already-selected', label: getResolvedLabel(PRIMARY_LABEL) };
546
+ return { status: 'already-selected', label: getResolvedLabel() };
545
547
  }
546
548
 
547
549
  let lastPointerClick = 0;
@@ -934,7 +936,7 @@ function buildModelSelectionExpression(targetModel, strategy) {
934
936
  desiredModelVariant === 'sol' &&
935
937
  (labelHasProWord(normalizedText) || normalizeText(testid ?? '').includes('pro'))
936
938
  ) return false;
937
- if (desiredVersion === '5-6') return !hasProComposerPill();
939
+ if (desiredVersion === '5-6') return true;
938
940
  const currentButtonLabel = normalizeText(getButtonLabel());
939
941
  return !labelHasProWord(currentButtonLabel) && !hasProComposerPill();
940
942
  };
@@ -537,8 +537,11 @@ export async function waitForResumedConversationHydration(Runtime, timeoutMs, lo
537
537
  priorTurns = turns;
538
538
  await delay(250);
539
539
  }
540
- await delay(1_000); // final settle so React won't wipe the composer after we type
541
- await ensureReady(Runtime, timeoutMs, logger);
540
+ const requirePromptReady = deps.requirePromptReady ?? true;
541
+ if (requirePromptReady) {
542
+ await delay(1_000); // final settle so React won't wipe the composer after we type
543
+ await ensureReady(Runtime, timeoutMs, logger);
544
+ }
542
545
  if ((deps.requirePriorTurns ?? false) && (!settled || priorTurns <= 0)) {
543
546
  throw new BrowserAutomationError("Saved ChatGPT conversation did not load stable prior turns; refusing to submit follow-up as a fresh chat.", {
544
547
  stage: "resume-conversation",
@@ -563,7 +566,9 @@ export async function waitForResumedConversationHydration(Runtime, timeoutMs, lo
563
566
  });
564
567
  }
565
568
  }
566
- logger(`[browser] Resumed conversation hydrated (${priorTurns} prior turns); composer settled.`);
569
+ logger(requirePromptReady
570
+ ? `[browser] Resumed conversation hydrated (${priorTurns} prior turns); composer settled.`
571
+ : `[browser] Resumed conversation hydrated (${priorTurns} prior turns).`);
567
572
  return priorTurns;
568
573
  }
569
574
  async function waitForDocumentReady(Runtime, timeoutMs) {
@@ -390,6 +390,24 @@ function buildThinkingTimeExpression(level, desiredModel) {
390
390
  return null;
391
391
  }
392
392
  }
393
+ if (
394
+ TARGET_IS_GPT56_MODEL &&
395
+ TARGET_LEVEL === 'heavy' &&
396
+ isIntelligenceEffortMenu(menu)
397
+ ) {
398
+ for (const item of items) {
399
+ const itemText = normalize(
400
+ (item.textContent ?? '') + ' ' + (item.getAttribute?.('aria-label') ?? ''),
401
+ );
402
+ if (
403
+ hasToken(itemText, 'pro') &&
404
+ !itemText.includes('gpt') &&
405
+ !/(?:^|\\s)5[ .-]?6(?:\\s|$)/.test(itemText)
406
+ ) {
407
+ return item;
408
+ }
409
+ }
410
+ }
393
411
  for (const item of items) {
394
412
  const itemText = normalize(
395
413
  (item.textContent ?? '') + ' ' + (item.getAttribute?.('aria-label') ?? ''),
@@ -513,19 +531,37 @@ function buildThinkingTimeExpression(level, desiredModel) {
513
531
  const currentEffortPillMatchesTarget = (trigger, modelKindOverride = null) => {
514
532
  if (currentProEffortPillMatchesTarget(trigger, modelKindOverride)) return true;
515
533
  const button = freshComposerTrigger(trigger) || findModelButton();
534
+ const normalizedLabel = normalize(
535
+ (button?.textContent ?? '') + ' ' + (button?.getAttribute?.('aria-label') ?? ''),
536
+ );
537
+ if (
538
+ TARGET_IS_GPT56_MODEL &&
539
+ TARGET_LEVEL === 'heavy' &&
540
+ hasToken(normalizedLabel, 'pro')
541
+ ) {
542
+ return true;
543
+ }
516
544
  if ((modelKindOverride || TARGET_MODEL_KIND || modelKindFromNode(button)) === 'pro') {
517
545
  return false;
518
546
  }
519
- const label = (button?.textContent ?? '') + ' ' + (button?.getAttribute?.('aria-label') ?? '');
520
- return matchesLevel(label);
547
+ return matchesLevel(normalizedLabel);
521
548
  };
522
549
  const selectAndVerify = async (trigger, findOption, modelKindOverride = null) => {
523
- const option = findOption();
524
550
  const triggerModelKind =
525
551
  modelKindOverride ||
526
552
  TARGET_MODEL_KIND ||
527
553
  modelKindFromNode(trigger) ||
528
554
  effectiveTargetModelKind();
555
+ const option = findOption();
556
+ if (
557
+ !option &&
558
+ TARGET_IS_GPT56_MODEL &&
559
+ TARGET_LEVEL === 'heavy' &&
560
+ currentEffortPillMatchesTarget(trigger, triggerModelKind)
561
+ ) {
562
+ closeOpenMenus();
563
+ return { status: 'already-selected', label: trigger.textContent?.trim?.() || null };
564
+ }
529
565
  if (!option) return failure('option-not-found', { modelKind: triggerModelKind });
530
566
  const label = option.textContent?.trim?.() || null;
531
567
  if (optionIsSelected(option)) {