Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@

## Unreleased

### Changed

- Browser: keep flattened text as the `auto` bundle format for text-only uploads, matching the previous contract. ZIP is used when raw/native files are present or when `--browser-bundle-format zip` is set, and ChatGPT gets a short extract instruction in those cases. Multiple uploaded text/source files still collapse to one bundle; small `auto` inputs still paste inline unless `--browser-bundle-files` forces an upload.
- Dependencies: update provider SDKs, browser tooling, terminal utilities, development tools, pnpm, and transitive overrides while retaining the two-day release-age policy; refresh the Pages pnpm action pin.
- **Breaking** — Remote: accept only conversation-scoped fields from remote clients. The service overrode six known-dangerous `browserConfig` fields and passed the rest of `BrowserSessionConfig` through to `runBrowserMode` verbatim. The remainder is not inert: `chromePath` names an executable the host spawns, `remoteChrome` a debugger to attach to, `copyProfileSource` a directory to copy a signed-in profile out of, `debugPort` one to expose, and `attachRunning`/`browserTabRef` select an existing ChatGPT tab — with an empty or "current" ref resolving to the first ChatGPT tab in the browser. That makes a bridge token a permission to run code on the host rather than to ask ChatGPT a question. A client may now describe the conversation it wants — URL, model, effort, archive mode, resume target, time budgets — and nothing about the machine. A caller that was setting host-scoped fields is now ignored on them rather than obeyed.
- Remote: stop advertising addresses the service is not listening on. `oracle serve --host 127.0.0.1` printed the host's LAN and tailnet addresses in its startup banner while bound to loopback only. That banner is how an operator decides whether a port needs a tunnel or a firewall rule, and for browser automation behind a bearer token, erring toward "more exposed than it is" is the wrong direction.

### Fixed

- Browser: honor `--browser-bundle-files` (and MCP `browserBundleFiles`) with the default `auto` attachment policy, so small files are uploaded as one bundle instead of being pasted inline.
- Browser: create fallback source bundles only when ChatGPT rejects an inline paste, including remote runs, and delete generated `oracle-browser-bundle-*` directories after browser runs and dry-runs.
- Browser: retain per-file attachment evidence through local/remote upload and send checks, including filename-less images; activate and stabilize the send target without replaying a dispatched prompt. Fixes #418. Thanks @hubofvalley!
- Remote: allocate unique upload basenames for primary and fallback attachments that collide after sanitization, preserving original display paths and every payload. Fixes #387. Thanks @postoso!
- Browser: attach to running Chrome when `DevToolsActivePort` metadata is absent, with IPv6 support and bounded endpoint retries that include response-body reads. Fixes #414. Thanks @devYRPauli!
Expand All @@ -15,13 +24,6 @@
- Browser: apply the configured input timeout to prompt preparation so stalled local file assembly fails clearly before launching Chrome. Fixes #381.
- Browser: report ChatGPT's rate limit as a rate limit. When ChatGPT covers the page with its "Too many requests — we've temporarily limited access to your conversations" modal, the model-switcher scrape walked it like any other menu and reported its "Got it" button as an available model, so a throttled run failed with `Unable to find model option matching "…". Available: Got it` — a message that sends the reader after a model-naming bug when the correct response is to wait a few minutes. Model selection now probes for the notice first and raises a `chatgpt-throttled` error carrying `retryable: true` and the notice text; without a notice the original diagnosis is unchanged.

### Changed

- Dependencies: update provider SDKs, browser tooling, terminal utilities, development tools, pnpm, and transitive overrides while retaining the two-day release-age policy; refresh the Pages pnpm action pin.

- **Breaking** — Remote: accept only conversation-scoped fields from remote clients. The service overrode six known-dangerous `browserConfig` fields and passed the rest of `BrowserSessionConfig` through to `runBrowserMode` verbatim. The remainder is not inert: `chromePath` names an executable the host spawns, `remoteChrome` a debugger to attach to, `copyProfileSource` a directory to copy a signed-in profile out of, `debugPort` one to expose, and `attachRunning`/`browserTabRef` select an existing ChatGPT tab — with an empty or "current" ref resolving to the first ChatGPT tab in the browser. That makes a bridge token a permission to run code on the host rather than to ask ChatGPT a question. A client may now describe the conversation it wants — URL, model, effort, archive mode, resume target, time budgets — and nothing about the machine. A caller that was setting host-scoped fields is now ignored on them rather than obeyed.
- Remote: stop advertising addresses the service is not listening on. `oracle serve --host 127.0.0.1` printed the host's LAN and tailnet addresses in its startup banner while bound to loopback only. That banner is how an operator decides whether a port needs a tunnel or a firewall rule, and for browser automation behind a bearer token, erring toward "more exposed than it is" is the wrong direction.

