@steipete/oracle 0.10.0 → 0.11.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 (52) hide show
  1. package/README.md +55 -10
  2. package/dist/bin/oracle-cli.js +104 -16
  3. package/dist/src/browser/actions/archiveConversation.js +224 -0
  4. package/dist/src/browser/actions/assistantResponse.js +26 -0
  5. package/dist/src/browser/actions/deepResearch.js +662 -0
  6. package/dist/src/browser/actions/modelSelection.js +78 -13
  7. package/dist/src/browser/actions/navigation.js +22 -0
  8. package/dist/src/browser/actions/projectSources.js +491 -0
  9. package/dist/src/browser/actions/promptComposer.js +52 -27
  10. package/dist/src/browser/actions/thinkingStatus.js +391 -0
  11. package/dist/src/browser/artifacts.js +150 -0
  12. package/dist/src/browser/attachRunning.js +31 -0
  13. package/dist/src/browser/chatgptImages.js +315 -0
  14. package/dist/src/browser/chromeLifecycle.js +214 -3
  15. package/dist/src/browser/config.js +26 -2
  16. package/dist/src/browser/constants.js +8 -0
  17. package/dist/src/browser/controlPlan.js +81 -0
  18. package/dist/src/browser/detect.js +206 -33
  19. package/dist/src/browser/domDebug.js +49 -0
  20. package/dist/src/browser/index.js +1257 -485
  21. package/dist/src/browser/liveTabs.js +434 -0
  22. package/dist/src/browser/profileState.js +83 -3
  23. package/dist/src/browser/projectSourcesRunner.js +366 -0
  24. package/dist/src/browser/reattach.js +117 -45
  25. package/dist/src/browser/reattachHelpers.js +1 -1
  26. package/dist/src/browser/sessionRunner.js +53 -1
  27. package/dist/src/browser/tabLeaseRegistry.js +182 -0
  28. package/dist/src/cli/bridge/claudeConfig.js +12 -8
  29. package/dist/src/cli/bridge/codexConfig.js +2 -2
  30. package/dist/src/cli/browserConfig.js +40 -0
  31. package/dist/src/cli/browserDefaults.js +31 -7
  32. package/dist/src/cli/browserTabs.js +228 -0
  33. package/dist/src/cli/dryRun.js +33 -1
  34. package/dist/src/cli/duplicatePromptGuard.js +10 -2
  35. package/dist/src/cli/help.js +1 -1
  36. package/dist/src/cli/options.js +4 -0
  37. package/dist/src/cli/projectSources.js +116 -0
  38. package/dist/src/cli/sessionCommand.js +51 -0
  39. package/dist/src/cli/sessionDisplay.js +121 -9
  40. package/dist/src/cli/sessionRunner.js +51 -7
  41. package/dist/src/mcp/consultPresets.js +19 -0
  42. package/dist/src/mcp/server.js +2 -0
  43. package/dist/src/mcp/tools/consult.js +201 -26
  44. package/dist/src/mcp/tools/projectSources.js +123 -0
  45. package/dist/src/mcp/types.js +7 -0
  46. package/dist/src/mcp/utils.js +6 -1
  47. package/dist/src/oracle/run.js +4 -1
  48. package/dist/src/projectSources/plan.js +27 -0
  49. package/dist/src/projectSources/types.js +1 -0
  50. package/dist/src/projectSources/url.js +23 -0
  51. package/dist/src/sessionManager.js +1 -0
  52. package/package.json +2 -1
package/README.md CHANGED
@@ -51,7 +51,7 @@ oracle --engine browser --browser-manual-login \
51
51
  Install globally: `npm install -g @steipete/oracle`
52
52
  Homebrew: `brew install steipete/tap/oracle`
53
53
 
54
- Requires Node 22+. Or use `npx -y @steipete/oracle …` (or pnpx).
54
+ Requires Node 24+. Or use `npx -y @steipete/oracle …` (or pnpx).
55
55
 
