pi-oracle 0.1.12 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/README.md +26 -10
- package/docs/ORACLE_DESIGN.md +593 -0
- package/docs/ORACLE_RECOVERY_DRILL.md +127 -0
- package/extensions/oracle/index.ts +15 -4
- package/extensions/oracle/lib/commands.ts +39 -12
- package/extensions/oracle/lib/config.ts +2 -2
- package/extensions/oracle/lib/jobs.ts +510 -73
- package/extensions/oracle/lib/locks.ts +99 -13
- package/extensions/oracle/lib/poller.ts +224 -38
- package/extensions/oracle/lib/queue.ts +193 -0
- package/extensions/oracle/lib/runtime.ts +70 -16
- package/extensions/oracle/lib/tools.ts +313 -64
- package/extensions/oracle/worker/artifact-heuristics.d.mts +29 -0
- package/extensions/oracle/worker/auth-bootstrap.mjs +2 -72
- package/extensions/oracle/worker/auth-cookie-policy.d.mts +31 -0
- package/extensions/oracle/worker/run-job.mjs +330 -71
- package/extensions/oracle/worker/state-locks.d.mts +45 -0
- package/extensions/oracle/worker/state-locks.mjs +235 -0
- package/package.json +13 -4
- package/prompts/oracle.md +2 -0
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
# pi-oracle design
|
|
2
|
+
|
|
3
|
+
Status: isolated-profile concurrency architecture implemented in code; major live validation now passes, but a few non-blocking hardening items remain.
|
|
4
|
+
Date: 2026-04-03
|
|
5
|
+
|
|
6
|
+
Companion doc:
|
|
7
|
+
- `docs/ORACLE_RECOVERY_DRILL.md` — safe expired-auth recovery validation drill
|
|
8
|
+
|
|
9
|
+
Compatibility target:
|
|
10
|
+
- `pi` 0.65.0+
|
|
11
|
+
- current extension lifecycle only; no backward-compatibility shims for removed `session_switch` / `session_fork` events
|
|
12
|
+
|
|
13
|
+
## Goal
|
|
14
|
+
|
|
15
|
+
Create a `pi` extension that lets the user or agent consult ChatGPT.com through the web product instead of the API, with:
|
|
16
|
+
|
|
17
|
+
- manual invocation via `/oracle ...`
|
|
18
|
+
- automatic invocation by the agent in rare high-difficulty cases
|
|
19
|
+
- mandatory project-context archive upload (`.tar.zst`)
|
|
20
|
+
- long-running execution in the background
|
|
21
|
+
- durable response/artifact persistence plus best-effort wake-the-agent behavior when the oracle response is ready
|
|
22
|
+
- oracle requires a persisted pi session identity; in-memory/no-session contexts are rejected instead of risking cross-session wake-up misdelivery
|
|
23
|
+
- legacy project-scoped jobs from the older no-session model remain inspectable by project, but are treated as manual/status-only instead of being rebound to a different persisted session for wake-up delivery
|
|
24
|
+
- persisted responses and artifacts under `/tmp`
|
|
25
|
+
- optional same-thread follow-up questions later
|
|
26
|
+
|
|
27
|
+
## Architecture decision
|
|
28
|
+
|
|
29
|
+
The production architecture is now:
|
|
30
|
+
|
|
31
|
+
- use `agent-browser`
|
|
32
|
+
- do **not** automate the user’s real Chrome in production
|
|
33
|
+
- maintain one authenticated **seed profile** via `/oracle-auth`
|
|
34
|
+
- clone that seed into a **per-job runtime profile** for each oracle run
|
|
35
|
+
- launch each job in its own **runtime browser session**
|
|
36
|
+
- persist same-thread continuity by saved `chatUrl`, not by keeping tabs or browsers alive
|
|
37
|
+
- allow parallel jobs only when they do not target the same ChatGPT conversation
|
|
38
|
+
|
|
39
|
+
## Rejected production path
|
|
40
|
+
|
|
41
|
+
The old real-Chrome/CDP architecture is rejected for production.
|
|
42
|
+
|
|
43
|
+
Why:
|
|
44
|
+
|
|
45
|
+
- `agent-browser tab new <url>` opens a new tab and selects it
|
|
46
|
+
- `agent-browser tab <index>` switches the active tab
|
|
47
|
+
- upstream `agent-browser` source calls `Page.bringToFront` during tab switching
|
|
48
|
+
- this stole focus in the user’s real environment and disrupted typing
|
|
49
|
+
|
|
50
|
+
That violates a hard requirement.
|
|
51
|
+
|
|
52
|
+
Real-Chrome automation was useful for investigation and earlier smoke tests, but it is no longer the target architecture.
|
|
53
|
+
|
|
54
|
+
## Current extension surface
|
|
55
|
+
|
|
56
|
+
The extension now follows the current `pi` session lifecycle model:
|
|
57
|
+
|
|
58
|
+
- session transitions are handled from `session_start`
|
|
59
|
+
- previous runtimes are expected to clean up in `session_shutdown`
|
|
60
|
+
- no new logic depends on removed post-transition events
|
|
61
|
+
|
|
62
|
+
### Prompt template
|
|
63
|
+
|
|
64
|
+
- `/oracle <request>`
|
|
65
|
+
- implemented as a prompt template, not an extension command
|
|
66
|
+
- asks the agent to gather context and dispatch an oracle job
|
|
67
|
+
- intentionally uses native pi prompt/template queueing so submissions survive streaming and compaction
|
|
68
|
+
|
|
69
|
+
### Commands
|
|
70
|
+
|
|
71
|
+
- `/oracle-auth`
|
|
72
|
+
- syncs ChatGPT cookies from the user’s real Chrome into the isolated oracle profile and verifies them there
|
|
73
|
+
- `/oracle-status [job-id]`
|
|
74
|
+
- shows job status
|
|
75
|
+
- `/oracle-cancel [job-id]`
|
|
76
|
+
- cancels a queued or active job
|
|
77
|
+
- `/oracle-clean <job-id|all>`
|
|
78
|
+
- removes temp files for terminal jobs only
|
|
79
|
+
|
|
80
|
+
### Tools
|
|
81
|
+
|
|
82
|
+
- `oracle_submit`
|
|
83
|
+
- low-level agent-facing dispatch tool
|
|
84
|
+
- creates archive and launches a detached worker
|
|
85
|
+
- supports optional `followUpJobId` to continue the same ChatGPT thread by persisted URL
|
|
86
|
+
- `oracle_read`
|
|
87
|
+
- reads job status and outputs
|
|
88
|
+
- `oracle_cancel`
|
|
89
|
+
- cancels a queued or active job
|
|
90
|
+
|
|
91
|
+
## High-level flow
|
|
92
|
+
|
|
93
|
+
### `/oracle ...`
|
|
94
|
+
|
|
95
|
+
`/oracle <request>` should not directly drive ChatGPT.
|
|
96
|
+
It expands through the prompt-template path so pi can apply its native queueing semantics before the agent starts work.
|
|
97
|
+
|
|
98
|
+
Instead it instructs the agent to:
|
|
99
|
+
|
|
100
|
+
1. understand the task
|
|
101
|
+
2. gather repo context
|
|
102
|
+
3. choose exact archive inputs
|
|
103
|
+
4. craft the oracle prompt
|
|
104
|
+
5. call `oracle_submit`
|
|
105
|
+
6. stop and wait for the completion wake-up (best-effort; durable oracle response/artifact state is already persisted outside session history)
|
|
106
|
+
|
|
107
|
+
### `/oracle-auth`
|
|
108
|
+
|
|
109
|
+
Auth bootstrap flow:
|
|
110
|
+
|
|
111
|
+
1. load oracle config
|
|
112
|
+
2. acquire the global auth-maintenance lock
|
|
113
|
+
3. read ChatGPT cookies directly from the user’s real Chrome cookie store in read-only mode
|
|
114
|
+
- configurable source profile / cookie DB path
|
|
115
|
+
- no launch or mutation of the real Chrome profile
|
|
116
|
+
4. validate that `browser.authSeedProfileDir` is an absolute safe path and not inside the real Chrome user-data tree
|
|
117
|
+
5. create a staged seed-profile path next to the target seed profile
|
|
118
|
+
6. launch the isolated auth browser headed with:
|
|
119
|
+
- dedicated auth `--session`
|
|
120
|
+
- dedicated staged seed `--profile`
|
|
121
|
+
- configured executable path / user agent / launch args if set
|
|
122
|
+
7. clear isolated browser cookies and seed the staged profile with imported ChatGPT cookies
|
|
123
|
+
8. open ChatGPT in the isolated browser
|
|
124
|
+
9. verify auth with `/backend-api/me` plus ChatGPT UI readiness checks
|
|
125
|
+
10. on success, close the isolated browser so Chrome flushes profile state cleanly
|
|
126
|
+
11. atomically swap the staged profile into `browser.authSeedProfileDir`, keeping `*.prev` as rollback
|
|
127
|
+
12. write a seed-generation marker used by future runtime clones
|
|
128
|
+
13. if ChatGPT presents a challenge page, leave the staged auth browser/profile open for the user to solve and reuse
|
|
129
|
+
|
|
130
|
+
This keeps production oracle jobs off the user’s real Chrome while using the user’s existing authenticated ChatGPT cookies as the bootstrap source.
|
|
131
|
+
|
|
132
|
+
The authenticated seed profile remains the source of truth for future oracle runtimes.
|
|
133
|
+
|
|
134
|
+
### `oracle_submit`
|
|
135
|
+
|
|
136
|
+
1. validate config and model options
|
|
137
|
+
2. resolve optional `followUpJobId` into a prior `chatUrl` and `conversationId`
|
|
138
|
+
3. build the archive first into a temporary path
|
|
139
|
+
4. allocate a unique runtime:
|
|
140
|
+
- `runtimeId`
|
|
141
|
+
- `runtimeSessionName`
|
|
142
|
+
- `runtimeProfileDir`
|
|
143
|
+
5. under the global admission lock, first promote any older queued jobs that can now run
|
|
144
|
+
6. if runtime capacity is still available:
|
|
145
|
+
- acquire the runtime lease
|
|
146
|
+
- acquire the conversation lease for follow-up jobs
|
|
147
|
+
- create `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/...` job state as `submitted`
|
|
148
|
+
7. otherwise create `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/...` job state as `queued`
|
|
149
|
+
8. move the prepared archive into the job directory with a unique filename
|
|
150
|
+
9. spawn a detached worker only for submitted jobs
|
|
151
|
+
10. return immediately
|
|
152
|
+
11. stop the agent turn until the completion wake-up arrives (best-effort; durable oracle response/artifact state is already persisted outside session history)
|
|
153
|
+
|
|
154
|
+
### Worker run flow
|
|
155
|
+
|
|
156
|
+
Per job:
|
|
157
|
+
|
|
158
|
+
1. clone the authenticated seed profile into the job’s `runtimeProfileDir` under the auth lock
|
|
159
|
+
2. launch a fresh isolated browser with:
|
|
160
|
+
- the job’s `runtimeSessionName`
|
|
161
|
+
- the job’s `runtimeProfileDir`
|
|
162
|
+
- headless by default
|
|
163
|
+
3. open either:
|
|
164
|
+
- the saved `chatUrl` for follow-up jobs, or
|
|
165
|
+
- the configured default ChatGPT URL
|
|
166
|
+
4. classify page state before touching the UI
|
|
167
|
+
5. fail fast on:
|
|
168
|
+
- login required
|
|
169
|
+
- challenge/verification page
|
|
170
|
+
- transient outage after one retry
|
|
171
|
+
6. configure model family / effort
|
|
172
|
+
7. upload archive
|
|
173
|
+
8. wait for upload confirmation scoped to the active composer
|
|
174
|
+
9. fill prompt
|
|
175
|
+
10. send
|
|
176
|
+
11. wait for a stable conversation URL and persist `chatUrl` / `conversationId`
|
|
177
|
+
12. wait for completion anchored to the current turn only
|
|
178
|
+
13. persist plain-text response
|
|
179
|
+
14. download any response-local artifacts directly into the job artifact directory
|
|
180
|
+
15. close the isolated browser session and delete the runtime profile in `finally`
|
|
181
|
+
|
|
182
|
+
## Persistence model
|
|
183
|
+
|
|
184
|
+
### Default auth persistence
|
|
185
|
+
|
|
186
|
+
Default and recommended:
|
|
187
|
+
|
|
188
|
+
- auth seed via `--profile <authSeedProfileDir>` for durable ChatGPT authentication state
|
|
189
|
+
- per-job runtime via unique `--session <runtimeSessionName>` + unique `--profile <runtimeProfileDir>`
|
|
190
|
+
|
|
191
|
+
Not the default:
|
|
192
|
+
|
|
193
|
+
- `--session-name`
|
|
194
|
+
- `state save/load` as the primary auth bootstrap path
|
|
195
|
+
|
|
196
|
+
Reason:
|
|
197
|
+
|
|
198
|
+
`--profile` is the broadest persistence primitive and preserves full browser profile state such as cookies, localStorage, IndexedDB, service workers, cache, and login sessions. The safe concurrent design is therefore:
|
|
199
|
+
|
|
200
|
+
- one persistent authenticated seed profile
|
|
201
|
+
- many disposable runtime profile clones derived from that seed
|
|
202
|
+
|
|
203
|
+
## Config files
|
|
204
|
+
|
|
205
|
+
Merged config locations:
|
|
206
|
+
|
|
207
|
+
- global: `~/.pi/agent/extensions/oracle.json`
|
|
208
|
+
- project: `.pi/extensions/oracle.json`
|
|
209
|
+
|
|
210
|
+
Project config remains restricted to safe overrides only.
|
|
211
|
+
|
|
212
|
+
Browser/auth settings are global-only because they control local privileged browser state.
|
|
213
|
+
|
|
214
|
+
### Current config shape
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"defaults": {
|
|
219
|
+
"modelFamily": "pro",
|
|
220
|
+
"effort": "extended",
|
|
221
|
+
"autoSwitchToThinking": false
|
|
222
|
+
},
|
|
223
|
+
"browser": {
|
|
224
|
+
"sessionPrefix": "oracle",
|
|
225
|
+
"authSeedProfileDir": "<absolute path to oracle auth seed profile>",
|
|
226
|
+
"runtimeProfilesDir": "<absolute path to oracle runtime profiles dir>",
|
|
227
|
+
"maxConcurrentJobs": 2,
|
|
228
|
+
"cloneStrategy": "apfs-clone",
|
|
229
|
+
"chatUrl": "https://chatgpt.com/",
|
|
230
|
+
"authUrl": "https://chatgpt.com/auth/login",
|
|
231
|
+
"runMode": "headless",
|
|
232
|
+
"executablePath": "<optional absolute path to Chrome executable>",
|
|
233
|
+
"userAgent": "<optional real-Chrome UA override>",
|
|
234
|
+
"args": ["--disable-blink-features=AutomationControlled"]
|
|
235
|
+
},
|
|
236
|
+
"auth": {
|
|
237
|
+
"pollMs": 1000,
|
|
238
|
+
"bootstrapTimeoutMs": 600000,
|
|
239
|
+
"chromeProfile": "<optional Chrome profile name>",
|
|
240
|
+
"chromeCookiePath": "<optional absolute path to Chrome Cookies DB>"
|
|
241
|
+
},
|
|
242
|
+
"worker": {
|
|
243
|
+
"pollMs": 5000,
|
|
244
|
+
"completionTimeoutMs": 5400000
|
|
245
|
+
},
|
|
246
|
+
"poller": {
|
|
247
|
+
"intervalMs": 5000
|
|
248
|
+
},
|
|
249
|
+
"artifacts": {
|
|
250
|
+
"capture": true
|
|
251
|
+
},
|
|
252
|
+
"cleanup": {
|
|
253
|
+
"completeJobRetentionMs": 1209600000,
|
|
254
|
+
"failedJobRetentionMs": 2592000000
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Cleanup maintenance model
|
|
260
|
+
|
|
261
|
+
Long-run hygiene is intentionally conservative:
|
|
262
|
+
|
|
263
|
+
- runtime profiles, runtime leases, and conversation leases are cleaned immediately as part of worker/command cleanup paths
|
|
264
|
+
- browser close is time-bounded so cleanup can continue even if `agent-browser close` wedges
|
|
265
|
+
- `/oracle-clean` performs runtime cleanup before removing the persisted job directory, but refuses terminal jobs whose worker is still live or whose wake-up was just sent inside a short post-send retention grace window
|
|
266
|
+
- stale lock directories are swept before reconcile maintenance
|
|
267
|
+
- old auth `.staging-*` profiles are swept during `/oracle-auth` startup when the auth browser session is not still active
|
|
268
|
+
- terminal job directories are retained for inspection, then pruned later based on configurable retention windows
|
|
269
|
+
|
|
270
|
+
Current retention policy is configurable via `cleanup.*`:
|
|
271
|
+
|
|
272
|
+
- `cleanup.completeJobRetentionMs`
|
|
273
|
+
- applies to `complete` and `cancelled` jobs based on terminal-job age; wake-up delivery remains best-effort only, with a short post-send grace so saved response/artifact paths survive the follow-up turn
|
|
274
|
+
- `cleanup.failedJobRetentionMs`
|
|
275
|
+
- applies to `failed` jobs
|
|
276
|
+
|
|
277
|
+
Cleanup warnings are treated as diagnostics, not silent no-ops:
|
|
278
|
+
|
|
279
|
+
- worker cleanup warnings are appended to `logs/worker.log`
|
|
280
|
+
- command-side cleanup warnings are surfaced to the user
|
|
281
|
+
- cancellation/stale-job recovery persists cleanup warnings into `job.json`
|
|
282
|
+
- terminal cleanup recovery will terminate stale live cleanup workers before retrying teardown so blocked capacity does not wedge indefinitely
|
|
283
|
+
|
|
284
|
+
## Job layout under the configured jobs dir
|
|
285
|
+
|
|
286
|
+
Default location: `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/`
|
|
287
|
+
|
|
288
|
+
```text
|
|
289
|
+
${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/
|
|
290
|
+
job.json
|
|
291
|
+
prompt.md
|
|
292
|
+
context-<job-id>.tar.zst
|
|
293
|
+
response.md
|
|
294
|
+
artifacts.json
|
|
295
|
+
artifacts/
|
|
296
|
+
...downloaded files...
|
|
297
|
+
logs/
|
|
298
|
+
worker.log
|
|
299
|
+
...diagnostic captures on failure...
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### `job.json` fields
|
|
303
|
+
|
|
304
|
+
Important fields include:
|
|
305
|
+
|
|
306
|
+
- `id`
|
|
307
|
+
- `status`: `queued | preparing | submitted | waiting | complete | failed | cancelled`
|
|
308
|
+
- `phase`: `queued | submitted | cloning_runtime | launching_browser | verifying_auth | configuring_model | uploading_archive | awaiting_response | extracting_response | downloading_artifacts | complete | complete_with_artifact_errors | failed | cancelled`
|
|
309
|
+
- `phaseAt`
|
|
310
|
+
- `createdAt`
|
|
311
|
+
- `queuedAt`
|
|
312
|
+
- `submittedAt`
|
|
313
|
+
- `completedAt`
|
|
314
|
+
- `heartbeatAt`
|
|
315
|
+
- `cwd`
|
|
316
|
+
- `projectId`
|
|
317
|
+
- `sessionId`
|
|
318
|
+
- `originSessionFile`
|
|
319
|
+
- `requestSource`
|
|
320
|
+
- `chatModelFamily`
|
|
321
|
+
- `effort`
|
|
322
|
+
- `autoSwitchToThinking`
|
|
323
|
+
- `followUpToJobId`
|
|
324
|
+
- `chatUrl`
|
|
325
|
+
- `conversationId`
|
|
326
|
+
- `responsePath`
|
|
327
|
+
- `responseFormat` (`text/plain`)
|
|
328
|
+
- `artifactPaths`
|
|
329
|
+
- `artifactsManifestPath`
|
|
330
|
+
- `archivePath`
|
|
331
|
+
- `archiveSha256`
|
|
332
|
+
- `archiveDeletedAfterUpload`
|
|
333
|
+
- `notifiedAt`
|
|
334
|
+
- `notificationEntryId`
|
|
335
|
+
- `notificationSessionKey`
|
|
336
|
+
- `wakeupAttemptCount`
|
|
337
|
+
- `wakeupLastRequestedAt`
|
|
338
|
+
- `wakeupSettledAt`
|
|
339
|
+
- `wakeupSettledSource`
|
|
340
|
+
- `wakeupSettledSessionFile`
|
|
341
|
+
- `wakeupSettledSessionKey`
|
|
342
|
+
- `wakeupSettledBeforeFirstAttempt`
|
|
343
|
+
- `wakeupObservedAt`
|
|
344
|
+
- `wakeupObservedSource`
|
|
345
|
+
- `wakeupObservedSessionFile`
|
|
346
|
+
- `wakeupObservedSessionKey`
|
|
347
|
+
- `notifyClaimedAt`
|
|
348
|
+
- `notifyClaimedBy`
|
|
349
|
+
- `artifactFailureCount`
|
|
350
|
+
- `error`
|
|
351
|
+
- `cleanupWarnings`
|
|
352
|
+
- `lastCleanupAt`
|
|
353
|
+
- `workerPid`
|
|
354
|
+
- `workerNonce`
|
|
355
|
+
- `workerStartedAt`
|
|
356
|
+
- `runtimeId`
|
|
357
|
+
- `runtimeSessionName`
|
|
358
|
+
- `runtimeProfileDir`
|
|
359
|
+
- `seedGeneration`
|
|
360
|
+
- `config`
|
|
361
|
+
|
|
362
|
+
## Response format
|
|
363
|
+
|
|
364
|
+
Canonical oracle response format remains:
|
|
365
|
+
|
|
366
|
+
- `text/plain`
|
|
367
|
+
|
|
368
|
+
The saved file path is currently `response.md` for continuity with earlier job layouts, but the content contract is normalized plain text for agent consumption.
|
|
369
|
+
|
|
370
|
+
## ChatGPT page-state classifier
|
|
371
|
+
|
|
372
|
+
Before upload/send, the worker classifies ChatGPT as one of:
|
|
373
|
+
|
|
374
|
+
- `authenticated_and_ready`
|
|
375
|
+
- `login_required`
|
|
376
|
+
- `challenge_blocking`
|
|
377
|
+
- `transient_outage_error`
|
|
378
|
+
- `unknown`
|
|
379
|
+
|
|
380
|
+
Signals used:
|
|
381
|
+
|
|
382
|
+
- current URL
|
|
383
|
+
- accessibility snapshot
|
|
384
|
+
- body text
|
|
385
|
+
|
|
386
|
+
### Ready
|
|
387
|
+
|
|
388
|
+
Require all of:
|
|
389
|
+
|
|
390
|
+
- ChatGPT origin is correct
|
|
391
|
+
- not on `/auth/*`
|
|
392
|
+
- composer exists
|
|
393
|
+
- `Add files and more` exists
|
|
394
|
+
- model selector / selected model control exists
|
|
395
|
+
- no login/challenge/outage signals
|
|
396
|
+
|
|
397
|
+
### Login required
|
|
398
|
+
|
|
399
|
+
Any of:
|
|
400
|
+
|
|
401
|
+
- URL on `/auth/*`
|
|
402
|
+
- login/provider signals like `Log in`, `Sign up`, `Continue with Google`, etc.
|
|
403
|
+
- logged-out page shape where a composer may exist but required oracle controls do not
|
|
404
|
+
- redirect away from the expected ChatGPT origin
|
|
405
|
+
|
|
406
|
+
### Challenge blocking
|
|
407
|
+
|
|
408
|
+
Examples:
|
|
409
|
+
|
|
410
|
+
- `Just a moment`
|
|
411
|
+
- `Verify you are human`
|
|
412
|
+
- `Cloudflare`
|
|
413
|
+
- captcha / turnstile markers
|
|
414
|
+
- suspicious or unusual activity messages
|
|
415
|
+
|
|
416
|
+
### Transient outage
|
|
417
|
+
|
|
418
|
+
Examples:
|
|
419
|
+
|
|
420
|
+
- `Something went wrong`
|
|
421
|
+
- `A network error occurred`
|
|
422
|
+
- websocket error text
|
|
423
|
+
- `Try again later`
|
|
424
|
+
|
|
425
|
+
## Artifact strategy
|
|
426
|
+
|
|
427
|
+
The artifact path is now direct and browser-local.
|
|
428
|
+
|
|
429
|
+
Use response-local candidate detection exactly as before, but replace browser-download-manager scraping with direct `agent-browser` downloads:
|
|
430
|
+
|
|
431
|
+
- find artifact candidates only in the current assistant response region
|
|
432
|
+
- for each candidate ref:
|
|
433
|
+
- call `agent-browser download <ref> <dest>`
|
|
434
|
+
- write directly into `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/artifacts`
|
|
435
|
+
- compute size / sha256 / detected type
|
|
436
|
+
- append manifest entry
|
|
437
|
+
|
|
438
|
+
This deliberately avoids:
|
|
439
|
+
|
|
440
|
+
- `chrome://downloads`
|
|
441
|
+
- downloads-tab ownership logic
|
|
442
|
+
- browser-global download history heuristics
|
|
443
|
+
- focus-sensitive tab hacks
|
|
444
|
+
|
|
445
|
+
Visible labels are still not trusted as authoritative filenames. They are treated primarily as display metadata.
|
|
446
|
+
|
|
447
|
+
## Same-thread follow-ups
|
|
448
|
+
|
|
449
|
+
Same-thread continuity is persisted as data, not runtime browser state.
|
|
450
|
+
|
|
451
|
+
Approach:
|
|
452
|
+
|
|
453
|
+
- store `chatUrl` only after the conversation URL stabilizes
|
|
454
|
+
- derive and persist `conversationId` from that URL when possible
|
|
455
|
+
- for a follow-up job, resolve `followUpJobId` to the prior `chatUrl`
|
|
456
|
+
- acquire a conversation lease before launching the follow-up
|
|
457
|
+
- launch a fresh isolated browser using a fresh runtime clone of the auth seed
|
|
458
|
+
- open that URL
|
|
459
|
+
- continue there if authentication and page-state checks pass
|
|
460
|
+
|
|
461
|
+
Do not keep a browser or tab alive between jobs just to preserve thread continuity.
|
|
462
|
+
Do not allow concurrent jobs to target the same `conversationId`.
|
|
463
|
+
|
|
464
|
+
## Poller / wake-up model
|
|
465
|
+
|
|
466
|
+
The extension still uses the same general `pi`-native background completion pattern, but notification semantics are now explicit:
|
|
467
|
+
|
|
468
|
+
- detached worker writes `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-*` state
|
|
469
|
+
- poller scans jobs on an interval
|
|
470
|
+
- completed job durability lives in oracle job state plus saved response/artifact files, not in synthetic session-history assistant messages
|
|
471
|
+
- when a matching job reaches `complete`, `failed`, or `cancelled`, the poller issues bounded best-effort wake-up reminders to whichever matching session is currently live
|
|
472
|
+
- those wake-ups direct the receiver to `oracle_read(jobId)` as the canonical completion-consumption path, while still surfacing saved response/artifact paths as secondary context
|
|
473
|
+
- manual `oracle_read` or `/oracle-status` inspection settles further reminder retries once the terminal job has been opened and persists provenance about which path/session settled the wake-up
|
|
474
|
+
- manual inspection before the first wake-up attempt is recorded separately as observation metadata and does not suppress the first reminder send
|
|
475
|
+
- if no wake-up lands, the job remains available via `/oracle-status`, `oracle_read`, and the saved `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/` response/artifact files
|
|
476
|
+
- because completion delivery is best-effort, pruning uses explicit terminal-job age policy instead of pretending a durable session notification happened
|
|
477
|
+
- recently sent wake-ups keep response/artifact files retained briefly so follow-up turns do not point at deleted paths if cleanup or pruning races with delivery
|
|
478
|
+
|
|
479
|
+
## What was removed by this pivot
|
|
480
|
+
|
|
481
|
+
The isolated-profile design deletes or supersedes the old real-Chrome-specific machinery:
|
|
482
|
+
|
|
483
|
+
- CDP attach/verification to port `9222`
|
|
484
|
+
- `cdpVerified` / `cdpUrl` job state
|
|
485
|
+
- dedicated oracle tab parking/reuse in the user’s browser
|
|
486
|
+
- wrong-tab drift handling
|
|
487
|
+
- selected-tab / tab-index tracking
|
|
488
|
+
- temporary `chrome://downloads` tabs
|
|
489
|
+
- browser download-manager scraping via `downloads-manager.items_`
|
|
490
|
+
- copy-from-`~/Downloads` artifact recovery flow
|
|
491
|
+
|
|
492
|
+
## Current implementation status
|
|
493
|
+
|
|
494
|
+
Implemented in code for the pivot and concurrency redesign:
|
|
495
|
+
|
|
496
|
+
- config now uses `browser.*` + `auth.*`
|
|
497
|
+
- `/oracle-auth` now syncs real-Chrome ChatGPT cookies into the authenticated seed profile instead of opening a manual-login browser
|
|
498
|
+
- `oracle_submit` supports follow-ups via persisted `chatUrl`
|
|
499
|
+
- job state no longer stores CDP verification fields
|
|
500
|
+
- workers now run with per-job runtime sessions and per-job runtime profile clones
|
|
501
|
+
- runtime admission is controlled by runtime leases and `browser.maxConcurrentJobs`
|
|
502
|
+
- queued jobs are workerless and do not consume runtime or conversation leases until promotion
|
|
503
|
+
- follow-up jobs now acquire conversation leases
|
|
504
|
+
- persisted job state now records explicit lifecycle phases instead of relying only on coarse statuses
|
|
505
|
+
- poller notifications now use per-job notification claims rather than broad global scan serialization
|
|
506
|
+
- worker now uses a structured ChatGPT page-state classifier
|
|
507
|
+
- worker now downloads artifacts directly with `agent-browser download <ref> <dest>`
|
|
508
|
+
- poller scans are now best-effort/non-fatal with per-session in-flight guards
|
|
509
|
+
- worker heartbeats during artifact downloads, writes artifact manifests incrementally, and reopens the saved conversation before artifact capture/download
|
|
510
|
+
- artifact-only responses are treated as valid completion content
|
|
511
|
+
- the repo now includes a repeatable sanity harness: `npm run sanity:oracle`
|
|
512
|
+
- the repo now includes a safe expired-auth recovery drill: `docs/ORACLE_RECOVERY_DRILL.md`
|
|
513
|
+
- worker closes the isolated browser, removes the runtime profile, and releases leases in `finally`
|
|
514
|
+
|
|
515
|
+
Retained from the earlier MVP:
|
|
516
|
+
|
|
517
|
+
- `/oracle`, `/oracle-status`, `/oracle-cancel`, `/oracle-clean`
|
|
518
|
+
- `oracle_submit`, `oracle_read`, `oracle_cancel`
|
|
519
|
+
- detached background worker model
|
|
520
|
+
- `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/...` state layout
|
|
521
|
+
- shell-safe archive creation using `tar` piped to `zstd`
|
|
522
|
+
- private permissions and atomic writes
|
|
523
|
+
- stale-worker reconciliation
|
|
524
|
+
- upload ordering: attach → confirm → fill → send
|
|
525
|
+
- current-turn response anchoring
|
|
526
|
+
- plain-text canonical response extraction
|
|
527
|
+
- wake-the-agent poller integration
|
|
528
|
+
- unique archive filenames per job
|
|
529
|
+
- worker PID identity checks using recorded process start time
|
|
530
|
+
- composer-scoped upload confirmation
|
|
531
|
+
- stable `chatUrl` capture after send
|
|
532
|
+
- redacted `oracle_read` details and same-project job scoping
|
|
533
|
+
- serialized poller scans
|
|
534
|
+
|
|
535
|
+
## Live validation status
|
|
536
|
+
|
|
537
|
+
Live-validated after the concurrency redesign:
|
|
538
|
+
|
|
539
|
+
- `/oracle-auth` happy path still works against the seed profile
|
|
540
|
+
- headless normal oracle runs still work using per-job runtime clones
|
|
541
|
+
- two concurrent runs in different projects work with isolated runtimes
|
|
542
|
+
- two concurrent runs in the same project but different `pi` sessions work when they target different conversations
|
|
543
|
+
- same-conversation concurrent follow-up rejection works and fails fast with a clear lease error
|
|
544
|
+
- runtime profile cleanup works on completion and cancellation
|
|
545
|
+
- runtime/conversation lease cleanup works on completion and cancellation
|
|
546
|
+
- global browser args overrides (for example `--disable-gpu`) apply to real jobs
|
|
547
|
+
- artifact-producing runs work with direct `download <ref> <dest>`
|
|
548
|
+
- multi-artifact runs complete, target the correct `pi` session, and persist both downloaded files with correct contents
|
|
549
|
+
- the poller no longer needs the worker to stay alive just to observe completion for artifact-producing runs
|
|
550
|
+
- expired/missing auth now fails as a clean auth-related error instead of generic UI/config drift
|
|
551
|
+
- `/oracle-auth` repairs the seed profile and a post-repair probe succeeds again
|
|
552
|
+
- live auth recovery also exposed and corrected a real source-profile misconfiguration during validation; the configured Chrome profile must actually contain the active ChatGPT session cookies
|
|
553
|
+
|
|
554
|
+
## Known remaining work
|
|
555
|
+
|
|
556
|
+
Still to verify live after this pivot:
|
|
557
|
+
|
|
558
|
+
- model-selection verification against the current ChatGPT UI under additional real-world variation
|
|
559
|
+
- optional richer terminal semantics for partial artifact failure (`complete_with_artifact_errors`) in more live scenarios
|
|
560
|
+
|
|
561
|
+
## Production readiness criteria
|
|
562
|
+
|
|
563
|
+
This architecture is now live-validated for the core release path:
|
|
564
|
+
|
|
565
|
+
- no interaction with the user’s real Chrome during normal jobs
|
|
566
|
+
- no focus disruption during normal jobs
|
|
567
|
+
- the seed profile survives browser restarts and can be cloned into runtime profiles repeatedly
|
|
568
|
+
- different projects / sessions can run in parallel without co-mingled data
|
|
569
|
+
- same-conversation follow-ups are rejected while another job owns that conversation lease
|
|
570
|
+
- artifact capture works without `chrome://downloads`
|
|
571
|
+
- artifact-only responses and multi-artifact responses both complete correctly
|
|
572
|
+
- same-thread follow-ups reopen correctly from persisted `chatUrl`
|
|
573
|
+
- failure modes are clearly classified as auth / challenge / outage / UI drift
|
|
574
|
+
- expired/missing auth now fails cleanly, `/oracle-auth` repairs the seed profile, and the post-repair probe succeeds again
|
|
575
|
+
|
|
576
|
+
### Current readiness summary
|
|
577
|
+
|
|
578
|
+
Current release blockers for the validated scope:
|
|
579
|
+
- none currently known
|
|
580
|
+
|
|
581
|
+
Remaining non-blocking hardening work:
|
|
582
|
+
- broaden live proof of the new lifecycle/state-machine model across more degraded paths
|
|
583
|
+
- broaden live proof of notification-claim semantics under more concurrent completions
|
|
584
|
+
- extend regression-harness coverage for browser/download failure classes
|
|
585
|
+
- polish partial-artifact terminal semantics (`complete_with_artifact_errors`)
|
|
586
|
+
- keep hardening model-selection verification against future ChatGPT UI variation
|
|
587
|
+
|
|
588
|
+
Recent proof points:
|
|
589
|
+
- expired-auth drill fail path: `a2460bc1-7d89-4041-b67d-39680d310325`
|
|
590
|
+
- `/oracle-auth` repair evidence: `/tmp/oracle-auth.log`
|
|
591
|
+
- expired-auth drill post-repair success: `fa26a2a7-0057-4a21-b3e0-71c1d020facf`
|
|
592
|
+
- successful multi-artifact completion: `b6b3599c-6b91-4315-adfa-8a83aa5eda9b`
|
|
593
|
+
- repo-owned sanity harness: `npm run sanity:oracle`
|