## 0.18.0 — 2026-08-14

### Changed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ oracle --dry-run summary --files-report \
--file "!**/*.test.ts"
```

Generated text bundles include stable line numbers so answers can cite `path:line`. Binary and large browser inputs can be uploaded or bundled without converting their contents. The [CLI reference](docs/cli-reference.md) lists the file, size, output, and browser controls.
Generated text bundles include stable line numbers so answers can cite `path:line`. In browser mode, one uploaded text/source file stays native and multiple text/source files are packed into one bundle: flattened text for text-only `auto` uploads, or a ZIP when raw files are present or `--browser-bundle-format zip` is set. Native images and documents remain direct attachments when possible. The [CLI reference](docs/cli-reference.md) lists the file, size, output, and browser controls.

## Sessions and follow-ups

Expand Down
4 changes: 2 additions & 2 deletions bin/oracle-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -875,13 +875,13 @@ program
.addOption(
new Option(
"--browser-bundle-files",
"Bundle all attachments into a single archive before uploading.",
"Force one browser upload bundle; auto/zip includes all resolved files. Multi-file text/source uploads already bundle by default (flattened text unless ZIP is selected).",
).default(false),
)
.addOption(
new Option(
"--browser-bundle-format <format>",
"Bundle format for browser uploads when files are bundled: auto (default), text, or zip.",
"Bundle format for browser uploads: auto (flattened text for text-only, ZIP when raw files are present), text, or zip.",
)
.choices(["auto", "text", "zip"])
.default("auto"),
Expand Down
8 changes: 4 additions & 4 deletions docs/browser-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Notes:
- Launcher mode can optionally copy cookies from the requested browser profile via Oracle’s built-in cookie reader (Keychain/DPAPI aware), but this requires `--browser-cookie-sync` or `browser.cookieSync=true`.
- Navigates to `chatgpt.com`, switches the model to the requested GPT-5.5 / GPT-5.4 / GPT-5.2 variant (including `Advanced` → `Model` in the unified picker), optionally activates Deep Research, pastes the prompt, waits for completion, and copies the markdown via the built-in “copy turn” button.
- Immediately probes the cookie-authenticated `/api/auth/session` endpoint in the ChatGPT tab and checks only whether it contains a user; returned tokens are never logged. If that endpoint is unavailable, Oracle falls back to the legacy `/backend-api/me` probe and a visible composer plus profile or chat-history authentication signals. Auth pages, visible login controls, resolved sessions without a user, composer-only shells, and pages without profile/history signals still fail with login guidance.
- When `--file` inputs would push the pasted composer content over ~60k characters, we switch to uploading attachments (optionally bundled) and wait for ChatGPT to re-enable the send button before submitting the combined system+user prompt.
- When `--file` inputs would push the pasted composer content over ~60k characters, we switch to uploads and wait for ChatGPT to re-enable the send button before submitting the combined system+user prompt. A single text/source file is uploaded directly; multiple text/source files are packed into one bundle. Text-only `auto` bundles stay flattened text; ZIP is used when raw files are present or `--browser-bundle-format zip` is set.
- Launcher mode cleans up the temporary profile unless `--browser-keep-browser` is passed.

3. **Session integration** – browser sessions use the normal log writer, add `mode: "browser"` plus `browser.config/runtime` metadata, and persist Chrome pid/port or websocket attach metadata plus the Oracle-owned target/tab URL for reattach.
Expand Down Expand Up @@ -123,8 +123,8 @@ Notes:
- Filename-less attachment previews are accepted only when that file's assignment creates a distinct removal control in the active composer. Oracle retains that per-file evidence through local/remote upload, completion, and send readiness; removing or replacing the control invalidates it. A generic file count or an old attachment is not proof of a new upload.
- Before sending, Oracle activates the target and checks stable, unobstructed button coordinates. It scrolls only if the button is offscreen, then measures again. Once a click is dispatched, Oracle waits for the original turn to commit and never retries with Enter or an upload fallback merely because the prompt remains staged. An ambiguous commit timeout preserves diagnostics for inspection; do not blindly rerun it. Truncation detected before dispatch can still use the normal upload fallback.
- `--browser-inline-files`: alias for `--browser-attachments never` (forces inline paste; never uploads attachments).
- `--browser-bundle-files`: bundle all resolved attachments into a single temp file before uploading (only used when uploads are enabled/selected).
- `--browser-bundle-format <auto|text|zip>`: choose the bundle format. `auto` uses a text bundle for text-only inputs and a byte-preserving ZIP when bundled inputs include raw files; `text` keeps the single Markdown-style text bundle; `zip` archives the original file bytes. ZIP bundle inputs are capped at 128 MiB because bundle creation is in-memory.
- `--browser-bundle-files`: force one browser upload bundle, including when `auto` would otherwise paste small files inline. With `auto` or `zip`, it contains all resolved attachments. Explicit `text` can flatten only text/source files, leaving native attachments separate. Without this flag, Oracle already bundles multiple text/source uploads while leaving images, PDFs, archives, and other native attachments separate when the 10-attachment limit permits. Generated `oracle-browser-bundle-*` directories are deleted after the run or dry-run.
- `--browser-bundle-format <auto|text|zip>`: choose the bundle format. `auto` keeps the established flattened-text bundle for text-only uploads and uses a byte-preserving ZIP when raw/native files are present; `text` always flattens; `zip` always archives. ZIP inputs are capped at 128 MiB because bundle creation is in-memory. Oracle adds a short composer instruction telling ChatGPT to extract ZIP bundles into its sandbox before inspection.
- sqlite bindings: automatic rebuilds now require `ORACLE_ALLOW_SQLITE_REBUILD=1`. Without it, the CLI logs instructions instead of running `pnpm rebuild` on your behalf.
- `--model`: the same GPT-5.6 aliases work in API and browser mode. Use `gpt-5.6` for the current GPT-5.6 default or `gpt-5.6-sol` to pin Sol; browser mode maps either alias to the `GPT-5.6 Sol` picker entry, while API mode sends the corresponding first-party OpenAI model ID. GPT-5.2 base, Instant, and Thinking aliases remain available through the API but browser mode rejects them because ChatGPT retired those picker entries. Legacy Pro aliases still resolve to the latest Pro picker target.
- Live Chrome cookie copying is disabled by default. The recommended migration is `--browser-manual-login`, which keeps token rotation inside a dedicated persistent automation profile. To retain the old launcher behavior, pass `--browser-cookie-sync` or set `browser.cookieSync=true` in the user config; Oracle warns about the live-session invalidation risk. When enabled, cookie copy is mandatory—if Oracle cannot copy cookies, the run exits early. Oracle copies a small ChatGPT auth/Cloudflare allowlist to avoid oversized request headers; use `--browser-cookie-names` only when you need to override that set.
Expand Down Expand Up @@ -386,7 +386,7 @@ This mode is ideal when you have a macOS VM (or spare Mac mini) logged into Chat

## Limitations / Follow-Up Plan

- **Attachment lifecycle** – in `auto` mode we prefer inlining files into the composer (fewer moving parts). When we do upload, each `--file` path is uploaded separately (or bundled) so ChatGPT can ingest filenames/content. The automation waits for uploads to finish (send button enabled, upload chips visible) before submitting. When inline paste is rejected by ChatGPT (too large), Oracle retries automatically with uploads.
- **Attachment lifecycle** – in `auto` mode we prefer inlining small text inputs into the composer. When uploads are selected, one text/source file stays native and multiple text/source files become one bundle. Text-only `auto` bundles stay flattened text so existing workflows keep direct text ingestion; `--browser-bundle-format zip` (or mixed raw inputs) creates a ZIP plus an extract instruction. Images, PDFs, archives, and other native attachments stay separate unless `--browser-bundle-files` is set or the upload cap requires a single archive. `--browser-bundle-files` selects the upload plan even for small auto inputs. Fallback bundles are created only if ChatGPT rejects the inline paste, including on remote hosts. Generated bundle directories are removed after the run. The automation waits for uploads to finish (send button enabled, upload chips visible) before submitting.
- **Model picker drift** – we rely on heuristics to pick GPT-5.6 / GPT-5.5 / GPT-5.4 / GPT-5.2 variants. If OpenAI changes the DOM we need to refresh the selectors quickly. Consider snapshot tests or a small “self check” command.
- **Non-mac platforms** – window hiding uses AppleScript today; Linux/Windows just ignore the flag. We should detect platforms explicitly and document the behavior.
- **Streaming UX** – browser runs cannot stream tokens, so we emit heartbeat/status logs while waiting. Investigate whether we can stream clipboard deltas via mutation observers for a closer UX.
Expand Down
2 changes: 1 addition & 1 deletion docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ See [OpenAI / Azure / OpenRouter](openai-endpoints.md) and [OpenRouter](openrout
| `--browser-keep-browser` | Keep the browser open after the run. |
| `--browser-headless`, `--browser-hide-window` | Visibility controls. |
| `--browser-attachments <auto\|never\|always>` | Attach files inline vs upload. |
| `--browser-bundle-files`, `--browser-bundle-format <auto\|text\|zip>` | Bundle browser uploads as text or byte-preserving ZIP. |
| `--browser-bundle-files`, `--browser-bundle-format <auto\|text\|zip>` | Multi-file source uploads bundle by default; `auto` keeps flattened text unless ZIP is selected or raw files are present. |
| `--browser-chrome-path`, `--browser-cookie-path` | Override Chrome / cookie store discovery (Linux / Windows). |

See [Browser Mode](browser-mode.md) for usage.
Expand Down
5 changes: 5 additions & 0 deletions docs/manual-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ Confirm the logs report a verified GPT-5.5 model followed by `Thinking time: Pro
`pnpm run oracle -- --engine browser --browser-manual-login --model gpt-5.5 --prompt "Summarize the key idea from the attached note" --file /tmp/browser-md.txt`
Ensure upload logs show “Attachment queued” and the answer references the file contents explicitly.