56
56
  ```bash
57
57
  # Copy the bundle and paste into ChatGPT
@@ -75,6 +75,19 @@ npx -y @steipete/oracle --dry-run summary -p "Check release notes" --file docs/r
75
75
  # Browser run (no API key, will open ChatGPT)
76
76
  npx -y @steipete/oracle --engine browser -p "Walk through the UI smoke test" --file "src/**/*.ts"
77
77
 
78
+ # Add explicit shared context to a ChatGPT Project without deleting anything
79
+ npx -y @steipete/oracle project-sources add \
80
+ --chatgpt-url "https://chatgpt.com/g/g-p-example/project" \
81
+ --browser-manual-login \
82
+ --file docs/architecture.md \
83
+ --dry-run
84
+
85
+ # Browser multi-turn consult in one ChatGPT conversation
86
+ npx -y @steipete/oracle --engine browser --model gpt-5.5-pro \
87
+ -p "Review this migration plan" --file docs/migration.md \
88
+ --browser-follow-up "Challenge your previous recommendation" \
89
+ --browser-follow-up "Give the final decision"
90
+
78
91
  # Gemini browser mode (no API key; uses Chrome cookies from gemini.google.com)
79
92
  npx -y @steipete/oracle --engine browser --model gemini-3-pro --prompt "a cute robot holding a banana" --generate-image out.jpg --aspect 1:1
80
93
 
@@ -99,6 +112,10 @@ Engine auto-picks API when `OPENAI_API_KEY` is set, otherwise browser; browser i
99
112
  - Prefer API mode or `--copy` + manual paste; browser automation is experimental.
100
113
  - Browser support: stable on macOS; works on Linux (add `--browser-chrome-path/--browser-cookie-path` when needed) and Windows (manual-login or inline cookies recommended when app-bound cookies block decryption).
101
114
  - Remote browser service: `oracle serve` on a signed-in host; clients use `--remote-host/--remote-token`.
115
+ - Browser artifacts: browser sessions save `transcript.md` and generated artifacts under `~/.oracle/sessions/<id>/artifacts/`. Deep Research saves `deep-research-report.md` when the report surface is captured; ChatGPT-generated images are downloaded with the active browser cookies when image URLs are present.
116
+ - Browser archiving: by default, successful non-project, non-Deep-Research, non-multi-turn ChatGPT one-shots are archived after local artifacts are saved. Use `--browser-archive never` to disable or `--browser-archive always` to force archiving after a successful browser run. Archived chats remain manageable in ChatGPT.
117
+ - Conversation mode guidance: use one-shot browser runs for narrow bug reports or quick file-set reviews; use explicit browser follow-ups for ambiguous architecture/product tradeoffs where a challenge pass and final decision are valuable; use Deep Research for broad public-web questions that need citations. Oracle never invents follow-ups automatically.
118
+ - Project Sources: `oracle project-sources list|add --chatgpt-url <project-url>` manages the Project Sources tab in ChatGPT browser mode. v1 is append-only (`list`, `add`, `--dry-run`) so agents can share explicit project context without deleting or replacing user sources.
102
119
  - AGENTS.md/CLAUDE.md:
103
120
  ```
104
121
  - Oracle bundles a prompt plus the right files so another AI (GPT 5 Pro + more) can answer. Use when stuck/bugs/reviewing.
@@ -117,6 +134,13 @@ Engine auto-picks API when `OPENAI_API_KEY` is set, otherwise browser; browser i
117
134
 
118
135
  - Run the stdio server via `oracle-mcp`.
