@steipete/oracle 0.14.1 → 0.15.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 (85) hide show
  1. package/dist/bin/oracle-cli.js +16 -6
  2. package/dist/bin/oracle.js +569 -0
  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 +399 -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 +388 -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 +323 -0
  37. package/dist/docs-site/windows.html +320 -0
  38. package/dist/src/browser/actions/assistantResponse.js +40 -31
  39. package/dist/src/browser/actions/attachments.js +28 -1
  40. package/dist/src/browser/actions/deepResearch.js +344 -128
  41. package/dist/src/browser/actions/modelSelection.js +75 -9
  42. package/dist/src/browser/actions/promptComposer.js +71 -14
  43. package/dist/src/browser/actions/thinkingStatus.js +19 -1
  44. package/dist/src/browser/actions/thinkingTime.js +65 -20
  45. package/dist/src/browser/artifacts.js +193 -14
  46. package/dist/src/browser/chatgptFiles.js +674 -91
  47. package/dist/src/browser/chromeCookies.js +312 -0
  48. package/dist/src/browser/chromeLifecycle.js +35 -4
  49. package/dist/src/browser/constants.js +5 -0
  50. package/dist/src/browser/deepResearchResult.js +23 -0
  51. package/dist/src/browser/index.js +112 -41
  52. package/dist/src/browser/keytarShim.js +56 -0
  53. package/dist/src/browser/profileCopy.js +93 -0
  54. package/dist/src/browser/sessionRunner.js +9 -3
  55. package/dist/src/browser/windowsCookies.js +219 -0
  56. package/dist/src/cli/bridge/client.js +4 -1
  57. package/dist/src/cli/bridge/doctor.js +19 -0
  58. package/dist/src/cli/browserConfig.js +17 -1
  59. package/dist/src/cli/runOptions.js +11 -2
  60. package/dist/src/cli/sessionDisplay.js +6 -1
  61. package/dist/src/cli/sessionRunner.js +41 -17
  62. package/dist/src/config.js +3 -0
  63. package/dist/src/oracle/client.js +2 -0
  64. package/dist/src/oracle/modelResolver.js +85 -0
  65. package/dist/src/oracle/multiModelRunner.js +4 -1
  66. package/dist/src/oracle/run.js +4 -1
  67. package/dist/src/remote/client.js +253 -22
  68. package/dist/src/remote/health.js +27 -0
  69. package/dist/src/remote/server.js +239 -4
  70. package/dist/src/remote/types.js +1 -1
  71. package/dist/src/sessionManager.js +1 -0
  72. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  73. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/Info.plist +20 -0
  74. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  75. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
  76. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +128 -0
  77. package/dist/vendor/oracle-notifier/build-notifier.sh +0 -0
  78. package/package.json +45 -43
  79. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  80. package/vendor/oracle-notifier/OracleNotifier.app/Contents/Info.plist +20 -0
  81. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  82. package/vendor/oracle-notifier/OracleNotifier.app/Contents/Resources/OracleIcon.icns +0 -0
  83. package/vendor/oracle-notifier/OracleNotifier.app/Contents/_CodeSignature/CodeResources +128 -0
  84. package/vendor/oracle-notifier/README.md +26 -0
  85. package/vendor/oracle-notifier/build-notifier.sh +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>
@@ -1,8 +1,9 @@
1
- import { ANSWER_SELECTORS, ASSISTANT_ROLE_SELECTOR, CONVERSATION_TURN_SELECTOR, COPY_BUTTON_SELECTOR, FINISHED_ACTIONS_SELECTOR, STOP_BUTTON_SELECTOR, } from "../constants.js";
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 { delay } from "../utils.js";
3
3
  import { logDomFailure, logConversationSnapshot, buildConversationDebugExpression, } from "../domDebug.js";
4
4
  import { buildClickDispatcher } from "./domEvents.js";
5
5
  const ASSISTANT_POLL_TIMEOUT_ERROR = "assistant-response-watchdog-timeout";