3b. **GPT-5.5 + multi-file ZIP**
Create `/tmp/oracle-zip-smoke/src/one.txt` and `/tmp/oracle-zip-smoke/src/two.txt` with distinct sentinel text, then run
`pnpm run oracle -- --engine browser --browser-manual-login --model gpt-5.5 --browser-attachments always --browser-bundle-format zip --prompt "Extract the attached bundle, report both relative paths, and quote each sentinel." --file /tmp/oracle-zip-smoke/src`
Confirm Oracle uploads one `attachments-bundle.zip`, the submitted composer text includes the extraction instruction, and the answer reports both paths and sentinels from the extracted tree.

4. **GPT-5.5 + attachment (verbose)**
Prepare `/tmp/browser-report.txt` with faux metrics, then run
`pnpm run oracle -- --engine browser --browser-manual-login --model gpt-5.5 --prompt "Use the attachment to report current CPU and memory figures" --file /tmp/browser-report.txt --verbose`
Expand Down
1 change: 1 addition & 0 deletions docs/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Claude Code can call `oracle-mcp` and ask a subscription-backed ChatGPT browser
- Inputs: `prompt` (required), `files?: string[]` (globs), `model?: string` (defaults to CLI), `engine?: "api" | "browser"` (optional; Oracle follows CLI defaults: `ORACLE_ENGINE` and the effective config first, then API when `OPENAI_API_KEY` is set, otherwise browser), `slug?: string`.
- Presets: `preset?: "chatgpt-pro-heavy"` applies browser mode + current Pro model alias + extended thinking, unless the request overrides those fields.
- Browser-only extras: `browserAttachments?: "auto"|"never"|"always"`, `browserBundleFiles?: boolean`, `browserBundleFormat?: "auto"|"text"|"zip"`, `browserThinkingTime?: "light"|"standard"|"extended"|"extra-high"|"pro"|"heavy"`, `browserResearchMode?: "deep"`, `browserFollowUps?: string[]`, `browserArchive?: "auto"|"always"|"never"`, `browserKeepBrowser?: boolean`, `browserModelLabel?: string`, `browserModelStrategy?: "select"|"current"|"ignore"`, `generateImage?: string`, `outputPath?: string`.
- Browser file uploads: one text/source file stays native, while multiple text/source files default to one bundle. `browserBundleFormat:"auto"` keeps flattened text for text-only uploads and uses ZIP when raw files are present. Set `browserBundleFormat:"zip"` for a filesystem tree, or `browserBundleFiles:true` to force one all-file bundle.
- Dry runs: set `dryRun: true` to preview the resolved request without creating a session or touching the browser.
- Behavior: starts a session, runs it with the chosen engine, returns final output + metadata. Background/foreground follows the CLI (e.g., GPT‑5 Pro detaches by default). If API mode fails because `OPENAI_API_KEY` is missing and you have ChatGPT Pro, retry with `engine: "browser"` or `preset: "chatgpt-pro-heavy"` to use your signed-in ChatGPT session instead of an API key.
- Logging: emits MCP logs (`info` per line, `debug` for streamed chunks with byte sizes). If browser prerequisites are missing, returns an error payload instead of running.
Expand Down
Loading