119
136
  - Configure clients via [steipete/mcporter](https://github.com/steipete/mcporter) or `.mcp.json`; see [docs/mcp.md](docs/mcp.md) for connection examples.
137
+ - Claude Code on the same Mac as a signed-in ChatGPT browser can generate a local config directly:
138
+
139
+ ```bash
140
+ oracle bridge claude-config --local-browser > .mcp.json
141
+ ```
142
+
143
+ - In MCP `consult`, use `preset: "chatgpt-pro-heavy"` for ChatGPT browser mode with `gpt-5.5-pro` and Pro Extended thinking. Add `dryRun: true` to inspect the resolved run without creating a session or touching Chrome.
120
144
 
121
145
  ```bash
122
146
  npx -y @steipete/oracle oracle-mcp
@@ -138,6 +162,7 @@ npx -y @steipete/oracle oracle-mcp
138
162
 
139
163
  - Bundle once, reuse anywhere (API or experimental browser).
140
164
  - Multi-model API runs with aggregated cost/usage, including OpenRouter IDs alongside first-party models.
165
+ - Claude Code / MCP browser consults can use the `chatgpt-pro-heavy` preset for a compact ChatGPT Pro second-opinion workflow.
141
166
  - Render/copy bundles for manual paste into ChatGPT when automation is blocked.
142
167
  - GPT‑5 Pro API runs detach by default; reattach via `oracle session <id>` / `oracle status` or block with `--wait`.
143
168
  - OpenAI/Azure follow-up API runs can continue from `--followup <sessionId|responseId>`; for multi-model parents, add `--followup-model <model>`.
@@ -196,6 +221,16 @@ oracle --engine browser \
196
221
  -p "Run the long UI audit" --file "src/**/*.ts"
197
222
  ```
198
223
 
224
+ ## Calmer browser runs
225
+
226
+ Browser automation can open or control Chrome, so dry-runs and live runs print a short browser control plan before touching ChatGPT. Use it to choose the least disruptive path for shared desktops and agent-driven consults.
227
+
228
+ - `--dry-run summary --engine browser ...` previews whether Oracle will launch visible Chrome, hide a new window, attach to an existing browser, or use remote Chrome.
229
+ - `--browser-attach-running` and `--remote-chrome <host:port>` are the calmest options when a signed-in Chrome is already running with DevTools enabled.
230
+ - `--browser-hide-window` is best-effort: Chrome can briefly take focus before Oracle hides it.
231
+ - Long GPT-5.5 Pro browser consults are normal. Use `--heartbeat`, `oracle status`, and `oracle session <id>` instead of starting a duplicate run if the host agent appears to be waiting.
232
+ - Successful manual-profile runs close Oracle's own ChatGPT tab and clean up leftover blank startup tabs when no other Oracle browser slots are active. Incomplete runs leave the tab open so `oracle session <id>` can reattach.
233
+
199
234
  ## Flags you’ll actually use
200
235
 
201
236
  | Flag | Purpose |
@@ -211,13 +246,20 @@ oracle --engine browser \
211
246
  | `--chatgpt-url <url>` | Target a ChatGPT workspace/folder (browser). |
212
247
  | `--browser-model-strategy <select\|current\|ignore>` | Control ChatGPT model selection in browser mode (current keeps the active model; ignore skips the picker). |
213
248
  | `--browser-manual-login` | Skip cookie copy; reuse a persistent automation profile and wait for manual ChatGPT login. |
249
+ | `--browser-attach-running` | Reuse your current local browser session through local `DevToolsActivePort` discovery; Oracle opens a dedicated tab instead of launching Chrome (defaults to `127.0.0.1:9222`, or combine with `--remote-chrome <host:port>` to hint a different local endpoint). |
250
+ | `--browser-tab <ref>` | Reuse an existing ChatGPT tab by `current`, target id, URL, or title substring instead of opening a new tab. |
214
251
  | `--browser-thinking-time <light\|standard\|extended\|heavy>` | Set ChatGPT thinking-time intensity (browser; Thinking/Pro models only). |
252
+ | `--browser-research deep` | Activate ChatGPT Deep Research for broad web research and cited reports (browser only). |
253
+ | `--browser-follow-up <prompt>` | Browser-only multi-turn consult: submit an additional prompt in the same ChatGPT conversation after the initial answer. Repeat for challenge/revision/final-decision passes. Not supported with Deep Research mode. |
254
+ | `--browser-archive <auto\|always\|never>` | Archive completed ChatGPT browser conversations after local artifacts are saved. `auto` archives successful one-shot chats only, and skips project, Deep Research, multi-turn, failed, and incomplete sessions. |
215
255
  | `--browser-port <port>` | Pin the Chrome DevTools port (WSL/Windows firewall helper). |
216
256
  | `--browser-inline-cookies[(-file)] <payload \| path>` | Supply cookies without Chrome/Keychain (browser). |
217
257
  | `--browser-timeout`, `--browser-input-timeout` | Control overall/browser input timeouts (supports h/m/s/ms). |
218
258
  | `--browser-recheck-delay`, `--browser-recheck-timeout` | Delayed recheck for long Pro runs: wait then retry capture after timeout (supports h/m/s/ms). |
259
+ | `--heartbeat <seconds>` | Emit API and browser progress heartbeats. Browser mode reports ChatGPT Thinking/Reasoning sidecar liveness metadata when available, without logging reasoning text. |
219
260
  | `--browser-reuse-wait` | Wait for a shared Chrome profile before launching (parallel browser runs). |
220
261
  | `--browser-profile-lock-timeout` | Wait for the shared manual-login profile lock before sending (serializes parallel runs). |
262
+ | `--browser-max-concurrent-tabs` | Soft limit for simultaneous ChatGPT tabs sharing one manual-login profile (default 3). |
221
263
  | `--render`, `--copy` | Print and/or copy the assembled markdown bundle. |
222
264
  | `--wait` | Block for background API runs (e.g., GPT‑5.1 Pro) instead of detaching. |
223
265
  | `--timeout <seconds\|auto>` | Overall API deadline (auto = 60m for pro, 120s otherwise). |
@@ -225,14 +267,14 @@ oracle --engine browser \
225
267
  | `--http-timeout <ms\|s\|m\|h>` | HTTP client timeout (default 20m). |
226
268
  | `--zombie-timeout <ms\|s\|m\|h>` | Override stale-session cutoff used by `oracle status`. |
227
269
  | `--zombie-last-activity` | Use last log activity to detect stale sessions. |
228
- | `--write-output <path>` | Save only the final answer (multi-model adds `.<model>`). |
270
+ | `--write-output <path>` | Save only the final answer (multi-model adds `.<model>`). Browser sessions also save transcripts and generated artifacts under `~/.oracle/sessions/<id>/artifacts/`. |
229
271
  | `--files-report` | Print per-file token usage. |
230
272
  | `--dry-run [summary\|json\|full]` | Preview without sending. |
231
273
  | `--remote-host`, `--remote-token` | Use a remote `oracle serve` host (browser). |
232
- | `--remote-chrome <host:port>` | Attach to an existing remote Chrome session (browser). |
274
+ | `--remote-chrome <host:port>` | Attach to an existing remote Chrome session (browser), or when combined with `--browser-attach-running` use this host:port as the local attach hint. |
233
275
  | `--youtube <url>` | YouTube video URL to analyze (Gemini browser mode). |
234
- | `--generate-image <file>` | Generate image and save to file (Gemini browser mode). |
235
- | `--edit-image <file>` | Edit existing image with `--output` (Gemini browser mode). |
276
+ | `--generate-image <file>` | Generate image and save to file (Gemini browser mode; ChatGPT browser mode saves downloadable image artifacts when present). Extra ChatGPT images save as numbered siblings. |
277
+ | `--edit-image <file>` | Edit existing image with `--output` (Gemini browser mode). For ChatGPT browser mode, attach source images with `--file` and use `--generate-image` for the output path. |
236
278
  | `--azure-endpoint`, `--azure-deployment`, `--azure-api-version` | Target Azure OpenAI endpoints (picks Azure client automatically). |
237
279
 
238
280
  ## Configuration
@@ -246,6 +288,7 @@ Put defaults in `~/.oracle/config.json` (JSON5). Example:
246
288
  filesReport: true,
247
289
  browser: {
248
290
  chatgptUrl: "https://chatgpt.com/g/g-p-691edc9fec088191b553a35093da1ea8-oracle/project",
291
+ archiveConversations: "auto",
249
292
  },
250
293
  }