6
+ const STOP_CONTROL_SELECTOR = STOP_BUTTON_SELECTORS.join(", ");
6
7
  const THINKING_STATUS_LABELS = [
7
8
  "thinking",
8
9
  "pro thinking",
@@ -44,8 +45,8 @@ function isAnswerNowPlaceholderText(normalized) {
44
45
  }
45
46
  function buildActiveThinkingStatusPredicateJs(fnName) {
46
47
  const labelsLiteral = JSON.stringify(THINKING_STATUS_LABELS);
47
- const stopSelectorLiteral = JSON.stringify(STOP_BUTTON_SELECTOR);
48
- return `const ${fnName} = (snapshot) => {
48
+ return `${buildStopButtonVisibilityPredicateJs("isStopControlVisible")}
49
+ const ${fnName} = (snapshot) => {
49
50
  const normalized = String(snapshot?.text ?? '').toLowerCase().replace(/\\s+/g, ' ').trim();
50
51
  if (!normalized) return false;
51
52
  const labels = ${labelsLiteral};
@@ -53,7 +54,7 @@ function buildActiveThinkingStatusPredicateJs(fnName) {
53
54
  labels.includes(normalized) ||
54
55
  (normalized.startsWith('thought for ') && normalized.length <= 40) ||
55
56
  (normalized.startsWith('pro thinking') && normalized.length <= 40);
56
- return matches && Boolean(document.querySelector(${stopSelectorLiteral}));
57
+ return matches && isStopControlVisible();
57
58
  };`;
58
59
  }
59
60
  export function matchesThinkingStatusLabelForTest(text) {
@@ -166,16 +167,19 @@ export async function waitForAssistantResponse(Runtime, timeoutMs, logger, minTu
166
167
  isStopButtonVisible(Runtime),
167
168
  isCompletionVisible(Runtime),
168
169
  ]);
169
- if (stopVisible) {
170
- logger("Assistant still generating; waiting for completion");
170
+ // Completion controls can appear briefly while Pro is still replacing its thinking UI.
171
+ // Confirm every capture from that transition with the stability-based watchdog; a
172
+ // partial first paragraph can be arbitrarily long.
173
+ const candidateText = String(candidate?.text ?? "").trim();
174
+ if (stopVisible || completionVisible) {
175
+ logger(stopVisible
176
+ ? "Assistant still generating; waiting for completion"
177
+ : "Completion controls surfaced; confirming stable assistant response");
171
178
  const completed = await pollAssistantCompletion(Runtime, remainingMs, minTurnIndex, expectedConversationId);
172
- if (completed) {
179
+ if (completed && String(completed.text ?? "").trim().length >= candidateText.length) {
173
180
  return completed;
174
181
  }
175
182
  }
176
- else if (completionVisible) {
177
- // No-op: completion UI surfaced and stop button is gone.
178
- }
179
183
  }
180
184
  return candidate;
181
185
  }
@@ -390,7 +394,7 @@ async function pollAssistantCompletion(Runtime, timeoutMs, minTurnIndex, expecte
390
394
  async function isStopButtonVisible(Runtime) {
391
395
  try {
392
396
  const { result } = await Runtime.evaluate({
393
- expression: `Boolean(document.querySelector('${STOP_BUTTON_SELECTOR}'))`,
397
+ expression: buildStopButtonVisibilityExpression(),
394
398
  returnByValue: true,
395
399
  });
396
400
  return Boolean(result?.value);
@@ -399,6 +403,30 @@ async function isStopButtonVisible(Runtime) {
399
403
  return false;
400
404
  }
401
405
  }
406
+ function buildStopButtonVisibilityExpression() {
407
+ return `(() => {
408
+ ${buildStopButtonVisibilityPredicateJs("isStopControlVisible")}
409
+ return isStopControlVisible();
410
+ })()`;
411
+ }
412
+ function buildStopButtonVisibilityPredicateJs(fnName) {
413
+ const selectorLiteral = JSON.stringify(STOP_CONTROL_SELECTOR);
414
+ return `const ${fnName} = () => {
415
+ const isVisible = (node) => {
416
+ if (!(node instanceof HTMLElement)) return false;
417
+ const rect = node.getBoundingClientRect();
418
+ if (!rect || rect.width <= 0 || rect.height <= 0) return false;
419
+ const style = window.getComputedStyle(node);
420
+ return !(
421
+ style.display === 'none' ||
422
+ style.visibility === 'hidden' ||
423
+ (style.opacity !== '' && Number(style.opacity) === 0)
424
+ );
425
+ };
426
+ return Array.from(document.querySelectorAll(${selectorLiteral})).some((node) => isVisible(node));
427
+ };`;
428
+ }
429
+ export const buildStopButtonVisibilityExpressionForTest = buildStopButtonVisibilityExpression;
402
430
  async function isCompletionVisible(Runtime) {
403
431
  try {
404
432
  const { result } = await Runtime.evaluate({
@@ -540,7 +568,7 @@ function buildResponseObserverExpression(timeoutMs, minTurnIndex, expectedConver
540
568
  return `(() => {
541
569
  ${buildClickDispatcher()}
542
570
  const SELECTORS = ${selectorsLiteral};
543
- const STOP_SELECTOR = '${STOP_BUTTON_SELECTOR}';
571
+ const STOP_SELECTOR = ${JSON.stringify(STOP_CONTROL_SELECTOR)};
544
572
  const FINISHED_SELECTOR = '${FINISHED_ACTIONS_SELECTOR}';
545
573
  const CONVERSATION_SELECTOR = ${conversationLiteral};
546
574
  const ASSISTANT_SELECTOR = ${assistantLiteral};
@@ -598,7 +626,6 @@ function buildResponseObserverExpression(timeoutMs, minTurnIndex, expectedConver
598
626
  const captureViaObserver = () =>
599
627
  new Promise((resolve, reject) => {
600
628
  const deadline = Date.now() + ${timeoutMs};
601
- let stopInterval = null;
602
629
  let timeoutId = null;
603
630
  let cleanedUp = false;
604
631
  let observer = null;
@@ -607,10 +634,6 @@ function buildResponseObserverExpression(timeoutMs, minTurnIndex, expectedConver
607
634
  const cleanup = () => {
608
635
  if (cleanedUp) return;
609
636
  cleanedUp = true;
610
- if (stopInterval) {
611
- clearInterval(stopInterval);
612
- stopInterval = null;
613
- }
614
637
  if (timeoutId) {
615
638
  clearTimeout(timeoutId);
616
639
  timeoutId = null;
@@ -662,20 +685,6 @@ function buildResponseObserverExpression(timeoutMs, minTurnIndex, expectedConver
662
685
  observer = new MutationObserver(observerCallback);
663
686
  observer.observe(document.body, { childList: true, subtree: true, characterData: true });
664
687
 
665
- stopInterval = setInterval(() => {
666
- if (cleanedUp) return;
667
- const stop = document.querySelector(STOP_SELECTOR);
668
- if (!stop) {
669
- return;
670
- }
671
- const isStopButton =
672
- stop.getAttribute('data-testid') === 'stop-button' || stop.getAttribute('aria-label')?.toLowerCase()?.includes('stop');
673
- if (isStopButton) {
674
- return;
675
- }
676
- dispatchClickSequence(stop);
677
- }, 500);
678
-
679
688
  timeoutId = setTimeout(() => {
680
689
  cleanup();
681
690
  reject(new Error('Response timeout'));
@@ -1173,7 +1173,14 @@ export async function clearComposerAttachments(Runtime, timeoutMs, logger) {
1173
1173
  export async function waitForAttachmentCompletion(Runtime, timeoutMs, expectedNames = [], logger) {
1174
1174
  const deadline = Date.now() + timeoutMs;
1175
1175
  const expectedNormalized = expectedNames.map((name) => name.toLowerCase());
1176
+ const expectedInputBasenames = expectedNormalized
1177
+ .map((name) => name.split("/").pop()?.split("\\").pop() ?? name)
1178
+ .map((name) => name.toLowerCase().replace(/\s+/g, " ").trim())
1179
+ .filter(Boolean);
1180
+ const expectedInputSignature = expectedInputBasenames.slice().sort().join("\0");
1176
1181
  let inputMatchSince = null;
1182
+ let inputOnlyReadySince = null;
1183
+ let inputOnlySignature = "";
1177
1184
  let sawInputMatch = false;
1178
1185
  let attachmentMatchSince = null;
1179
1186
  let lastVerboseLog = 0;
@@ -1466,6 +1473,26 @@ export async function waitForAttachmentCompletion(Runtime, timeoutMs, expectedNa
1466
1473
  else {
1467
1474
  attachmentMatchSince = null;
1468
1475
  }
1476
+ const inputOnlySignatureNow = inputNames.slice().sort().join("\0");
1477
+ const inputOnlyNamesSatisfied = expectedInputBasenames.length > 0 && inputOnlySignatureNow === expectedInputSignature;
1478
+ const inputOnlyReady = inputOnlyNamesSatisfied &&
1479
+ value.state === "ready" &&
1480
+ value.uploading === false &&
1481
+ !value.filesAttached &&
1482
+ fileCount === 0;
1483
+ if (inputOnlyReady) {
1484
+ if (inputOnlyReadySince === null || inputOnlySignature !== inputOnlySignatureNow) {
1485
+ inputOnlyReadySince = Date.now();
1486
+ inputOnlySignature = inputOnlySignatureNow;
1487
+ }
1488
+ if (Date.now() - inputOnlyReadySince > 1500) {
1489
+ return;
1490
+ }
1491
+ }
1492
+ else {
1493
+ inputOnlyReadySince = null;
1494
+ inputOnlySignature = "";
1495
+ }
1469
1496
  // Fallback: if the file input has the expected names, allow progress once that condition is stable.
1470
1497
  // Some ChatGPT surfaces only render the filename after sending the message.
1471
1498
  const inputMissing = expectedNormalized.filter((expected) => {
@@ -1478,7 +1505,7 @@ export async function waitForAttachmentCompletion(Runtime, timeoutMs, expectedNa
1478
1505
  // Don't include 'disabled' - a disabled button likely means upload is still in progress.
1479
1506
  const inputStateOk = value.state === "ready" || value.state === "missing";
1480
1507
  const inputSeenNow = inputMissing.length === 0 || fileCountSatisfied;
1481
- const inputEvidenceOk = Boolean(value.filesAttached) || Boolean(value.uploading) || fileCountSatisfied;
1508
+ const inputEvidenceOk = Boolean(value.filesAttached) || fileCountSatisfied;
1482
1509
  const stableThresholdMs = value.uploading ? 3000 : 1500;
1483
1510
  if (inputSeenNow && inputStateOk && inputEvidenceOk) {
1484
1511
  if (inputMatchSince === null) {