pi-oracle 0.6.16 → 0.6.17
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.
- package/CHANGELOG.md +10 -0
- package/README.md +178 -121
- package/extensions/oracle/lib/tools.ts +2 -0
- package/extensions/oracle/shared/job-observability-helpers.d.mts +6 -0
- package/extensions/oracle/shared/job-observability-helpers.mjs +15 -0
- package/extensions/oracle/worker/auth-flow-helpers.mjs +17 -5
- package/extensions/oracle/worker/chatgpt-ui-helpers.mjs +17 -1
- package/extensions/oracle/worker/run-job.mjs +26 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.6.17 - 2026-05-10
|
|
6
|
+
|
|
5
7
|
### Changed
|
|
6
8
|
- made `/oracle-auth` success and failure output easier to scan, with compact source summaries and source-specific troubleshooting for configured Chromium cookie sources
|
|
9
|
+
- tightened README quickstart/command wording around preflight-first `/oracle` behavior, context-rich archive selection, cleanup retention, and preset defaults
|
|
10
|
+
- added the resolved oracle model preset snapshot to `oracle_submit` queued/dispatched output so agents can see what preset will run
|
|
11
|
+
- clarified `oracle_preflight` output so users can see that it validates the persisted pi session, local config, and ChatGPT auth seed created by `oracle_auth`
|
|
12
|
+
- made direct `scripts/oracle-sanity.ts` execution fail fast unless isolated oracle state/jobs dirs are provided, preventing accidental sanity-job leakage into live oracle pollers
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- stopped worker/auth readiness checks from treating ChatGPT's public logged-out composer shell as an authenticated ready state, and made stale-auth failures name the visible login controls instead of reporting a misleading partial-login state
|
|
16
|
+
- made plain `instant` model selection robust against the current ChatGPT model menu by recognizing closed Instant chips and falling back to the top-level model menu when the configure sheet is unavailable
|
|
7
17
|
|
|
8
18
|
## 0.6.16 - 2026-05-07
|
|
9
19
|
|
package/README.md
CHANGED
|
@@ -1,62 +1,118 @@
|
|
|
1
1
|
# pi-oracle
|
|
2
2
|
|
|
3
|
-
`pi-oracle`
|
|
3
|
+
`pi-oracle` lets a `pi` agent send hard, long-running work to ChatGPT.com through the web app, with repo archives, background execution, saved results, and a best-effort wake-up back into `pi` when the answer is ready.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- your real ChatGPT account
|
|
7
|
-
- web-model behavior instead of API usage
|
|
8
|
-
- large repo/context uploads
|
|
9
|
-
- async background execution
|
|
10
|
-
- durable saved responses/artifacts plus best-effort wake-ups back into `pi`
|
|
5
|
+
> Status: experimental public beta. Validated primarily on macOS with Google Chrome/Chromium and `pi` 0.65.0+. Normal oracle jobs run in an isolated browser profile, not your active browser window.
|
|
11
6
|
|
|
12
|
-
|
|
7
|
+
## What a successful run looks like
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
```text
|
|
10
|
+
You: /oracle Review the pending changes. Include the whole repo unless a narrower archive is clearly better.
|
|
11
|
+
|
|
12
|
+
pi-oracle:
|
|
13
|
+
1. preflights local session/auth readiness
|
|
14
|
+
2. builds a context-rich `.tar.zst` repo archive
|
|
15
|
+
3. starts an isolated ChatGPT web runtime in the background
|
|
16
|
+
4. uploads the archive and prompt to ChatGPT.com
|
|
17
|
+
5. saves the response/artifacts under /tmp/oracle-<job-id>/
|
|
18
|
+
6. sends a best-effort wake-up back to the matching pi session
|
|
19
|
+
|
|
20
|
+
Later: /oracle-read <job-id>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
What you are seeing: the local `pi` agent keeps control of context selection and safety checks, while ChatGPT web handles the expensive second-opinion work asynchronously. If the wake-up is missed, the result still lives on disk and can be read by job id.
|
|
24
|
+
|
|
25
|
+
## Who this is for
|
|
26
|
+
|
|
27
|
+
Use `pi-oracle` if you use `pi` and want a larger asynchronous reviewer, planner, or analyst that can use your real ChatGPT web account instead of an API model.
|
|
28
|
+
|
|
29
|
+
It is most useful for:
|
|
30
|
+
|
|
31
|
+
- maintainers reviewing broad repo changes before shipping
|
|
32
|
+
- agents that need a slower second opinion without blocking the main `pi` turn
|
|
33
|
+
- migration, architecture, or failure-mode analysis that benefits from a large archive
|
|
34
|
+
- follow-up questions that should continue the same ChatGPT thread later
|
|
35
|
+
|
|
36
|
+
Do not use it for:
|
|
37
|
+
|
|
38
|
+
- short local coding tasks that `pi` can handle directly
|
|
39
|
+
- projects that must never be uploaded to ChatGPT.com
|
|
40
|
+
- non-macOS environments or machines without the required local browser/tooling
|
|
41
|
+
|
|
42
|
+
## Problem it solves
|
|
15
43
|
|
|
16
|
-
|
|
44
|
+
A normal coding-agent turn is the wrong shape for some work: the task may need a large repo snapshot, a slower web model, a real ChatGPT subscription, artifact downloads, or a durable result that arrives after the active turn ends.
|
|
17
45
|
|
|
18
|
-
|
|
19
|
-
- big code reviews of a repo or pending changes
|
|
20
|
-
- architectural or migration analysis that benefits from a large uploaded archive
|
|
21
|
-
- long-running prompts that may take minutes to finish
|
|
22
|
-
- follow-up questions in the same ChatGPT thread later
|
|
46
|
+
`pi-oracle` makes that workflow explicit and recoverable instead of asking the main agent to manually drive ChatGPT in your real browser window.
|
|
23
47
|
|
|
24
|
-
|
|
25
|
-
- normal short coding tasks that `pi` can handle directly
|
|
26
|
-
- workflows that must never upload project archives to ChatGPT.com
|
|
27
|
-
- environments outside the current supported setup
|
|
48
|
+
## What it does
|
|
28
49
|
|
|
29
|
-
|
|
50
|
+
| Problem | Capability | Proof in this repo |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| Hard tasks need more context than a quick turn should gather. | `/oracle` prompts the agent to preflight, choose a context-rich archive, and submit it to ChatGPT web. | [`prompts/oracle.md`](prompts/oracle.md), `oracle_submit`, archive tests in `scripts/oracle-sanity-*` |
|
|
53
|
+
| Browser automation should not steal focus or mutate your active profile. | Jobs clone an authenticated seed profile into per-job isolated runtime profiles. | [`docs/ORACLE_DESIGN.md`](docs/ORACLE_DESIGN.md), [`extensions/oracle/lib/runtime.ts`](extensions/oracle/lib/runtime.ts) |
|
|
54
|
+
| Long jobs need durability. | Job state, responses, logs, and artifacts persist under `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/`. | [`extensions/oracle/lib/jobs.ts`](extensions/oracle/lib/jobs.ts), `/oracle-read`, `/oracle-status` |
|
|
55
|
+
| ChatGPT auth can expire or drift. | `/oracle-auth` refreshes the isolated auth seed from a configured local Chromium profile, with recovery guidance. | [`extensions/oracle/lib/auth.ts`](extensions/oracle/lib/auth.ts), [`docs/ORACLE_RECOVERY_DRILL.md`](docs/ORACLE_RECOVERY_DRILL.md) |
|
|
56
|
+
| Agents need a simple API, not UI-driving instructions. | The package exposes agent-facing tools: `oracle_preflight`, `oracle_submit`, `oracle_read`, `oracle_auth`, and `oracle_cancel`. | [`extensions/oracle/lib/tools.ts`](extensions/oracle/lib/tools.ts) |
|
|
30
57
|
|
|
31
|
-
|
|
58
|
+
## Fastest way to see it work
|
|
59
|
+
|
|
60
|
+
### 1. Install
|
|
61
|
+
|
|
62
|
+
From npm:
|
|
32
63
|
|
|
33
64
|
```bash
|
|
34
65
|
pi install npm:pi-oracle
|
|
35
66
|
```
|
|
36
67
|
|
|
37
|
-
GitHub:
|
|
68
|
+
Or from GitHub:
|
|
38
69
|
|
|
39
70
|
```bash
|
|
40
71
|
pi install https://github.com/fitchmultz/pi-oracle
|
|
41
72
|
```
|
|
42
73
|
|
|
43
|
-
|
|
74
|
+
### 2. Check requirements
|
|
75
|
+
|
|
76
|
+
You need:
|
|
77
|
+
|
|
78
|
+
- macOS
|
|
79
|
+
- Node.js 22 or newer
|
|
80
|
+
- `pi` 0.65.0 or newer
|
|
81
|
+
- Google Chrome or another Chromium-family browser
|
|
82
|
+
- ChatGPT already signed in to the configured local browser profile
|
|
83
|
+
- `agent-browser`, `tar`, and `zstd` available on the machine
|
|
84
|
+
- a normal persisted `pi` session, not `pi --no-session`
|
|
85
|
+
|
|
86
|
+
### 3. Sync ChatGPT auth once
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
/oracle-auth
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This reads ChatGPT cookies from your configured local browser profile and writes an isolated oracle seed profile. It should not automate your active browser window for normal jobs.
|
|
44
93
|
|
|
45
|
-
|
|
46
|
-
2. Make sure ChatGPT already works in your configured local browser profile.
|
|
47
|
-
3. Make sure these are installed: Google Chrome, `agent-browser`, `tar`, and `zstd`.
|
|
48
|
-
4. Optional: create `~/.pi/agent/extensions/oracle.json` if you want non-default settings.
|
|
49
|
-
5. Run `/oracle-auth`.
|
|
50
|
-
6. Run `/oracle Review the current pending changes. Include the whole repo unless a narrower archive is clearly better.`
|
|
51
|
-
7. Wait for the one-time best-effort wake-up, or check `/oracle-status`.
|
|
94
|
+
### 4. Submit a tiny job
|
|
52
95
|
|
|
53
|
-
|
|
96
|
+
```text
|
|
97
|
+
/oracle Read README.md and package.json. Tell me in five bullets what this package does and who should not use it.
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Expected result:
|
|
54
101
|
|
|
55
|
-
|
|
102
|
+
- The `/oracle` prompt now runs an early oracle preflight before expensive repo reading or archive creation.
|
|
103
|
+
- The agent chooses a context-rich relevant archive up to the 250 MB ceiling, not the smallest possible one-file slice when nearby context helps.
|
|
104
|
+
- `oracle_submit` creates or queues a job.
|
|
105
|
+
- If local packing is too large, the prompt treats that as a retryable archive-selection failure and narrows automatically before surfacing the problem.
|
|
106
|
+
- The job uploads a repo archive to ChatGPT.com, capped at 250 MiB after default exclusions/pruning.
|
|
107
|
+
- The response is saved under `/tmp/oracle-<job-id>/response.md` by default.
|
|
108
|
+
- The matching `pi` session gets one best-effort wake-up when the job finishes.
|
|
56
109
|
|
|
57
|
-
If
|
|
110
|
+
If the wake-up does not arrive, run:
|
|
58
111
|
|
|
59
|
-
|
|
112
|
+
```text
|
|
113
|
+
/oracle-status
|
|
114
|
+
/oracle-read <job-id>
|
|
115
|
+
```
|
|
60
116
|
|
|
61
117
|
## Example requests
|
|
62
118
|
|
|
@@ -73,46 +129,53 @@ If you miss the one-time wake-up, the result is still saved durably in the oracl
|
|
|
73
129
|
```
|
|
74
130
|
|
|
75
131
|
```text
|
|
76
|
-
/oracle-followup <job-id> Tighten the migration plan around rollback risk, and include the most relevant surrounding files/docs as long as the archive stays comfortably within the limit.
|
|
132
|
+
/oracle-followup <job-id> Tighten the migration plan around rollback risk, and include the most relevant surrounding files/docs as long as the archive stays comfortably within the 250 MiB limit.
|
|
77
133
|
```
|
|
78
134
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
## High-level flow
|
|
135
|
+
## How it works
|
|
82
136
|
|
|
83
137
|
```mermaid
|
|
84
138
|
flowchart LR
|
|
85
139
|
A["/oracle request"] --> B["Agent preflights, then gathers a context-rich relevant repo slice"]
|
|
86
|
-
B --> C["
|
|
87
|
-
C --> D["
|
|
88
|
-
D --> E["
|
|
89
|
-
E --> F["
|
|
90
|
-
F --> G["
|
|
140
|
+
B --> C["Agent chooses context-rich archive inputs"]
|
|
141
|
+
C --> D["oracle_submit builds .tar.zst archive"]
|
|
142
|
+
D --> E["Detached worker clones isolated auth seed profile"]
|
|
143
|
+
E --> F["ChatGPT web receives archive + prompt"]
|
|
144
|
+
F --> G["Response/artifacts saved under oracle job dir"]
|
|
145
|
+
G --> H["Best-effort wake-up to matching pi session"]
|
|
91
146
|
```
|
|
92
147
|
|
|
93
|
-
|
|
148
|
+
Key design choices:
|
|
94
149
|
|
|
95
|
-
|
|
150
|
+
- **Prompt templates own context gathering.** `/oracle` and `/oracle-followup` tell the agent how to preflight, gather context, choose archive inputs, and then stop after dispatch.
|
|
151
|
+
- **Tools own execution.** `oracle_submit` builds the archive, admits or queues the job, starts the worker, and returns immediately.
|
|
152
|
+
- **Auth uses a seed profile.** `/oracle-auth` imports cookies into an isolated seed profile; each job clones that seed into its own temporary runtime profile.
|
|
153
|
+
- **Follow-ups preserve ChatGPT thread state.** `/oracle-followup <job-id> ...` resolves the prior job's saved ChatGPT URL and submits the next prompt with `followUpJobId`.
|
|
154
|
+
- **Wake-up is best effort, storage is durable.** A missed wake-up does not lose the result.
|
|
155
|
+
|
|
156
|
+
## Commands and tools
|
|
96
157
|
|
|
97
158
|
User-facing commands:
|
|
98
|
-
|
|
99
|
-
- `/oracle
|
|
100
|
-
- `/oracle-
|
|
101
|
-
- `/oracle-
|
|
102
|
-
- `/oracle-
|
|
103
|
-
- `/oracle-
|
|
104
|
-
- `/oracle-
|
|
159
|
+
|
|
160
|
+
- `/oracle <request>` — prepare context and dispatch a ChatGPT web oracle job
|
|
161
|
+
- `/oracle-followup <job-id> <request>` — continue an earlier oracle job in the same ChatGPT thread
|
|
162
|
+
- `/oracle-auth` — sync ChatGPT cookies into the isolated oracle auth seed profile
|
|
163
|
+
- `/oracle-read [job-id]` — inspect job status and saved response preview
|
|
164
|
+
- `/oracle-status [job-id]` — inspect a job or list recent job ids when no explicit id is given
|
|
165
|
+
- `/oracle-cancel <job-id>` — cancel a queued or active job
|
|
166
|
+
- `/oracle-clean <job-id|all>` — remove temp files for terminal jobs; recently woken terminal jobs may stay retained briefly, and a blocked cleanup returns the next eligible cleanup time
|
|
105
167
|
|
|
106
168
|
Agent-facing tools:
|
|
169
|
+
|
|
107
170
|
- `oracle_preflight`
|
|
108
171
|
- `oracle_auth`
|
|
109
172
|
- `oracle_submit`
|
|
110
173
|
- `oracle_read`
|
|
111
174
|
- `oracle_cancel`
|
|
112
175
|
|
|
113
|
-
##
|
|
176
|
+
## Configuration
|
|
114
177
|
|
|
115
|
-
Most users can start with
|
|
178
|
+
Most users can start with defaults. Set an agent-level config only when you need a non-default preset or browser profile.
|
|
116
179
|
|
|
117
180
|
`~/.pi/agent/extensions/oracle.json`
|
|
118
181
|
|
|
@@ -128,14 +191,16 @@ Most users can start with the packaged defaults and only set the browser profile
|
|
|
128
191
|
```
|
|
129
192
|
|
|
130
193
|
Notes:
|
|
194
|
+
|
|
131
195
|
- `defaults.preset` is the default ChatGPT model preset for oracle jobs.
|
|
132
|
-
- The canonical preset ids live in `extensions/oracle/lib/config.ts
|
|
133
|
-
- If the packaged default is fine,
|
|
134
|
-
-
|
|
196
|
+
- The canonical preset ids live in [`extensions/oracle/lib/config.ts`](extensions/oracle/lib/config.ts).
|
|
197
|
+
- If the packaged default is fine, omit `defaults.preset`.
|
|
198
|
+
- When an agent is unsure which oracle preset fits, it should omit `preset` and use the configured default model instead of asking by default.
|
|
199
|
+
- You usually do not need browser paths unless auto-detection fails.
|
|
135
200
|
|
|
136
201
|
### Custom Chromium cookie sources
|
|
137
202
|
|
|
138
|
-
|
|
203
|
+
Use this only for a Chromium-family browser that the default cookie importer cannot read.
|
|
139
204
|
|
|
140
205
|
Before running `/oracle-auth` with this path:
|
|
141
206
|
|
|
@@ -143,7 +208,7 @@ Before running `/oracle-auth` with this path:
|
|
|
143
208
|
2. Fully quit the browser so its `Cookies` database is stable.
|
|
144
209
|
3. Find the profile `Cookies` SQLite DB path.
|
|
145
210
|
4. Find the browser's macOS Keychain safe-storage item account and service name.
|
|
146
|
-
5. Configure all of `browser.executablePath`, `auth.chromeCookiePath`, and `auth.chromiumKeychain` in
|
|
211
|
+
5. Configure all of `browser.executablePath`, `auth.chromeCookiePath`, and `auth.chromiumKeychain` in `~/.pi/agent/extensions/oracle.json`.
|
|
147
212
|
|
|
148
213
|
Example Helium config:
|
|
149
214
|
|
|
@@ -164,9 +229,7 @@ Example Helium config:
|
|
|
164
229
|
}
|
|
165
230
|
```
|
|
166
231
|
|
|
167
|
-
`auth.chromeCookiePath`
|
|
168
|
-
|
|
169
|
-
If macOS prompts for Keychain access during `/oracle-auth`, allow access for the configured browser safe-storage item. If auth still fails after cookies are synced, the cookie DB may be stale, from the wrong profile, or for an account that is logged out; reopen the configured browser profile, confirm ChatGPT works there, quit the browser, and rerun `/oracle-auth`.
|
|
232
|
+
`auth.chromeCookiePath` must be paired with `auth.chromiumKeychain`; partial config is rejected so oracle does not silently fall back to another browser source.
|
|
170
233
|
|
|
171
234
|
## Available presets
|
|
172
235
|
|
|
@@ -181,38 +244,34 @@ If macOS prompts for Keychain access during `/oracle-auth`, allow access for the
|
|
|
181
244
|
| `instant` | Instant |
|
|
182
245
|
| `instant_auto_switch` | Instant - Auto-switch to Thinking Enabled |
|
|
183
246
|
|
|
184
|
-
`oracle_submit` accepts
|
|
247
|
+
`oracle_submit` accepts canonical preset ids or a matching human-readable preset label. Keep config values on canonical ids.
|
|
185
248
|
|
|
186
|
-
|
|
187
|
-
- `browser.runMode`
|
|
188
|
-
- `browser.args`
|
|
189
|
-
- `browser.authSeedProfileDir`
|
|
190
|
-
- `browser.runtimeProfilesDir`
|
|
191
|
-
- `cleanup.completeJobRetentionMs`
|
|
192
|
-
- `cleanup.failedJobRetentionMs`
|
|
249
|
+
## Outputs and cleanup
|
|
193
250
|
|
|
194
|
-
|
|
251
|
+
- Jobs persist response text, metadata, logs, and artifacts under `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/` by default.
|
|
252
|
+
- Jobs can queue automatically when runtime capacity is full.
|
|
253
|
+
- Completion delivery into `pi` is one-time best-effort wake-up based.
|
|
254
|
+
- `/oracle-read [job-id]` and `oracle_read({ jobId })` inspect saved output later.
|
|
255
|
+
- `/oracle-clean` removes terminal job temp files, but can briefly refuse cleanup after a wake-up so the follow-up turn can still read the saved paths.
|
|
195
256
|
|
|
196
|
-
##
|
|
257
|
+
## Privacy and local data
|
|
197
258
|
|
|
198
|
-
|
|
199
|
-
- Jobs can queue automatically if runtime capacity is full.
|
|
200
|
-
- Completion delivery into `pi` is one-time best-effort wake-up based; duplicate poller scans are deduped in job state.
|
|
201
|
-
- If you miss the wake-up, use `/oracle-read [job-id]` to inspect the saved response preview.
|
|
202
|
-
- `/oracle-status [job-id]` still shows saved job metadata and lists recent job ids when you omit the id.
|
|
203
|
-
- Agent callers can use `oracle_read({ jobId })`.
|
|
204
|
-
- If a prior oracle run failed because ChatGPT login was required or the worker explicitly said to rerun `/oracle-auth`, agent callers can run `oracle_auth({})` once and then retry the submission once.
|
|
205
|
-
- `/oracle-clean` can still refuse a terminal job briefly after a wake-up send so saved response/artifact paths survive the follow-up turn; when that guard applies, it returns the next eligible cleanup time.
|
|
259
|
+
This extension is local-first, but it handles sensitive local and project data:
|
|
206
260
|
|
|
207
|
-
|
|
261
|
+
- `/oracle-auth` reads ChatGPT cookies from the configured local browser profile.
|
|
262
|
+
- `oracle_submit` uploads selected project archives to ChatGPT.com.
|
|
263
|
+
- Responses, logs, and artifacts are written to the configured oracle jobs directory.
|
|
208
264
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
-
|
|
214
|
-
-
|
|
215
|
-
-
|
|
265
|
+
Review the code and design docs before using it with private or regulated material.
|
|
266
|
+
|
|
267
|
+
## Current limits
|
|
268
|
+
|
|
269
|
+
- Experimental public beta, validated primarily on macOS.
|
|
270
|
+
- ChatGPT UI, auth, model controls, and artifact download behavior can drift.
|
|
271
|
+
- Archive uploads are capped at 250 MiB after default exclusions and automatic whole-repo pruning.
|
|
272
|
+
- A real ChatGPT web session is required.
|
|
273
|
+
- The README currently uses command-level proof and design docs; no public screenshot or demo GIF is checked into the repo.
|
|
274
|
+
- Production hardening should keep focusing on UI drift detection, auth recovery, artifact capture, and environment diagnostics.
|
|
216
275
|
|
|
217
276
|
## Troubleshooting
|
|
218
277
|
|
|
@@ -221,6 +280,7 @@ Project config should only override safe, non-privileged settings.
|
|
|
221
280
|
- Make sure ChatGPT works in the same local browser profile you configured.
|
|
222
281
|
- For custom Chromium cookie sources, confirm `auth.chromeCookiePath` points at that profile's `Cookies` DB and `auth.chromiumKeychain.services` names the browser's safe-storage Keychain service.
|
|
223
282
|
- Re-run `/oracle-auth`.
|
|
283
|
+
- Agent callers can use `oracle_auth({})` once before retrying a stale-auth oracle submission.
|
|
224
284
|
- If ChatGPT is half-logged-in or challenge flow state looks weird, finish the login/challenge in the headed auth browser and retry.
|
|
225
285
|
|
|
226
286
|
### Custom Chromium auth says cookies synced but the session is rejected
|
|
@@ -234,10 +294,10 @@ This usually means the cookie import worked but the source cookies are not the a
|
|
|
234
294
|
5. Confirm `auth.chromiumKeychain.services` names the browser's safe-storage Keychain service for that DB.
|
|
235
295
|
6. Re-run `/oracle-auth`.
|
|
236
296
|
|
|
237
|
-
### You hit a challenge
|
|
297
|
+
### You hit a challenge or verification page
|
|
238
298
|
|
|
239
299
|
- Solve it in the auth/bootstrap browser if prompted.
|
|
240
|
-
-
|
|
300
|
+
- Re-run `/oracle-auth` before submitting jobs again.
|
|
241
301
|
|
|
242
302
|
### You see "Oracle requires a persisted pi session"
|
|
243
303
|
|
|
@@ -247,19 +307,19 @@ This usually means the cookie import worked but the source cookies are not the a
|
|
|
247
307
|
### A job finished but no wake-up arrived
|
|
248
308
|
|
|
249
309
|
- Use `/oracle-read [job-id]` to inspect the saved response preview.
|
|
250
|
-
- Use `/oracle-status
|
|
251
|
-
- Agent callers can use `oracle_read({ jobId })
|
|
310
|
+
- Use `/oracle-status` if you need help finding a recent job id.
|
|
311
|
+
- Agent callers can use `oracle_read({ jobId })`.
|
|
252
312
|
- Results are still saved on disk even if the reminder turn does not land.
|
|
253
313
|
|
|
254
314
|
### `/oracle-clean` refuses a terminal job right after completion
|
|
255
315
|
|
|
256
316
|
- This can happen during the short post-send retention grace window after a wake-up was sent.
|
|
257
|
-
- The command
|
|
258
|
-
- Wait until that time, then rerun `/oracle-clean
|
|
317
|
+
- The command returns a `Retry after ...` timestamp when that guard is active.
|
|
318
|
+
- Wait until that time, then rerun `/oracle-clean <job-id|all>`.
|
|
259
319
|
|
|
260
320
|
### `agent-browser`, `tar`, or `zstd` is missing
|
|
261
321
|
|
|
262
|
-
|
|
322
|
+
Install the missing local dependency and rerun the command.
|
|
263
323
|
|
|
264
324
|
### Auto-detection picked the wrong browser profile
|
|
265
325
|
|
|
@@ -269,25 +329,11 @@ This usually means the cookie import worked but the source cookies are not the a
|
|
|
269
329
|
|
|
270
330
|
### You want more details about a failed run
|
|
271
331
|
|
|
272
|
-
|
|
273
|
-
- The worker log and captured diagnostics are stored there.
|
|
332
|
+
Inspect the job directory under `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/`. The worker log and captured diagnostics are stored there.
|
|
274
333
|
|
|
275
|
-
##
|
|
334
|
+
## Verification
|
|
276
335
|
|
|
277
|
-
|
|
278
|
-
- `docs/ORACLE_RECOVERY_DRILL.md` — safe expired-auth recovery validation drill
|
|
279
|
-
- `docs/ORACLE_ISOLATED_PI_VALIDATION.md` — repeatable isolated `pi` session smoke test for local-extension validation
|
|
280
|
-
|
|
281
|
-
## Privacy / local data
|
|
282
|
-
|
|
283
|
-
This extension is local-first, but it does read and persist local data:
|
|
284
|
-
- `/oracle-auth` reads ChatGPT cookies from the configured local browser profile
|
|
285
|
-
- job archives are uploaded to ChatGPT.com
|
|
286
|
-
- responses and artifacts are written under the configured oracle jobs dir
|
|
287
|
-
|
|
288
|
-
Review the code and design docs before using it with sensitive material.
|
|
289
|
-
|
|
290
|
-
## Validation helpers
|
|
336
|
+
Useful local checks:
|
|
291
337
|
|
|
292
338
|
```bash
|
|
293
339
|
npm run check:oracle-extension
|
|
@@ -295,21 +341,32 @@ npm run typecheck
|
|
|
295
341
|
npm run typecheck:worker-helpers
|
|
296
342
|
npm run sanity:oracle
|
|
297
343
|
npm run pack:check
|
|
298
|
-
# conventional local gate
|
|
299
344
|
npm test
|
|
300
|
-
# or all at once
|
|
301
345
|
npm run verify:oracle
|
|
302
346
|
```
|
|
303
347
|
|
|
304
|
-
`npm publish` is
|
|
348
|
+
`npm publish` is guarded by `prepublishOnly`, which runs `npm run verify:oracle`.
|
|
349
|
+
|
|
350
|
+
For end-to-end local-extension smoke testing, use [`docs/ORACLE_ISOLATED_PI_VALIDATION.md`](docs/ORACLE_ISOLATED_PI_VALIDATION.md). That workflow launches isolated `pi` sessions against this checkout and uses `instant` or `thinking_light`, as required by the project validation policy.
|
|
305
351
|
|
|
306
|
-
##
|
|
352
|
+
## Project map
|
|
307
353
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
354
|
+
| Path | Purpose |
|
|
355
|
+
| --- | --- |
|
|
356
|
+
| [`extensions/oracle/index.ts`](extensions/oracle/index.ts) | Extension entrypoint |
|
|
357
|
+
| `extensions/oracle/lib/` | Commands, tools, config, jobs, queueing, runtime, poller |
|
|
358
|
+
| `extensions/oracle/worker/` | Detached ChatGPT web worker and UI/auth helpers |
|
|
359
|
+
| `extensions/oracle/shared/` | Shared process, state, job, and observability helpers |
|
|
360
|
+
| [`prompts/oracle.md`](prompts/oracle.md) | `/oracle` prompt-template workflow |
|
|
361
|
+
| [`prompts/oracle-followup.md`](prompts/oracle-followup.md) | `/oracle-followup` prompt-template workflow |
|
|
362
|
+
| `scripts/oracle-sanity-*` | Local sanity and archive-safety checks |
|
|
363
|
+
| [`docs/ORACLE_DESIGN.md`](docs/ORACLE_DESIGN.md) | Architecture, lifecycle, queueing, persistence, recovery behavior |
|
|
364
|
+
| [`docs/ORACLE_ISOLATED_PI_VALIDATION.md`](docs/ORACLE_ISOLATED_PI_VALIDATION.md) | Repeatable isolated `pi` validation workflow |
|
|
365
|
+
| [`docs/ORACLE_RECOVERY_DRILL.md`](docs/ORACLE_RECOVERY_DRILL.md) | Safe expired-auth recovery drill |
|
|
366
|
+
|
|
367
|
+
## Next action
|
|
368
|
+
|
|
369
|
+
Install the package, run `/oracle-auth`, then submit the tiny README/package review job above. If you are evaluating the design before running it, start with [`docs/ORACLE_DESIGN.md`](docs/ORACLE_DESIGN.md).
|
|
313
370
|
|
|
314
371
|
## License
|
|
315
372
|
|
|
@@ -957,12 +957,14 @@ function formatOraclePreflightResponse(details: OraclePreflightDetails): string
|
|
|
957
957
|
"Oracle preflight ready.",
|
|
958
958
|
details.session.sessionFile ? `Persisted session: ${details.session.sessionFile}` : undefined,
|
|
959
959
|
details.auth.seedProfileDir ? `Auth seed profile: ${details.auth.seedProfileDir}` : undefined,
|
|
960
|
+
"Preflight validates the persisted pi session, local oracle config, and ChatGPT auth seed created by oracle_auth.",
|
|
960
961
|
"You can continue with oracle context gathering and submission.",
|
|
961
962
|
].filter(Boolean).join("\n");
|
|
962
963
|
}
|
|
963
964
|
|
|
964
965
|
return [
|
|
965
966
|
`Oracle preflight blocked: ${details.error?.message ?? "unknown blocker"}`,
|
|
967
|
+
"Preflight checks the persisted pi session, local oracle config, and ChatGPT auth seed before any archive work starts.",
|
|
966
968
|
details.error?.suggestedNextStep ? `Suggested next step: ${details.error.suggestedNextStep}` : undefined,
|
|
967
969
|
].filter(Boolean).join("\n");
|
|
968
970
|
}
|
|
@@ -12,6 +12,12 @@ export interface OracleJobSummaryLike {
|
|
|
12
12
|
heartbeatAt?: string;
|
|
13
13
|
projectId: string;
|
|
14
14
|
sessionId: string;
|
|
15
|
+
selection?: {
|
|
16
|
+
preset?: string;
|
|
17
|
+
modelFamily?: string;
|
|
18
|
+
effort?: string;
|
|
19
|
+
autoSwitchToThinking?: boolean;
|
|
20
|
+
};
|
|
15
21
|
followUpToJobId?: string;
|
|
16
22
|
chatUrl?: string;
|
|
17
23
|
conversationId?: string;
|
|
@@ -46,6 +46,20 @@ function formatAutoPrunedArchiveMessage(autoPrunedPrefixes) {
|
|
|
46
46
|
return `Archive auto-pruned generic generated-output-name dirs to fit size limit: ${autoPrunedPrefixes.map((entry) => `${entry.relativePath}/ (${formatBytes(entry.bytes)})`).join(", ")}`;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* @param {OracleJobSummaryLike["selection"]} selection
|
|
51
|
+
* @returns {string | undefined}
|
|
52
|
+
*/
|
|
53
|
+
function formatOracleSelection(selection) {
|
|
54
|
+
if (!selection?.preset || !selection.modelFamily) return undefined;
|
|
55
|
+
const details = [
|
|
56
|
+
`family=${selection.modelFamily}`,
|
|
57
|
+
selection.effort ? `effort=${selection.effort}` : undefined,
|
|
58
|
+
selection.autoSwitchToThinking === true ? "auto-switch-to-thinking=true" : undefined,
|
|
59
|
+
].filter(Boolean).join(", ");
|
|
60
|
+
return `Model preset: ${selection.preset}${details ? ` (${details})` : ""}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
49
63
|
const ACTIVE_SUMMARY_STATUSES = new Set(["preparing", "submitted", "waiting"]);
|
|
50
64
|
const DEFAULT_ORACLE_HEARTBEAT_STALE_MS = 3 * 60 * 1000;
|
|
51
65
|
const DEFAULT_ACTIVE_JOB_POLL_HINT_SECONDS = 15;
|
|
@@ -221,6 +235,7 @@ export function formatOracleSubmitResponse(job, options) {
|
|
|
221
235
|
`${options.queued ? "Oracle job queued" : "Oracle job dispatched"}: ${job.id}`,
|
|
222
236
|
options.queued && options.queuePosition && options.queueDepth ? `Queue position: ${options.queuePosition} of ${options.queueDepth}` : undefined,
|
|
223
237
|
job.followUpToJobId ? `Follow-up to: ${job.followUpToJobId}` : undefined,
|
|
238
|
+
formatOracleSelection(job.selection),
|
|
224
239
|
`Prompt: ${job.promptPath}`,
|
|
225
240
|
`Archive: ${job.archivePath}`,
|
|
226
241
|
formatAutoPrunedArchiveMessage(options.autoPrunedPrefixes),
|
|
@@ -109,7 +109,9 @@ export function classifyChatAuthPage(args) {
|
|
|
109
109
|
return { state: "transient_outage_error", message: `ChatGPT is showing a transient outage/error page. Logs: ${args.logPath}` };
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
const probeHasAccountIdentity = args.probe?.bodyHasId === true || args.probe?.bodyHasEmail === true;
|
|
113
|
+
|
|
114
|
+
if (args.probe?.status === 401 || (args.probe?.status === 403 && (!onAllowedOrigin || !hasUsableComposer))) {
|
|
113
115
|
return {
|
|
114
116
|
state: "login_required",
|
|
115
117
|
message:
|
|
@@ -120,7 +122,7 @@ export function classifyChatAuthPage(args) {
|
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
if (args.probe?.onAuthPage) {
|
|
123
|
-
if (
|
|
125
|
+
if (probeHasAccountIdentity) {
|
|
124
126
|
return {
|
|
125
127
|
state: "auth_transitioning",
|
|
126
128
|
message:
|
|
@@ -137,6 +139,16 @@ export function classifyChatAuthPage(args) {
|
|
|
137
139
|
};
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
if (onAllowedOrigin && hasUsableComposer && args.probe?.domLoginCta && !probeHasAccountIdentity) {
|
|
143
|
+
return {
|
|
144
|
+
state: "login_required",
|
|
145
|
+
message:
|
|
146
|
+
`ChatGPT still shows public login controls after syncing cookies from ${args.cookieSourceLabel}. ` +
|
|
147
|
+
`The cookie DB may be stale, from the wrong browser profile, or for an account that is logged out. ` +
|
|
148
|
+
`Check auth.chromeProfile/auth.chromeCookiePath/auth.chromiumKeychain and inspect ${args.logPath}.`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
140
152
|
if (onAllowedOrigin && (args.probe?.status === 200 || args.probe?.status === 403) && hasUsableComposer) {
|
|
141
153
|
if (!args.probe?.domLoginCta) {
|
|
142
154
|
return {
|
|
@@ -145,12 +157,12 @@ export function classifyChatAuthPage(args) {
|
|
|
145
157
|
};
|
|
146
158
|
}
|
|
147
159
|
|
|
160
|
+
// The public logged-out composer case returned above, so a remaining visible login CTA here still has account-like probe data.
|
|
148
161
|
return {
|
|
149
162
|
state: "auth_transitioning",
|
|
150
163
|
message:
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
: `ChatGPT accepted cookies but is still hydrating/auth-selecting. Logs: ${args.logPath}`,
|
|
164
|
+
`ChatGPT backend probe returned account-like fields, but the shell still shows public login controls. ` +
|
|
165
|
+
`Trying to resolve the account shell. Logs: ${args.logPath}`,
|
|
154
166
|
};
|
|
155
167
|
}
|
|
156
168
|
|
|
@@ -30,6 +30,7 @@ const THINKING_EFFORT_COMBOBOX_LABEL = "Thinking effort";
|
|
|
30
30
|
const PRO_THINKING_EFFORT_COMBOBOX_LABEL = "Pro thinking effort";
|
|
31
31
|
const EFFORT_LABELS = new Set(["Light", "Standard", "Extended", "Heavy"]);
|
|
32
32
|
const BARE_EFFORT_PATTERN = /^(light|standard|extended|heavy)(?:, click to remove)?$/i;
|
|
33
|
+
const INSTANT_CHIP_PATTERN = /^instant(?:, click to remove)?$/i;
|
|
33
34
|
const THINKING_CHIP_PATTERN = /^(?:(light|standard|extended|heavy)\s+)?thinking(?:, click to remove)?$/i;
|
|
34
35
|
const PRO_CHIP_PATTERN = /^(?:(light|standard|extended|heavy)\s+)?pro(?:, click to remove)?$/i;
|
|
35
36
|
const MODEL_FAMILY_CONTROL_KINDS = new Set(["button", "radio", "menuitemradio"]);
|
|
@@ -125,6 +126,12 @@ function parseComposerChipSelection(label) {
|
|
|
125
126
|
};
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
if (INSTANT_CHIP_PATTERN.test(normalized)) {
|
|
130
|
+
return {
|
|
131
|
+
modelFamily: /** @type {OracleUiModelFamily} */ ("instant"),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
128
135
|
const thinkingMatch = normalized.match(THINKING_CHIP_PATTERN);
|
|
129
136
|
if (thinkingMatch) {
|
|
130
137
|
return {
|
|
@@ -240,11 +247,20 @@ export function snapshotHasModelConfigurationUi(snapshot) {
|
|
|
240
247
|
.filter((family) => matchesModelFamilyLabel(entry.label, family)),
|
|
241
248
|
),
|
|
242
249
|
);
|
|
250
|
+
const visibleRadioFamilies = new Set(
|
|
251
|
+
entries
|
|
252
|
+
.filter((entry) => entry.kind === "radio" && typeof entry.label === "string")
|
|
253
|
+
.flatMap((entry) =>
|
|
254
|
+
/** @type {OracleUiModelFamily[]} */ (["instant", "thinking", "pro"])
|
|
255
|
+
.filter((family) => matchesModelFamilyLabel(entry.label, family)),
|
|
256
|
+
),
|
|
257
|
+
);
|
|
243
258
|
const hasCloseButton = entries.some((entry) => entry.kind === "button" && entry.label === "Close" && !entry.disabled);
|
|
259
|
+
const hasIntelligenceHeading = entries.some((entry) => entry.kind === "heading" && normalizeText(entry.label) === "Intelligence" && !entry.disabled);
|
|
244
260
|
const hasEffortCombobox = entries.some(
|
|
245
261
|
(entry) => entry.kind === "combobox" && EFFORT_LABELS.has(entry.value || "") && !entry.disabled,
|
|
246
262
|
);
|
|
247
|
-
return visibleFamilies.size >= 2 || hasCloseButton || hasEffortCombobox;
|
|
263
|
+
return visibleFamilies.size >= 2 || visibleRadioFamilies.size >= 2 || hasCloseButton || hasIntelligenceHeading || hasEffortCombobox;
|
|
248
264
|
}
|
|
249
265
|
|
|
250
266
|
/**
|
|
@@ -705,6 +705,14 @@ function matchesModelConfigurationOpener(candidate) {
|
|
|
705
705
|
|| /^(?:(?:Light|Standard|Extended|Heavy) )?Pro(?:, click to remove)?$/i.test(label);
|
|
706
706
|
}
|
|
707
707
|
|
|
708
|
+
function canUseOpenModelMenuForSelection(snapshot, selection) {
|
|
709
|
+
if (selection.modelFamily !== "instant" || selection.autoSwitchToThinking === true) return false;
|
|
710
|
+
return Boolean(findEntry(
|
|
711
|
+
snapshot,
|
|
712
|
+
(candidate) => candidate.kind === "menuitemradio" && matchesModelFamilyControl(candidate, selection.modelFamily),
|
|
713
|
+
));
|
|
714
|
+
}
|
|
715
|
+
|
|
708
716
|
function composerControlsVisible(snapshot) {
|
|
709
717
|
const entries = parseSnapshotEntries(snapshot);
|
|
710
718
|
const hasComposer = entries.some(
|
|
@@ -802,25 +810,35 @@ function classifyChatPage({ job, url, snapshot, body, probe }) {
|
|
|
802
810
|
const onAuthPath = typeof url === "string" && url.includes("/auth/");
|
|
803
811
|
const hasUsableComposer = snapshotHasUsableComposerControls(snapshot);
|
|
804
812
|
|
|
805
|
-
|
|
813
|
+
const probeHasAccountIdentity = probe?.bodyHasId === true || probe?.bodyHasEmail === true;
|
|
814
|
+
|
|
815
|
+
if (probe?.status === 401 || (probe?.status === 403 && (!onAllowedOrigin || !hasUsableComposer))) {
|
|
806
816
|
return { state: "login_required", message: "ChatGPT login is required. Run /oracle-auth." };
|
|
807
817
|
}
|
|
808
818
|
|
|
809
819
|
if (onAuthPath || probe?.onAuthPage) {
|
|
810
|
-
if (
|
|
820
|
+
if (probeHasAccountIdentity) {
|
|
811
821
|
return {
|
|
812
822
|
state: "auth_transitioning",
|
|
813
|
-
message: "ChatGPT is on an auth page even though the backend
|
|
823
|
+
message: "ChatGPT is on an auth page even though the backend probe returned account-like fields. Rerun /oracle-auth.",
|
|
814
824
|
};
|
|
815
825
|
}
|
|
816
826
|
return { state: "login_required", message: "ChatGPT login is required. Run /oracle-auth." };
|
|
817
827
|
}
|
|
818
828
|
|
|
829
|
+
if (onAllowedOrigin && hasUsableComposer && probe?.domLoginCta && !probeHasAccountIdentity) {
|
|
830
|
+
return {
|
|
831
|
+
state: "login_required",
|
|
832
|
+
message: "ChatGPT login is required: the chat shell still shows public Log in/Sign up controls. Run /oracle-auth.",
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
|
|
819
836
|
if (onAllowedOrigin && (probe?.status === 200 || probe?.status === 403) && hasUsableComposer) {
|
|
820
|
-
if (probe?.domLoginCta
|
|
837
|
+
if (probe?.domLoginCta) {
|
|
838
|
+
// The public logged-out composer case returned above, so a remaining visible login CTA here still has account-like probe data.
|
|
821
839
|
return {
|
|
822
840
|
state: "auth_transitioning",
|
|
823
|
-
message: "ChatGPT backend
|
|
841
|
+
message: "ChatGPT backend probe returned account-like fields, but the web shell still shows public login controls. Rerun /oracle-auth.",
|
|
824
842
|
};
|
|
825
843
|
}
|
|
826
844
|
return { state: "authenticated_and_ready", message: "ChatGPT is authenticated and ready." };
|
|
@@ -875,7 +893,7 @@ async function waitForOracleReady(job) {
|
|
|
875
893
|
}
|
|
876
894
|
if (elapsedMs >= 15_000) {
|
|
877
895
|
await captureDiagnostics(job, "preflight-auth-transition");
|
|
878
|
-
throw new Error("ChatGPT
|
|
896
|
+
throw new Error(classification.message || "ChatGPT auth did not settle into a ready chat shell. Rerun /oracle-auth.");
|
|
879
897
|
}
|
|
880
898
|
await sleep(1000);
|
|
881
899
|
continue;
|
|
@@ -1009,6 +1027,7 @@ async function openModelConfiguration(job) {
|
|
|
1009
1027
|
await agentBrowser(job, "wait", "800");
|
|
1010
1028
|
const after = await snapshotText(job);
|
|
1011
1029
|
if (snapshotHasModelConfigurationUi(after)) return after;
|
|
1030
|
+
if (canUseOpenModelMenuForSelection(after, job.selection)) return after;
|
|
1012
1031
|
|
|
1013
1032
|
const configureEntry = findEntry(
|
|
1014
1033
|
after,
|
|
@@ -1020,6 +1039,7 @@ async function openModelConfiguration(job) {
|
|
|
1020
1039
|
await agentBrowser(job, "wait", "1200");
|
|
1021
1040
|
const postConfigure = await snapshotText(job);
|
|
1022
1041
|
if (snapshotHasModelConfigurationUi(postConfigure)) return postConfigure;
|
|
1042
|
+
if (canUseOpenModelMenuForSelection(postConfigure, job.selection)) return postConfigure;
|
|
1023
1043
|
}
|
|
1024
1044
|
}
|
|
1025
1045
|
|