251
294
  ```
@@ -253,13 +296,15 @@ Put defaults in `~/.oracle/config.json` (JSON5). Example:
253
296
  Use `browser.chatgptUrl` (or the legacy alias `browser.url`) to target a specific ChatGPT workspace/folder for browser automation.
254
297
  See [docs/configuration.md](docs/configuration.md) for precedence and full schema.
255
298
 
299
+ When several agents share one manual-login ChatGPT profile, Oracle coordinates browser tab slots through that profile. Extra runs wait and log that they are waiting for a ChatGPT browser slot instead of crashing because another Codex/Claude/CLI run is already using the browser. For the most reliable shared-agent setup, keep one signed-in Chrome open with remote debugging and point callers at it with `--remote-chrome <host:port>`; direct manual-login launches are guarded so parallel callers reuse the first reachable Chrome instead of racing separate launches on the same profile.
300
+
256
301
  Advanced flags
257
302
 
258
- | Area | Flags |
259
- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
260
- | Browser | `--browser-manual-login`, `--browser-thinking-time`, `--browser-timeout`, `--browser-input-timeout`, `--browser-recheck-delay`, `--browser-recheck-timeout`, `--browser-reuse-wait`, `--browser-profile-lock-timeout`, `--browser-auto-reattach-delay`, `--browser-auto-reattach-interval`, `--browser-auto-reattach-timeout`, `--browser-cookie-wait`, `--browser-inline-cookies[(-file)]`, `--browser-attachments`, `--browser-inline-files`, `--browser-bundle-files`, `--browser-keep-browser`, `--browser-headless`, `--browser-hide-window`, `--browser-no-cookie-sync`, `--browser-allow-cookie-errors`, `--browser-chrome-path`, `--browser-cookie-path`, `--chatgpt-url` |
261
- | Run control | `--background`, `--no-background`, `--http-timeout`, `--zombie-timeout`, `--zombie-last-activity` |
262
- | Azure/OpenAI | `--azure-endpoint`, `--azure-deployment`, `--azure-api-version`, `--base-url` |
303
+ | Area | Flags |
304
+ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
305
+ | Browser | `--browser-manual-login`, `--browser-attach-running`, `--browser-thinking-time`, `--browser-research`, `--browser-follow-up`, `--browser-archive`, `--browser-timeout`, `--browser-input-timeout`, `--browser-recheck-delay`, `--browser-recheck-timeout`, `--browser-reuse-wait`, `--browser-profile-lock-timeout`, `--browser-max-concurrent-tabs`, `--browser-auto-reattach-delay`, `--browser-auto-reattach-interval`, `--browser-auto-reattach-timeout`, `--browser-cookie-wait`, `--browser-inline-cookies[(-file)]`, `--browser-attachments`, `--browser-inline-files`, `--browser-bundle-files`, `--browser-keep-browser`, `--browser-headless`, `--browser-hide-window`, `--browser-no-cookie-sync`, `--browser-allow-cookie-errors`, `--browser-chrome-path`, `--browser-cookie-path`, `--chatgpt-url` |
306
+ | Run control | `--background`, `--no-background`, `--http-timeout`, `--zombie-timeout`, `--zombie-last-activity` |
307
+ | Azure/OpenAI | `--azure-endpoint`, `--azure-deployment`, `--azure-api-version`, `--base-url` |
263
308
 
264
309
  Remote browser example
265
310
 
@@ -21,7 +21,7 @@ import { CHATGPT_URL } from "../src/browserMode.js";
21
21
  import { createRemoteBrowserExecutor } from "../src/remote/client.js";
22
22
  import { createGeminiWebExecutor } from "../src/gemini-web/index.js";
23
23
  import { applyHelpStyling } from "../src/cli/help.js";
24
- import { collectPaths, collectModelList, parseFloatOption, parseIntOption, parseSearchOption, usesDefaultStatusFilters, resolvePreviewMode, normalizeModelOption, normalizeBaseUrl, resolveApiModel, inferModelFromLabel, parseHeartbeatOption, parseTimeoutOption, parseDurationOption, mergePathLikeOptions, dedupePathInputs, } from "../src/cli/options.js";
24
+ import { collectPaths, collectModelList, collectTextValues, parseFloatOption, parseIntOption, parseSearchOption, usesDefaultStatusFilters, resolvePreviewMode, normalizeModelOption, normalizeBaseUrl, resolveApiModel, inferModelFromLabel, parseHeartbeatOption, parseTimeoutOption, parseDurationOption, mergePathLikeOptions, dedupePathInputs, } from "../src/cli/options.js";
25
25
  import { copyToClipboard } from "../src/cli/clipboard.js";
26
26
  import { buildMarkdownBundle } from "../src/cli/markdownBundle.js";
27
27
  import { shouldDetachSession } from "../src/cli/detach.js";
@@ -40,6 +40,7 @@ import { handleSessionCommand, formatSessionCleanupMessage, } from "../src/cli/s
40
40
  import { isErrorLogged } from "../src/cli/errorUtils.js";
41
41
  import { handleSessionAlias, handleStatusFlag } from "../src/cli/rootAlias.js";
42
42
  import { resolveOutputPath } from "../src/cli/writeOutputPath.js";
43
+ import { showBrowserTabsStatus } from "../src/cli/browserTabs.js";
43
44
  import { getCliVersion } from "../src/version.js";
44
45
  import { runDryRunSummary, runBrowserPreview } from "../src/cli/dryRun.js";
45
46
  import { launchTui } from "../src/cli/tui/index.js";
@@ -64,9 +65,13 @@ const normalizedArgv = process.argv.map((arg, index) => {
64
65
  const rawCliArgs = normalizedArgv.slice(2);
65
66
  const userCliArgs = rawCliArgs[0] === CLI_ENTRYPOINT ? rawCliArgs.slice(1) : rawCliArgs;
66
67
  const isTty = process.stdout.isTTY;
68
+ const suppressIntro = userCliArgs[0] === "bridge" &&
69
+ (userCliArgs[1] === "codex-config" || userCliArgs[1] === "claude-config");
67
70
  const program = new Command();
68
71
  let introPrinted = false;
69
72
  program.hook("preAction", () => {
73
+ if (suppressIntro)
74
+ return;
70
75
  if (introPrinted)
71
76
  return;
72
77
  console.log(formatIntroLine(VERSION, { env: process.env, richTty: isTty }));
@@ -193,6 +198,7 @@ program
193
198
  .addOption(new Option("--browser-chrome-profile <name>", "Chrome profile name/path for cookie reuse.").hideHelp())
194
199
  .addOption(new Option("--browser-chrome-path <path>", "Explicit Chrome or Chromium executable path.").hideHelp())
195
200
  .addOption(new Option("--browser-cookie-path <path>", "Explicit Chrome/Chromium cookie DB path for session reuse."))
201
+ .addOption(new Option("--browser-attach-running", "Attach to a running local browser session instead of launching Chrome (defaults to 127.0.0.1:9222; combine with --remote-chrome to hint a different host:port)."))
196
202
  .addOption(new Option("--chatgpt-url <url>", `Override the ChatGPT web URL (e.g., workspace/folder like https://chatgpt.com/g/.../project; default ${CHATGPT_URL}).`))
197
203
  .addOption(new Option("--browser-url <url>", `Alias for --chatgpt-url (default ${CHATGPT_URL}).`).hideHelp())
198
204
  .addOption(new Option("--browser-timeout <ms|s|m>", "Maximum time to wait for an answer (default 1200s / 20m).").hideHelp())
@@ -201,6 +207,7 @@ program
201
207
  .addOption(new Option("--browser-recheck-timeout <ms|s|m|h>", "Time budget for the delayed recheck attempt (default 120s).").hideHelp())
202
208
  .addOption(new Option("--browser-reuse-wait <ms|s|m|h>", "Wait for a shared Chrome profile to appear before launching a new one (helps parallel runs).").hideHelp())
203
209
  .addOption(new Option("--browser-profile-lock-timeout <ms|s|m|h>", "Wait for the shared manual-login profile lock before sending (serializes parallel runs).").hideHelp())
210
+ .addOption(new Option("--browser-max-concurrent-tabs <n>", "Soft limit for concurrent ChatGPT tabs sharing one manual-login profile (default 3).").hideHelp())
204
211
  .addOption(new Option("--browser-auto-reattach-delay <ms|s|m|h>", "Delay before starting periodic auto-reattach attempts after a timeout.").hideHelp())
205
212
  .addOption(new Option("--browser-auto-reattach-interval <ms|s|m|h>", "Interval between auto-reattach attempts (0 disables).").hideHelp())
206
213
  .addOption(new Option("--browser-auto-reattach-timeout <ms|s|m|h>", "Time budget for each auto-reattach attempt (default 120s).").hideHelp())
@@ -221,19 +228,25 @@ program
221
228
  .addOption(new Option("--browser-thinking-time <level>", "Thinking time intensity for Thinking/Pro models: light, standard, extended, heavy.")
222
229
  .choices(["light", "standard", "extended", "heavy"])
223
230
  .hideHelp())
231
+ .addOption(new Option("--browser-research <mode>", "Browser research mode: deep activates ChatGPT Deep Research.").choices(["off", "deep"]))
232
+ .addOption(new Option("--browser-archive <mode>", "Archive completed ChatGPT browser conversations after local artifacts are saved (auto archives successful non-project one-shots only).").choices(["auto", "always", "never"]))
233
+ .addOption(new Option("--browser-follow-up <prompt>", "Submit an additional prompt in the same ChatGPT browser conversation after the initial answer; repeat for multi-turn consults.")
234
+ .argParser(collectTextValues)
235
+ .default([]))
224
236
  .addOption(new Option("--browser-allow-cookie-errors", "Continue even if Chrome cookies cannot be copied.").hideHelp())
225
237
  .addOption(new Option("--browser-attachments <mode>", "How to deliver --file inputs in browser mode: auto (default) pastes inline up to ~60k chars then uploads; never always paste inline; always always upload.")
226
238
  .choices(["auto", "never", "always"])
227
239
  .default("auto"))
228
- .addOption(new Option("--remote-chrome <host:port>", "Connect to remote Chrome DevTools Protocol (e.g., 192.168.1.10:9222 or [2001:db8::1]:9222 for IPv6)."))
240
+ .addOption(new Option("--remote-chrome <host:port>", "Connect to remote Chrome DevTools Protocol, or when combined with --browser-attach-running use this host:port as the local attach hint."))
241
+ .option("--browser-tab <ref>", "Reuse an existing ChatGPT tab by ref (current, target id, full URL, or title substring) instead of opening a new tab.")
229
242
  .addOption(new Option("--remote-host <host:port>", "Delegate browser runs to a remote `oracle serve` instance."))
230
243
  .addOption(new Option("--remote-token <token>", "Access token for the remote `oracle serve` instance."))
231
244
  .addOption(new Option("--browser-inline-files", "Alias for --browser-attachments never (force pasting file contents inline).").default(false))
232
245
  .addOption(new Option("--browser-bundle-files", "Bundle all attachments into a single archive before uploading.").default(false))
233
246
  .addOption(new Option("--youtube <url>", "YouTube video URL to analyze (Gemini web/cookie mode only; uses your signed-in Chrome cookies for gemini.google.com)."))
234
- .addOption(new Option("--generate-image <file>", "Generate image and save to file (Gemini web/cookie mode only; requires gemini.google.com Chrome cookies)."))
235
- .addOption(new Option("--edit-image <file>", "Edit existing image (use with --output, Gemini web/cookie mode only)."))
236
- .addOption(new Option("--output <file>", "Output file path for image operations (Gemini web/cookie mode only)."))
247
+ .addOption(new Option("--generate-image <file>", "Generate image and save to file (Gemini browser mode; ChatGPT browser mode saves downloadable image artifacts when present)."))
248
+ .addOption(new Option("--edit-image <file>", "Edit existing image (Gemini browser mode; for ChatGPT attach source images with --file and use --generate-image for output)."))
249
+ .addOption(new Option("--output <file>", "Output file path for image operations."))
237
250
  .addOption(new Option("--aspect <ratio>", "Aspect ratio for image generation: 16:9, 1:1, 4:3, 3:4 (Gemini web/cookie mode only)."))
238
251
  .addOption(new Option("--gemini-show-thoughts", "Display Gemini thinking process (Gemini web/cookie mode only).").default(false))
239
252
  .option("--retain-hours <hours>", "Prune stored sessions older than this many hours before running (set 0 to disable).", parseFloatOption)
@@ -273,6 +286,47 @@ program
273
286
  manualLoginProfileDir: commandOptions.manualLoginProfileDir,
274
287
  });
275
288
  });
289
+ const projectSourcesCommand = program
290
+ .command("project-sources")
291
+ .description("Manage ChatGPT Project Sources as explicit shared project context.");
292
+ function addProjectSourcesCommonOptions(command) {
293
+ return command
294
+ .option("--chatgpt-url <url>", "ChatGPT project URL ending in /project (or browser.chatgptUrl config).")
295
+ .addOption(new Option("--browser-manual-login", "Reuse a persistent signed-in Chrome profile.").default(undefined))
296
+ .option("--browser-manual-login-profile-dir <path>", "Persistent Chrome profile directory.")
297
+ .option("--browser-timeout <duration>", "Overall browser timeout (e.g. 10m, 1h).")
298
+ .option("--browser-input-timeout <duration>", "Timeout waiting for the Project Sources UI.")
299
+ .option("--browser-profile-lock-timeout <duration>", "Timeout waiting for profile launch lock.")
300
+ .option("--browser-reuse-wait <duration>", "Wait for an existing shared Chrome to appear.")
301
+ .option("--browser-max-concurrent-tabs <n>", "Concurrent tabs allowed for the shared profile.")
302
+ .option("--browser-cookie-wait <duration>", "Wait before retrying cookie sync.")
303
+ .option("--browser-chrome-profile <profile>", "Chrome profile name for cookie sync.")
304
+ .option("--browser-chrome-path <path>", "Chrome/Chromium executable path.")
305
+ .option("--browser-cookie-path <path>", "Explicit Chrome cookie DB path.")
306
+ .option("--browser-inline-cookies <json>", "Inline ChatGPT cookies JSON.")
307
+ .option("--browser-inline-cookies-file <path>", "File containing ChatGPT cookies JSON.")
308
+ .option("--browser-no-cookie-sync", "Skip copying cookies from Chrome.")
309
+ .option("--browser-keep-browser", "Keep Chrome running after completion.", false)
310
+ .option("--browser-hide-window", "Hide Chrome window after launch on macOS.", false)
311
+ .option("--browser-allow-cookie-errors", "Continue when cookie sync fails.", false)
312
+ .option("--max-file-size-bytes <bytes>", "Reject uploads larger than this many bytes.", parseIntOption)
313
+ .option("--json", "Print structured JSON.", false)
314
+ .option("-v, --verbose", "Enable verbose browser logging.", false);
315
+ }
316
+ addProjectSourcesCommonOptions(projectSourcesCommand
317
+ .command("list")
318
+ .description("List sources already attached to a ChatGPT Project.")).action(async function () {
319
+ const { runProjectSourcesCliCommand } = await import("../src/cli/projectSources.js");
320
+ await runProjectSourcesCliCommand("list", this.optsWithGlobals());
321
+ });
322
+ addProjectSourcesCommonOptions(projectSourcesCommand
323
+ .command("add")
324
+ .description("Upload files into a ChatGPT Project's persistent Sources tab.")
325
+ .option("-f, --file <paths...>", "Files/directories or globs to add as project sources.", collectPaths, [])
326
+ .option("--dry-run", "Validate files and show the upload plan without touching the browser.", false)).action(async function () {
327
+ const { runProjectSourcesCliCommand } = await import("../src/cli/projectSources.js");
328
+ await runProjectSourcesCliCommand("add", this.optsWithGlobals());
329
+ });
276
330
  const bridgeCommand = program
277
331
  .command("bridge")
278
332
  .description("Bridge a Windows-hosted ChatGPT session to Linux clients.");
@@ -326,6 +380,9 @@ bridgeCommand
326
380
  .command("claude-config")
327
381
  .description("Print a Claude Code MCP config snippet (.mcp.json) for oracle-mcp.")
328
382
  .option("--print-token", "Include ORACLE_REMOTE_TOKEN in the snippet.", false)
383
+ .option("--local-browser", "Use a local signed-in Chrome profile instead of a remote bridge.", false)
384
+ .option("--oracle-home-dir <path>", "Override ORACLE_HOME_DIR in the generated snippet.")
385
+ .option("--browser-profile-dir <path>", "Override ORACLE_BROWSER_PROFILE_DIR in the generated snippet.")
329
386
  .action(async (commandOptions) => {
330
387
  const { runBridgeClaudeConfig } = await import("../src/cli/bridge/claudeConfig.js");
331
388
  await runBridgeClaudeConfig(commandOptions);
@@ -349,6 +406,10 @@ program
349
406
  .option("--render-markdown", "Alias for --render.", false)
350
407
  .option("--model <name>", "Filter sessions/output for a specific model.", "")
351
408
  .option("--path", "Print the stored session paths instead of attaching.", false)
409
+ .option("--harvest", "Re-read the bound browser tab and print/save the latest assistant output.", false)
410
+ .option("--live", "Tail the live browser tab for this session until it completes, stalls, or detaches.", false)
411
+ .option("--write-output <path>", "Write harvested browser output to this file (requires --harvest or --live).")
412
+ .option("--browser-tab <ref>", "Override the browser tab ref used for harvesting/live tail (current, target id, URL, or title substring).")
352
413
  .addOption(new Option("--clean", "Deprecated alias for --clear.").default(false).hideHelp())
353
414
  .action(async (sessionId, _options, cmd) => {
354
415
  await handleSessionCommand(sessionId, cmd);
@@ -364,9 +425,19 @@ program
364
425
  .option("--render-markdown", "Alias for --render.", false)
365
426
  .option("--model <name>", "Filter sessions/output for a specific model.", "")
366
427
  .option("--hide-prompt", "Hide stored prompt when displaying a session.", false)
428
+ .option("--browser-tabs", "List live ChatGPT browser tabs and known Oracle session linkage.", false)
367
429
  .addOption(new Option("--clean", "Deprecated alias for --clear.").default(false).hideHelp())
368
430
  .action(async (sessionId, _options, command) => {
369
431
  const statusOptions = command.opts();
432
+ if (statusOptions.browserTabs) {
433
+ if (sessionId) {
434
+ console.error("Cannot combine a session ID with --browser-tabs. Remove the ID to inspect live browser tabs.");
435
+ process.exitCode = 1;
436
+ return;
437
+ }
438
+ await showBrowserTabsStatus();
439
+ return;
440
+ }
370
441
  const clearRequested = Boolean(statusOptions.clear || statusOptions.clean);
371
442
  if (clearRequested) {
372
443
  if (sessionId) {
@@ -458,6 +529,9 @@ function buildRunOptions(options, overrides = {}) {
458
529
  "auto",
459
530
  browserInlineFiles: overrides.browserInlineFiles ?? options.browserInlineFiles ?? false,
460
531
  browserBundleFiles: overrides.browserBundleFiles ?? options.browserBundleFiles ?? false,
532
+ generateImage: overrides.generateImage ?? options.generateImage,
533
+ outputPath: overrides.outputPath ?? options.output,
534
+ browserFollowUps: overrides.browserFollowUps ?? options.browserFollowUp ?? [],
461
535
  background: overrides.background ?? undefined,
462
536
  renderPlain: overrides.renderPlain ?? options.renderPlain ?? false,
463
537
  writeOutputPath: overrides.writeOutputPath ?? options.writeOutputPath,
@@ -634,6 +708,7 @@ function buildRunOptionsFromMetadata(metadata) {
634
708
  browserAttachments: stored.browserAttachments,
635
709
  browserInlineFiles: stored.browserInlineFiles,
636
710
  browserBundleFiles: stored.browserBundleFiles,
711
+ browserFollowUps: stored.browserFollowUps,
637
712
  background: stored.background,
638
713
  renderPlain: stored.renderPlain,
639
714
  writeOutputPath: stored.writeOutputPath,
@@ -757,6 +832,9 @@ async function runRootCommand(options) {
757
832
  if (remoteHost && options.remoteChrome) {
758
833
  throw new Error("--remote-host cannot be combined with --remote-chrome.");
759
834
  }
835
+ if (options.browserTab && engine !== "browser") {
836
+ throw new Error("--browser-tab requires --engine browser.");
837
+ }
760
838
  if (optionUsesDefault("azureEndpoint")) {
761
839
  if (process.env.AZURE_OPENAI_ENDPOINT) {
762
840
  options.azureEndpoint = process.env.AZURE_OPENAI_ENDPOINT;
@@ -826,6 +904,10 @@ async function runRootCommand(options) {
826
904
  console.log(chalk.dim("gemini-3.1-pro is API-only today; switching to API."));
827
905
  engine = "api";
828
906
  }
907
+ const browserFollowUpCount = options.browserFollowUp?.filter((entry) => entry.trim().length > 0).length ?? 0;
908
+ if (engine !== "browser" && browserFollowUpCount > 0) {
909
+ throw new Error("--browser-follow-up requires --engine browser.");
910
+ }
829
911
  const effectiveModelId = resolvedModel.startsWith("gemini")
830
912
  ? resolveGeminiModelId(resolvedModel)
831
913
  : isKnownModel(resolvedModel)
@@ -905,6 +987,17 @@ async function runRootCommand(options) {
905
987
  }
906
988
  return;
907
989
  }
990
+ const getSource = (key) => program.getOptionValueSource?.(key) ?? undefined;
991
+ applyBrowserDefaultsFromConfig(options, userConfig, getSource);
992
+ const sessionMode = engine === "browser" ? "browser" : "api";
993
+ const browserModelLabelOverride = sessionMode === "browser" ? resolveBrowserModelLabel(cliModelArg, resolvedModel) : undefined;
994
+ const browserConfig = sessionMode === "browser"
995
+ ? await buildBrowserConfig({
996
+ ...options,
997
+ model: resolvedModel,
998
+ browserModelLabel: browserModelLabelOverride,
999
+ })
1000
+ : undefined;
908
1001
  if (previewMode) {
909
1002
  if (!options.prompt) {
910
1003
  throw new Error("Prompt is required when using --dry-run/preview.");
@@ -940,6 +1033,7 @@ async function runRootCommand(options) {
940
1033
  version: VERSION,
941
1034
  previewMode,
942
1035
  log: console.log,
1036
+ browserConfig,
943
1037
  }, {});
944
1038
  return;
945
1039
  }
@@ -987,6 +1081,7 @@ async function runRootCommand(options) {
987
1081
  }
988
1082
  const duplicateBlocked = await shouldBlockDuplicatePrompt({
989
1083
  prompt: resolvedOptions.prompt,
1084
+ browserFollowUps: resolvedOptions.browserFollowUp,
990
1085
  force: options.force,
991
1086
  sessionStore,
992
1087
  log: console.log,
@@ -1007,23 +1102,12 @@ async function runRootCommand(options) {
1007
1102
  });
1008
1103
  }
1009
1104
  }
1010
- const getSource = (key) => program.getOptionValueSource?.(key) ?? undefined;
1011
- applyBrowserDefaultsFromConfig(options, userConfig, getSource);
1012
1105
  const notifications = resolveNotificationSettings({
1013
1106
  cliNotify: options.notify,
1014
1107
  cliNotifySound: options.notifySound,
1015
1108
  env: process.env,
1016
1109
  config: userConfig.notify,
1017
1110
  });
1018
- const sessionMode = engine === "browser" ? "browser" : "api";
1019
- const browserModelLabelOverride = sessionMode === "browser" ? resolveBrowserModelLabel(cliModelArg, resolvedModel) : undefined;
1020
- const browserConfig = sessionMode === "browser"
1021
- ? await buildBrowserConfig({
1022
- ...options,
1023
- model: resolvedModel,
1024
- browserModelLabel: browserModelLabelOverride,
1025
- })
1026
- : undefined;
1027
1111
  let browserDeps;
1028
1112
  if (browserConfig && remoteHost) {
1029
1113
  browserDeps = {
@@ -1388,6 +1472,10 @@ function printDebugHelp(cliName) {
1388
1472
  ["--browser-chrome-profile <name>", "Reuse cookies from a specific Chrome profile."],
1389
1473
  ["--browser-chrome-path <path>", "Point to a custom Chrome/Chromium binary."],
1390
1474
  ["--browser-cookie-path <path>", "Use a specific Chrome/Chromium cookie store file."],
1475
+ [
1476
+ "--browser-attach-running",
1477
+ "Attach to your current Chrome session through its local remote debugging toggle.",
1478
+ ],
1391
1479
  ["--browser-url <url>", "Alias for --chatgpt-url."],
1392
1480
  ["--browser-timeout <ms|s|m>", "Cap total wait time for the assistant response."],
1393
1481
  ["--browser-input-timeout <ms|s|m>", "Cap how long we wait for the composer textarea."],