pi-oracle 0.3.4 → 0.5.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.
- package/CHANGELOG.md +38 -0
- package/README.md +27 -8
- package/docs/ORACLE_DESIGN.md +14 -8
- package/docs/ORACLE_ISOLATED_PI_VALIDATION.md +276 -0
- package/extensions/oracle/index.ts +8 -1
- package/extensions/oracle/lib/commands.ts +25 -29
- package/extensions/oracle/lib/config.ts +56 -2
- package/extensions/oracle/lib/jobs.ts +134 -219
- package/extensions/oracle/lib/locks.ts +41 -209
- package/extensions/oracle/lib/poller.ts +38 -52
- package/extensions/oracle/lib/queue.ts +75 -112
- package/extensions/oracle/lib/runtime.ts +102 -19
- package/extensions/oracle/lib/tools.ts +663 -294
- package/extensions/oracle/shared/job-coordination-helpers.d.mts +84 -0
- package/extensions/oracle/shared/job-coordination-helpers.mjs +168 -0
- package/extensions/oracle/shared/job-lifecycle-helpers.d.mts +131 -0
- package/extensions/oracle/shared/job-lifecycle-helpers.mjs +390 -0
- package/extensions/oracle/shared/job-observability-helpers.d.mts +60 -0
- package/extensions/oracle/shared/job-observability-helpers.mjs +161 -0
- package/extensions/oracle/shared/process-helpers.d.mts +20 -0
- package/extensions/oracle/shared/process-helpers.mjs +128 -0
- package/extensions/oracle/shared/state-coordination-helpers.d.mts +43 -0
- package/extensions/oracle/shared/state-coordination-helpers.mjs +381 -0
- package/extensions/oracle/worker/artifact-heuristics.mjs +5 -0
- package/extensions/oracle/worker/auth-bootstrap.mjs +125 -134
- package/extensions/oracle/worker/auth-cookie-policy.mjs +5 -0
- package/extensions/oracle/worker/auth-flow-helpers.d.mts +41 -0
- package/extensions/oracle/worker/auth-flow-helpers.mjs +165 -0
- package/extensions/oracle/worker/chatgpt-flow-helpers.d.mts +13 -0
- package/extensions/oracle/worker/chatgpt-flow-helpers.mjs +85 -0
- package/extensions/oracle/worker/chatgpt-ui-helpers.mjs +93 -9
- package/extensions/oracle/worker/run-job.mjs +166 -274
- package/extensions/oracle/worker/state-locks.mjs +31 -216
- package/package.json +4 -3
- package/prompts/oracle.md +16 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 - 2026-04-12
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `oracle_preflight`, a lightweight readiness tool that lets `/oracle` fail fast on missing persisted-session or local auth/config blockers before archive/context work begins
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- `/oracle` now follows a stricter preflight-first flow, biases toward minimal context gathering for explicitly narrow requests, and prefers the configured default model unless a different preset is clearly needed
|
|
10
|
+
- `oracle_read`, `/oracle-status`, and wake-up messaging now keep the true terminal event prominent, separate wake-up bookkeeping from failure state, and stop implying that a missing `response.md` is ready
|
|
11
|
+
- `/oracle-auth` failure guidance now reports the effective agent config path for the active agent dir and explains when a project config was also read but could not override `auth.*`
|
|
12
|
+
- `/oracle-clean` now documents the short post-send retention grace window and returns a retry-after timestamp when a terminal job is still intentionally retained
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- `oracle_submit` now rejects locally knowable auth-seed blockers before archive creation or job persistence while still preserving direct archive-input validation errors like symlink escapes
|
|
16
|
+
- oracle tool results now use consistent structured `details.job` / `details.error` payloads and preserve `isError` for structured failures through the tool-result hook
|
|
17
|
+
- `/oracle` no-session and missing-seed flows now stop before unnecessary repo exploration, and narrow prompt-template runs dispatch more quickly with smaller archives when the user scope is explicit
|
|
18
|
+
- repeated oracle sanity runs now quiesce background pollers before isolated-state teardown so release verification no longer emits a noisy temp-lock ENOENT race
|
|
19
|
+
|
|
20
|
+
## 0.4.0 - 2026-04-12
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- repeatable isolated local-extension `pi` validation guidance for oracle release verification, including smoke workflows that load the in-repo extension source directly
|
|
24
|
+
- persisted oracle lifecycle-event breadcrumbs plus richer detached-job observability in `oracle_submit`, `oracle_read`, poller wake-ups, and `/oracle-status`, including worker-log paths and last-event context
|
|
25
|
+
- shared worker/auth validation helpers, shared concurrency primitives, shared lifecycle reducers, and shared observability formatters to keep extension and worker behavior aligned
|
|
26
|
+
- extracted sanity-harness support and poller suites with typed helper scaffolding and repeated-run stability coverage
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- oracle whole-repo archiving now excludes local tool state like `.pi/`, `.oracle-context/`, `.cursor/`, and `.scratchpad.md` by default while preserving explicitly requested paths
|
|
30
|
+
- lock/lease recovery, queue promotion, process identity handling, and lifecycle transitions now flow through shared helper modules instead of duplicated inline implementations
|
|
31
|
+
- worker/auth verification now leans on behaviorally tested helper modules plus dedicated `typecheck:worker-helpers` coverage instead of syntax checks and brittle source-string assertions alone
|
|
32
|
+
- release validation now expects isolated local-extension `pi` smoke tests and a stronger local oracle verification gate before shipping
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- archive input resolution now rejects symlink escapes outside the repo root and preserves safer repo-boundary handling for targeted archives
|
|
36
|
+
- hung `tar`, `zstd`, `cp`, and auth `agent-browser` subprocesses now time out and fail clearly instead of wedging archive, runtime-clone, or auth flows indefinitely
|
|
37
|
+
- cleanup warnings without a live worker no longer consume runtime/conversation capacity forever, while teardown still attempts lease release and preserves warnings for later triage
|
|
38
|
+
- detached oracle workers and poller flows now report clearer lifecycle breadcrumbs, wake-up settlement state, and failure context during fast-fail auth/bootstrap scenarios
|
|
39
|
+
- sanity-runner cleanup now retries transient temp-directory removal races, and the extracted harness is less timing-fragile and less `any`-driven than the previous monolithic runner
|
|
40
|
+
|
|
3
41
|
## 0.3.4 - 2026-04-11
|
|
4
42
|
|
|
5
43
|
### Changed
|
package/README.md
CHANGED
|
@@ -42,12 +42,17 @@ pi install https://github.com/fitchmultz/pi-oracle
|
|
|
42
42
|
|
|
43
43
|
## Quickstart
|
|
44
44
|
|
|
45
|
-
1.
|
|
46
|
-
2. Make sure
|
|
47
|
-
3.
|
|
48
|
-
4.
|
|
49
|
-
5. Run `/oracle
|
|
50
|
-
6.
|
|
45
|
+
1. Start a normal persisted `pi` session. Do not use `pi --no-session` for oracle.
|
|
46
|
+
2. Make sure ChatGPT already works in your local Chrome 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 a best-effort wake-up, or check `/oracle-status`.
|
|
52
|
+
|
|
53
|
+
The `/oracle` prompt now runs an early oracle preflight before it gathers repo context, so missing persisted-session or local auth/config blockers fail before the agent spends time reading files.
|
|
54
|
+
|
|
55
|
+
For explicitly narrow requests, `/oracle` should gather only minimal context and prefer a minimal archive instead of broad repo exploration. It should also omit `preset` and use the configured default model unless the task clearly needs a different one.
|
|
51
56
|
|
|
52
57
|
If you miss the wake-up, the result is still saved durably in the oracle job directory and can be read later.
|
|
53
58
|
|
|
@@ -61,11 +66,15 @@ If you miss the wake-up, the result is still saved durably in the oracle job dir
|
|
|
61
66
|
/oracle Read the codebase and explain the highest-risk auth/session failure modes, including what to test before shipping.
|
|
62
67
|
```
|
|
63
68
|
|
|
69
|
+
```text
|
|
70
|
+
/oracle Explain the README guidance for /oracle-clean retention grace. Only archive README.md unless another file is clearly necessary.
|
|
71
|
+
```
|
|
72
|
+
|
|
64
73
|
## High-level flow
|
|
65
74
|
|
|
66
75
|
```mermaid
|
|
67
76
|
flowchart LR
|
|
68
|
-
A["/oracle request"] --> B["Agent gathers repo context"]
|
|
77
|
+
A["/oracle request"] --> B["Agent preflights, then gathers only the needed repo context"]
|
|
69
78
|
B --> C["oracle_submit builds archive"]
|
|
70
79
|
C --> D["Detached worker starts isolated ChatGPT runtime"]
|
|
71
80
|
D --> E["Archive + prompt sent to ChatGPT.com"]
|
|
@@ -82,9 +91,10 @@ User-facing commands:
|
|
|
82
91
|
- `/oracle-auth` — sync ChatGPT cookies from your real Chrome profile into the isolated oracle auth profile
|
|
83
92
|
- `/oracle-status [job-id]` — inspect job status
|
|
84
93
|
- `/oracle-cancel [job-id]` — cancel queued or active job
|
|
85
|
-
- `/oracle-clean <job-id|all>` — remove temp files for terminal jobs
|
|
94
|
+
- `/oracle-clean <job-id|all>` — remove temp files for terminal jobs; recently woken terminal jobs may stay retained briefly and return a retry-after hint
|
|
86
95
|
|
|
87
96
|
Agent-facing tools:
|
|
97
|
+
- `oracle_preflight`
|
|
88
98
|
- `oracle_submit`
|
|
89
99
|
- `oracle_read`
|
|
90
100
|
- `oracle_cancel`
|
|
@@ -143,6 +153,7 @@ Project config should only override safe, non-privileged settings.
|
|
|
143
153
|
- Jobs can queue automatically if runtime capacity is full.
|
|
144
154
|
- Completion delivery into `pi` is best-effort wake-up based.
|
|
145
155
|
- If you miss the wake-up, use `oracle_read(jobId)` or `/oracle-status`.
|
|
156
|
+
- `/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.
|
|
146
157
|
|
|
147
158
|
## Requirements
|
|
148
159
|
|
|
@@ -177,6 +188,12 @@ Project config should only override safe, non-privileged settings.
|
|
|
177
188
|
- Use `/oracle-status [job-id]` or `oracle_read(jobId)`.
|
|
178
189
|
- Results are still saved on disk even if the reminder turn does not land.
|
|
179
190
|
|
|
191
|
+
### `/oracle-clean` refuses a terminal job right after completion
|
|
192
|
+
|
|
193
|
+
- This can happen during the short post-send retention grace window after a wake-up was sent.
|
|
194
|
+
- The command now returns a `Retry after ...` timestamp when that guard is active.
|
|
195
|
+
- Wait until that time, then rerun `/oracle-clean [job-id|all]`.
|
|
196
|
+
|
|
180
197
|
### `agent-browser`, `tar`, or `zstd` is missing
|
|
181
198
|
|
|
182
199
|
- Install the missing local dependency and rerun the command.
|
|
@@ -195,6 +212,7 @@ Project config should only override safe, non-privileged settings.
|
|
|
195
212
|
|
|
196
213
|
- `docs/ORACLE_DESIGN.md` — architecture, lifecycle, queueing, persistence, presets, and recovery behavior
|
|
197
214
|
- `docs/ORACLE_RECOVERY_DRILL.md` — safe expired-auth recovery validation drill
|
|
215
|
+
- `docs/ORACLE_ISOLATED_PI_VALIDATION.md` — repeatable isolated `pi` session smoke test for local-extension validation
|
|
198
216
|
|
|
199
217
|
## Privacy / local data
|
|
200
218
|
|
|
@@ -210,6 +228,7 @@ Review the code and design docs before using it with sensitive material.
|
|
|
210
228
|
```bash
|
|
211
229
|
npm run check:oracle-extension
|
|
212
230
|
npm run typecheck
|
|
231
|
+
npm run typecheck:worker-helpers
|
|
213
232
|
npm run sanity:oracle
|
|
214
233
|
npm run pack:check
|
|
215
234
|
# conventional local gate
|
package/docs/ORACLE_DESIGN.md
CHANGED
|
@@ -79,6 +79,9 @@ The extension now follows the current `pi` session lifecycle model:
|
|
|
79
79
|
|
|
80
80
|
### Tools
|
|
81
81
|
|
|
82
|
+
- `oracle_preflight`
|
|
83
|
+
- lightweight agent-facing readiness check for persisted-session and local oracle prerequisites
|
|
84
|
+
- intended to run before expensive `/oracle` context gathering
|
|
82
85
|
- `oracle_submit`
|
|
83
86
|
- low-level agent-facing dispatch tool
|
|
84
87
|
- creates archive and launches a detached worker
|
|
@@ -97,12 +100,15 @@ It expands through the prompt-template path so pi can apply its native queueing
|
|
|
97
100
|
|
|
98
101
|
Instead it instructs the agent to:
|
|
99
102
|
|
|
100
|
-
1.
|
|
101
|
-
2.
|
|
102
|
-
3.
|
|
103
|
-
4.
|
|
104
|
-
5.
|
|
105
|
-
6.
|
|
103
|
+
1. call `oracle_preflight` immediately
|
|
104
|
+
2. stop right away if preflight reports the session or local oracle setup is not ready
|
|
105
|
+
3. understand whether the request is explicitly narrow or genuinely broad
|
|
106
|
+
4. gather only the smallest repo context needed to submit well
|
|
107
|
+
5. if the request is narrow, prefer a minimal targeted archive and dispatch as soon as enough context is in hand
|
|
108
|
+
6. if the request is broad/repo-wide, gather broader context and usually archive `.`
|
|
109
|
+
7. craft the oracle prompt
|
|
110
|
+
8. call `oracle_submit`
|
|
111
|
+
9. stop and wait for the completion wake-up (best-effort; durable oracle response/artifact state is already persisted outside session history)
|
|
106
112
|
|
|
107
113
|
### `/oracle-auth`
|
|
108
114
|
|
|
@@ -133,7 +139,7 @@ The authenticated seed profile remains the source of truth for future oracle run
|
|
|
133
139
|
|
|
134
140
|
### `oracle_submit`
|
|
135
141
|
|
|
136
|
-
Agent-facing submissions use **`preset`**; the canonical registry is `ORACLE_SUBMIT_PRESETS` in `extensions/oracle/lib/config.ts`. **`preset` is the only model-selection parameter** on `oracle_submit`. There are no `modelFamily`, `effort`, or `autoSwitchToThinking` fields. Submit-time inputs accept canonical preset ids plus matching human-readable labels/common hyphen-space variants, and the tool normalizes them back to the canonical id before persisting job state.
|
|
142
|
+
Agent-facing submissions use **`preset`**; the canonical registry is `ORACLE_SUBMIT_PRESETS` in `extensions/oracle/lib/config.ts`. **`preset` is the only model-selection parameter** on `oracle_submit`. There are no `modelFamily`, `effort`, or `autoSwitchToThinking` fields. Submit-time inputs accept canonical preset ids plus matching human-readable labels/common hyphen-space variants, and the tool normalizes them back to the canonical id before persisting job state. Prompt-template guidance biases toward omitting `preset` and using the configured default unless the task clearly needs a different model or the user explicitly asked for one.
|
|
137
143
|
|
|
138
144
|
1. resolve the preset (submit-time or config default) into an execution snapshot
|
|
139
145
|
2. resolve optional `followUpJobId` into a prior `chatUrl` and `conversationId`
|
|
@@ -262,7 +268,7 @@ Long-run hygiene is intentionally conservative:
|
|
|
262
268
|
|
|
263
269
|
- runtime profiles, runtime leases, and conversation leases are cleaned immediately as part of worker/command cleanup paths
|
|
264
270
|
- 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
|
|
271
|
+
- `/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; when blocked by that grace it returns a retry-after timestamp
|
|
266
272
|
- stale lock directories are swept before reconcile maintenance
|
|
267
273
|
- old auth `.staging-*` profiles are swept during `/oracle-auth` startup when the auth browser session is not still active
|
|
268
274
|
- terminal job directories are retained for inspection, then pruned later based on configurable retention windows
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Oracle isolated `pi` validation
|
|
2
|
+
|
|
3
|
+
This document describes the repeatable pre-commit smoke test for validating `pi-oracle` through isolated `pi` agent sessions that load the local extension source.
|
|
4
|
+
|
|
5
|
+
Use this workflow for code changes when you need end-to-end evidence beyond `npm test`.
|
|
6
|
+
|
|
7
|
+
## What this validates
|
|
8
|
+
|
|
9
|
+
- the local extension can be loaded directly by isolated `pi` sessions
|
|
10
|
+
- whole-repo `oracle_submit` archive creation excludes local tool state by default
|
|
11
|
+
- targeted archive inputs cannot escape the repo through symlinked paths
|
|
12
|
+
- the exercised `pi` agents can provide candid feedback about tool clarity or clunkiness
|
|
13
|
+
|
|
14
|
+
## Why this workflow is isolated
|
|
15
|
+
|
|
16
|
+
The test intentionally uses separate directories for:
|
|
17
|
+
|
|
18
|
+
- `PI_CODING_AGENT_DIR`
|
|
19
|
+
- `--session-dir`
|
|
20
|
+
- `PI_ORACLE_JOBS_DIR`
|
|
21
|
+
|
|
22
|
+
That keeps the validation run from reusing your normal `pi` agent state.
|
|
23
|
+
|
|
24
|
+
The extension is loaded from the local checkout with:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pi --no-extensions -e "$REPO/extensions/oracle/index.ts"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
That ensures the session is exercising the in-repo code, not a globally installed package.
|
|
31
|
+
|
|
32
|
+
If you also need the in-repo `/oracle` prompt template, load it explicitly instead of installing this repository as a project-local package:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pi --no-extensions -e "$REPO/extensions/oracle/index.ts" \
|
|
36
|
+
--no-prompt-templates --prompt-template "$REPO/prompts/oracle.md"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Do not add `https://github.com/fitchmultz/pi-oracle` to this repository's `.pi/settings.json` just to test local oracle changes. If you already keep `npm:pi-oracle` installed globally, mixing the global npm package with a project-local git package creates two distinct package identities and can trigger prompt/tool conflicts. Use the explicit CLI resource flags above instead.
|
|
40
|
+
|
|
41
|
+
`oracle_submit` now preflights a missing or unreadable auth seed profile before it creates an archive or persists a job. For archive-inspection smoke tests that intentionally run without real auth, create an empty isolated seed-profile directory under the temporary agent dir so submission can proceed far enough to write the archive while still staying isolated from your normal Chrome state.
|
|
42
|
+
|
|
43
|
+
## Preset requirement
|
|
44
|
+
|
|
45
|
+
Use either:
|
|
46
|
+
|
|
47
|
+
- `instant`
|
|
48
|
+
- `thinking_light`
|
|
49
|
+
|
|
50
|
+
The examples below use `instant` because it is the fastest smoke-test preset.
|
|
51
|
+
|
|
52
|
+
## Prerequisites
|
|
53
|
+
|
|
54
|
+
- `pi` installed locally
|
|
55
|
+
- `tmux` installed locally
|
|
56
|
+
- run from the repository root
|
|
57
|
+
|
|
58
|
+
## Repeatable smoke test
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
set -euo pipefail
|
|
62
|
+
|
|
63
|
+
REPO="$PWD"
|
|
64
|
+
TEST_ROOT="/tmp/pi-oracle-isolated-tests-$$"
|
|
65
|
+
|
|
66
|
+
TEST1_AGENT="$TEST_ROOT/agent1"
|
|
67
|
+
TEST1_SESSIONS="$TEST_ROOT/sessions1"
|
|
68
|
+
TEST1_JOBS="$TEST_ROOT/jobs1"
|
|
69
|
+
TEST2_AGENT="$TEST_ROOT/agent2"
|
|
70
|
+
TEST2_SESSIONS="$TEST_ROOT/sessions2"
|
|
71
|
+
TEST2_JOBS="$TEST_ROOT/jobs2"
|
|
72
|
+
|
|
73
|
+
FIXTURE="$TEST_ROOT/symlink-fixture"
|
|
74
|
+
OUTSIDE="$TEST_ROOT/outside"
|
|
75
|
+
|
|
76
|
+
SESSION1="pi-oracle-test1"
|
|
77
|
+
SESSION2="pi-oracle-test2"
|
|
78
|
+
|
|
79
|
+
mkdir -p \
|
|
80
|
+
"$TEST1_AGENT" "$TEST1_SESSIONS" "$TEST1_JOBS" \
|
|
81
|
+
"$TEST2_AGENT" "$TEST2_SESSIONS" "$TEST2_JOBS" \
|
|
82
|
+
"$FIXTURE" "$OUTSIDE"
|
|
83
|
+
|
|
84
|
+
mkdir -p "$TEST1_AGENT/extensions/oracle-auth-seed-profile"
|
|
85
|
+
|
|
86
|
+
echo 'secret' > "$OUTSIDE/secret.txt"
|
|
87
|
+
ln -s "$OUTSIDE" "$FIXTURE/linked-outside"
|
|
88
|
+
|
|
89
|
+
PROMPT1='Call oracle_submit directly with prompt "Sanity test for archive exclusions. Reply with OK." files ["."] and preset "instant". Do not use bash. After the tool returns, summarize the outcome in 3 bullets including the job id/status, and give one sentence of candid feedback on whether the oracle tool behavior feels clear or clunky.'
|
|
90
|
+
PROMPT2='Call oracle_submit directly with prompt "Sanity test for symlink escape rejection." files ["linked-outside/secret.txt"] and preset "instant". Do not use bash. After the tool returns, summarize the outcome in 3 bullets and give one sentence of candid feedback on whether the oracle tool behavior feels clear or clunky.'
|
|
91
|
+
|
|
92
|
+
cleanup() {
|
|
93
|
+
tmux kill-session -t "$SESSION1" 2>/dev/null || true
|
|
94
|
+
tmux kill-session -t "$SESSION2" 2>/dev/null || true
|
|
95
|
+
}
|
|
96
|
+
trap cleanup EXIT
|
|
97
|
+
cleanup
|
|
98
|
+
|
|
99
|
+
TMUX_CMD1="cd '$REPO' && env PI_CODING_AGENT_DIR='$TEST1_AGENT' PI_ORACLE_JOBS_DIR='$TEST1_JOBS' PATH='$PATH' pi --session-dir '$TEST1_SESSIONS' --no-extensions -e '$REPO/extensions/oracle/index.ts'"
|
|
100
|
+
tmux new-session -d -s "$SESSION1" "$TMUX_CMD1"
|
|
101
|
+
sleep 8
|
|
102
|
+
tmux send-keys -t "$SESSION1":0.0 "$PROMPT1" Enter
|
|
103
|
+
sleep 35
|
|
104
|
+
|
|
105
|
+
echo '--- pane:test1'
|
|
106
|
+
tmux capture-pane -p -S -220 -t "$SESSION1":0.0 | tail -n 160
|
|
107
|
+
|
|
108
|
+
JOB_DIR1="$(find "$TEST1_JOBS" -maxdepth 1 -type d -name 'oracle-*' | sort | tail -n 1 || true)"
|
|
109
|
+
echo "--- latest job dir:test1 ${JOB_DIR1:-<none>}"
|
|
110
|
+
|
|
111
|
+
if [ -n "${JOB_DIR1:-}" ] && [ -f "$JOB_DIR1/job.json" ]; then
|
|
112
|
+
ARCHIVE1="$(python3 - <<'PY' "$JOB_DIR1/job.json"
|
|
113
|
+
import json,sys
|
|
114
|
+
with open(sys.argv[1]) as f:
|
|
115
|
+
print(json.load(f)['archivePath'])
|
|
116
|
+
PY
|
|
117
|
+
)"
|
|
118
|
+
echo "--- archive:test1 $ARCHIVE1"
|
|
119
|
+
tar --zstd -tf "$ARCHIVE1" | head -n 80
|
|
120
|
+
LIST="$(mktemp)"
|
|
121
|
+
tar --zstd -tf "$ARCHIVE1" > "$LIST"
|
|
122
|
+
for path in .pi/settings.json .oracle-context .cursor .scratchpad.md README.md; do
|
|
123
|
+
if grep -E -q "^${path}$|^${path}/" "$LIST"; then
|
|
124
|
+
echo "FOUND $path"
|
|
125
|
+
else
|
|
126
|
+
echo "MISSING $path"
|
|
127
|
+
fi
|
|
128
|
+
done
|
|
129
|
+
rm -f "$LIST"
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
TMUX_CMD2="cd '$FIXTURE' && env PI_CODING_AGENT_DIR='$TEST2_AGENT' PI_ORACLE_JOBS_DIR='$TEST2_JOBS' PATH='$PATH' pi --session-dir '$TEST2_SESSIONS' --no-extensions -e '$REPO/extensions/oracle/index.ts'"
|
|
133
|
+
tmux new-session -d -s "$SESSION2" "$TMUX_CMD2"
|
|
134
|
+
sleep 8
|
|
135
|
+
tmux send-keys -t "$SESSION2":0.0 "$PROMPT2" Enter
|
|
136
|
+
sleep 25
|
|
137
|
+
|
|
138
|
+
echo '--- pane:test2'
|
|
139
|
+
tmux capture-pane -p -S -220 -t "$SESSION2":0.0 | tail -n 160
|
|
140
|
+
|
|
141
|
+
echo '--- jobs created:test2'
|
|
142
|
+
find "$TEST2_JOBS" -maxdepth 1 -type d -name 'oracle-*' | sort || true
|
|
143
|
+
|
|
144
|
+
echo "TEST_ROOT=$TEST_ROOT"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Expected results
|
|
148
|
+
|
|
149
|
+
### Test 1: whole-repo archive exclusions
|
|
150
|
+
|
|
151
|
+
Expected behavior:
|
|
152
|
+
|
|
153
|
+
- the isolated `pi` session loads the local extension successfully
|
|
154
|
+
- `oracle_submit` creates a job and an archive path under the isolated jobs dir
|
|
155
|
+
- the archive should exclude:
|
|
156
|
+
- `.pi/`
|
|
157
|
+
- `.oracle-context/`
|
|
158
|
+
- `.cursor/`
|
|
159
|
+
- `.scratchpad.md`
|
|
160
|
+
- the archive should still include normal repo files such as `README.md`
|
|
161
|
+
|
|
162
|
+
Notes:
|
|
163
|
+
|
|
164
|
+
- this smoke test does not require `/oracle-auth`
|
|
165
|
+
- the snippet creates an empty isolated auth seed profile for `TEST1_AGENT` because `oracle_submit` now rejects a missing seed profile before archiving
|
|
166
|
+
- with that empty seed profile, the worker still fails later due to missing real auth, which is useful because the archive remains on disk for inspection
|
|
167
|
+
|
|
168
|
+
### Test 2: symlink escape rejection
|
|
169
|
+
|
|
170
|
+
Expected behavior:
|
|
171
|
+
|
|
172
|
+
- `oracle_submit` rejects `linked-outside/secret.txt`
|
|
173
|
+
- the error should say the archive input must resolve inside the project cwd without symlink escapes
|
|
174
|
+
- no oracle job directory should be created for the rejected submit
|
|
175
|
+
|
|
176
|
+
## Testing local `/oracle` prompt changes too
|
|
177
|
+
|
|
178
|
+
The main smoke test above calls `oracle_submit` directly, so it only needs the local extension entrypoint. If you also changed `prompts/oracle.md`, start the isolated session with the local prompt template explicitly loaded:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
LOCAL_ORACLE_PI_CMD="pi --session-dir '$TEST1_SESSIONS' --no-extensions -e '$REPO/extensions/oracle/index.ts' --no-prompt-templates --prompt-template '$REPO/prompts/oracle.md'"
|
|
182
|
+
TMUX_CMD1="cd '$REPO' && env PI_CODING_AGENT_DIR='$TEST1_AGENT' PI_ORACLE_JOBS_DIR='$TEST1_JOBS' PATH='$PATH' $LOCAL_ORACLE_PI_CMD"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Use the same pattern for additional sessions, swapping the session/job directories as needed. This keeps the test on the in-repo extension and in-repo prompt template without depending on `.pi/settings.json` package entries.
|
|
186
|
+
|
|
187
|
+
`/oracle` now starts by calling `oracle_preflight`. If you want the prompt flow to proceed past that early guard in an isolated test without using your normal auth state, create an empty isolated auth seed profile first (for example `mkdir -p "$TEST1_AGENT/extensions/oracle-auth-seed-profile"`) or run `/oracle-auth` in the isolated agent dir.
|
|
188
|
+
|
|
189
|
+
## Additional failure-mode smoke tests
|
|
190
|
+
|
|
191
|
+
### `/oracle-auth` should fail fast when `agent-browser` hangs
|
|
192
|
+
|
|
193
|
+
Use this when validating timeout hardening around auth/bootstrap browser commands.
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
set -euo pipefail
|
|
197
|
+
|
|
198
|
+
REPO="$PWD"
|
|
199
|
+
TEST_ROOT="/tmp/pi-oracle-auth-timeout-$$"
|
|
200
|
+
AGENT_DIR="$TEST_ROOT/agent"
|
|
201
|
+
SESSION_DIR="$TEST_ROOT/sessions"
|
|
202
|
+
JOBS_DIR="$TEST_ROOT/jobs"
|
|
203
|
+
FAKE_BROWSER="$TEST_ROOT/agent-browser"
|
|
204
|
+
SESSION_NAME="pi-oracle-auth-timeout"
|
|
205
|
+
|
|
206
|
+
mkdir -p "$AGENT_DIR/extensions" "$SESSION_DIR" "$JOBS_DIR"
|
|
207
|
+
|
|
208
|
+
cat > "$AGENT_DIR/extensions/oracle.json" <<JSON
|
|
209
|
+
{
|
|
210
|
+
"auth": {
|
|
211
|
+
"chromeCookiePath": "$TEST_ROOT/missing-cookies.sqlite"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
JSON
|
|
215
|
+
|
|
216
|
+
cat > "$FAKE_BROWSER" <<'SH'
|
|
217
|
+
#!/bin/sh
|
|
218
|
+
trap 'exit 0' TERM INT
|
|
219
|
+
while :; do sleep 1; done
|
|
220
|
+
SH
|
|
221
|
+
chmod +x "$FAKE_BROWSER"
|
|
222
|
+
|
|
223
|
+
cleanup() {
|
|
224
|
+
tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true
|
|
225
|
+
}
|
|
226
|
+
trap 'cleanup; rm -rf "$TEST_ROOT"' EXIT
|
|
227
|
+
cleanup
|
|
228
|
+
|
|
229
|
+
TMUX_CMD="cd '$REPO' && env PI_CODING_AGENT_DIR='$AGENT_DIR' PI_ORACLE_JOBS_DIR='$JOBS_DIR' AGENT_BROWSER_PATH='$FAKE_BROWSER' PI_ORACLE_AUTH_AGENT_BROWSER_TIMEOUT_MS='250' PI_ORACLE_AUTH_CLOSE_TIMEOUT_MS='250' PI_ORACLE_AUTH_KILL_GRACE_MS='100' PATH='$PATH' pi --session-dir '$SESSION_DIR' --no-extensions -e '$REPO/extensions/oracle/index.ts'"
|
|
230
|
+
|
|
231
|
+
tmux new-session -d -s "$SESSION_NAME" "$TMUX_CMD"
|
|
232
|
+
sleep 8
|
|
233
|
+
tmux send-keys -t "$SESSION_NAME":0.0 '/oracle-auth' Enter
|
|
234
|
+
sleep 12
|
|
235
|
+
|
|
236
|
+
tmux capture-pane -p -S -220 -t "$SESSION_NAME":0.0 | tail -n 140
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Expected behavior:
|
|
240
|
+
|
|
241
|
+
- the isolated `pi` session loads the local extension successfully
|
|
242
|
+
- `/oracle-auth` returns with an error instead of hanging indefinitely
|
|
243
|
+
- the output should mention the missing ChatGPT session-token cookies or the configured cookie source problem
|
|
244
|
+
- the session should remain usable after the command failure
|
|
245
|
+
|
|
246
|
+
## Switching to `thinking_light`
|
|
247
|
+
|
|
248
|
+
To run the same smoke test with `thinking_light`, change both prompts from:
|
|
249
|
+
|
|
250
|
+
```text
|
|
251
|
+
preset "instant"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
to:
|
|
255
|
+
|
|
256
|
+
```text
|
|
257
|
+
preset "thinking_light"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Cleanup
|
|
261
|
+
|
|
262
|
+
The snippet already kills the temporary `tmux` sessions on exit.
|
|
263
|
+
|
|
264
|
+
To remove the temporary files after inspection:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
rm -rf "$TEST_ROOT"
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Minimum pre-commit evidence
|
|
271
|
+
|
|
272
|
+
Before committing code changes, keep evidence for:
|
|
273
|
+
|
|
274
|
+
- `npm test` passing
|
|
275
|
+
- isolated `pi` session validation using this workflow
|
|
276
|
+
- any agent feedback gathered during the isolated run if it exposed clunky or unclear behavior
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// Purpose: Register the oracle extension, wire commands/tools/workers, and manage per-session background maintenance.
|
|
2
|
+
// Responsibilities: Bootstrap oracle commands and tools, start or stop polling, and surface startup/config availability in the pi session UI.
|
|
3
|
+
// Scope: Extension entrypoint only; lifecycle mutation lives in lib modules and browser execution lives in worker scripts.
|
|
4
|
+
// Usage: Loaded by pi as the extension module declared in package.json.
|
|
5
|
+
// Invariants/Assumptions: Oracle only runs against persisted sessions, and startup maintenance should be best-effort without breaking session initialization.
|
|
1
6
|
import { fileURLToPath } from "node:url";
|
|
2
7
|
import { dirname, join } from "node:path";
|
|
3
8
|
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
@@ -42,7 +47,9 @@ export default function oracleExtension(pi: ExtensionAPI) {
|
|
|
42
47
|
|
|
43
48
|
const config = loadOracleConfig(ctx.cwd);
|
|
44
49
|
void runStartupMaintenance(ctx).catch((error) => {
|
|
45
|
-
|
|
50
|
+
const message = `Oracle startup maintenance failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
51
|
+
console.error(message);
|
|
52
|
+
ctx.ui.notify(message, "warning");
|
|
46
53
|
});
|
|
47
54
|
startPoller(pi, ctx, config.poller.intervalMs, workerPath);
|
|
48
55
|
refreshOracleStatus(ctx);
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
// Purpose: Register slash commands for oracle auth/bootstrap, status inspection, cancellation, and cleanup.
|
|
2
|
+
// Responsibilities: Bridge command handlers to shared oracle lifecycle helpers, surface consistent summaries, and coordinate follow-up queue advancement.
|
|
3
|
+
// Scope: Command-facing orchestration only; durable lifecycle mutations live in jobs/runtime/tools modules and browser execution stays in worker scripts.
|
|
4
|
+
// Usage: Imported by the oracle extension entrypoint to register /oracle-* commands with pi.
|
|
5
|
+
// Invariants/Assumptions: Commands operate on persisted project-scoped jobs and rely on shared observability formatting for detached-state clarity.
|
|
1
6
|
import { spawn } from "node:child_process";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
2
8
|
import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
3
|
-
import {
|
|
9
|
+
import { formatOracleJobSummary } from "../shared/job-observability-helpers.mjs";
|
|
10
|
+
import { formatOracleAuthConfigRemediation, formatOracleAuthConfigSummary, getOracleConfigLoadDetails, loadOracleConfig } from "./config.js";
|
|
4
11
|
import {
|
|
5
12
|
cancelOracleJob,
|
|
13
|
+
getJobDir,
|
|
6
14
|
isOpenOracleJob,
|
|
7
15
|
isTerminalOracleJob,
|
|
8
16
|
listJobsForCwd,
|
|
@@ -22,30 +30,12 @@ function summarizeJob(jobId: string): string {
|
|
|
22
30
|
const job = readJob(jobId);
|
|
23
31
|
if (!job) return `Oracle job ${jobId} not found.`;
|
|
24
32
|
|
|
25
|
-
const
|
|
26
|
-
return
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
job.queuedAt ? `queued: ${job.queuedAt}` : undefined,
|
|
32
|
-
job.submittedAt ? `submitted: ${job.submittedAt}` : undefined,
|
|
33
|
-
queuePosition ? `queue-position: ${queuePosition.position} of ${queuePosition.depth} global` : undefined,
|
|
34
|
-
`project: ${job.projectId}`,
|
|
35
|
-
`session: ${job.sessionId}`,
|
|
36
|
-
job.completedAt ? `completed: ${job.completedAt}` : undefined,
|
|
37
|
-
job.followUpToJobId ? `follow-up-to: ${job.followUpToJobId}` : undefined,
|
|
38
|
-
job.chatUrl ? `chat: ${job.chatUrl}` : undefined,
|
|
39
|
-
job.conversationId ? `conversation: ${job.conversationId}` : undefined,
|
|
40
|
-
job.responsePath ? `response: ${job.responsePath}` : undefined,
|
|
41
|
-
job.responseFormat ? `response-format: ${job.responseFormat}` : undefined,
|
|
42
|
-
typeof job.artifactFailureCount === "number" ? `artifact-failures: ${job.artifactFailureCount}` : undefined,
|
|
43
|
-
job.lastCleanupAt ? `last-cleanup: ${job.lastCleanupAt}` : undefined,
|
|
44
|
-
job.cleanupWarnings?.length ? `cleanup-warnings: ${job.cleanupWarnings.join(" | ")}` : undefined,
|
|
45
|
-
job.error ? `error: ${job.error}` : undefined,
|
|
46
|
-
]
|
|
47
|
-
.filter(Boolean)
|
|
48
|
-
.join("\n");
|
|
33
|
+
const responseAvailable = Boolean(job.responsePath && existsSync(job.responsePath));
|
|
34
|
+
return formatOracleJobSummary(job, {
|
|
35
|
+
queuePosition: job.status === "queued" ? getQueuePosition(job.id) : undefined,
|
|
36
|
+
artifactsPath: `${getJobDir(job.id)}/artifacts`,
|
|
37
|
+
responseAvailable,
|
|
38
|
+
});
|
|
49
39
|
}
|
|
50
40
|
|
|
51
41
|
function getLatestJobId(cwd: string): string | undefined {
|
|
@@ -60,6 +50,12 @@ function readScopedJob(jobId: string, cwd: string) {
|
|
|
60
50
|
|
|
61
51
|
async function runAuthBootstrap(authWorkerPath: string, cwd: string): Promise<string> {
|
|
62
52
|
const config = loadOracleConfig(cwd);
|
|
53
|
+
const configLoad = getOracleConfigLoadDetails(cwd);
|
|
54
|
+
const authConfigGuidance = {
|
|
55
|
+
...configLoad,
|
|
56
|
+
remediation: formatOracleAuthConfigRemediation(configLoad),
|
|
57
|
+
summary: formatOracleAuthConfigSummary(configLoad),
|
|
58
|
+
};
|
|
63
59
|
try {
|
|
64
60
|
await withGlobalReconcileLock({ processPid: process.pid, source: "oracle_auth", cwd }, async () => {
|
|
65
61
|
await reconcileStaleOracleJobs();
|
|
@@ -70,7 +66,7 @@ async function runAuthBootstrap(authWorkerPath: string, cwd: string): Promise<st
|
|
|
70
66
|
}
|
|
71
67
|
|
|
72
68
|
return await new Promise<string>((resolve, reject) => {
|
|
73
|
-
const child = spawn(process.execPath, [authWorkerPath, JSON.stringify(config)], {
|
|
69
|
+
const child = spawn(process.execPath, [authWorkerPath, JSON.stringify({ config, configLoad: authConfigGuidance })], {
|
|
74
70
|
cwd,
|
|
75
71
|
stdio: ["ignore", "pipe", "pipe"],
|
|
76
72
|
});
|
|
@@ -164,7 +160,7 @@ export function registerOracleCommands(pi: ExtensionAPI, authWorkerPath: string,
|
|
|
164
160
|
});
|
|
165
161
|
|
|
166
162
|
pi.registerCommand("oracle-clean", {
|
|
167
|
-
description: "Remove oracle temp files for
|
|
163
|
+
description: "Remove oracle temp files for terminal jobs; recently woken jobs may stay retained briefly",
|
|
168
164
|
handler: async (args, ctx: ExtensionCommandContext) => {
|
|
169
165
|
const target = args.trim();
|
|
170
166
|
if (!target) {
|
|
@@ -209,10 +205,10 @@ export function registerOracleCommands(pi: ExtensionAPI, authWorkerPath: string,
|
|
|
209
205
|
}
|
|
210
206
|
|
|
211
207
|
refreshOracleStatus(ctx);
|
|
212
|
-
const warningSuffix = cleanupWarnings.length > 0 ? ` Cleanup warnings:\n${cleanupWarnings.join("\n")}` : "";
|
|
208
|
+
const warningSuffix = cleanupWarnings.length > 0 ? ` Cleanup blockers/warnings:\n${cleanupWarnings.join("\n")}` : "";
|
|
213
209
|
const removalSummary = removedCount === jobs.length
|
|
214
210
|
? `Removed ${removedCount} oracle job director${removedCount === 1 ? "y" : "ies"}.`
|
|
215
|
-
: `Removed ${removedCount} of ${jobs.length} oracle job director${jobs.length === 1 ? "y" : "ies"}; retained ${jobs.length - removedCount}
|
|
211
|
+
: `Removed ${removedCount} of ${jobs.length} oracle job director${jobs.length === 1 ? "y" : "ies"}; retained ${jobs.length - removedCount} due to cleanup blockers or warnings.`;
|
|
216
212
|
ctx.ui.notify(`${removalSummary}${warningSuffix}`, cleanupWarnings.length > 0 ? "warning" : "info");
|
|
217
213
|
},
|
|
218
214
|
});
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// Purpose: Define oracle configuration schema, defaults, preset selection, and local config loading behavior.
|
|
2
|
+
// Responsibilities: Normalize preset ids, load extension config from disk, expose default browser/auth/runtime settings, and validate config shape.
|
|
3
|
+
// Scope: Configuration and preset resolution only; runtime/job execution stays in sibling oracle modules.
|
|
4
|
+
// Usage: Imported by oracle tools, commands, runtime helpers, and sanity tests when config or preset resolution is required.
|
|
5
|
+
// Invariants/Assumptions: Preset ids remain the canonical model-selection contract and config loading must fail clearly on invalid user overrides.
|
|
1
6
|
import { execFileSync } from "node:child_process";
|
|
2
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
8
|
import { homedir } from "node:os";
|
|
@@ -253,6 +258,54 @@ const detectedChromeUserAgent = detectDefaultChromeUserAgent(detectedChromeExecu
|
|
|
253
258
|
const agentExtensionsDir = join(getAgentDir(), "extensions");
|
|
254
259
|
const detectedChromeProfileName = detectDefaultChromeProfileName();
|
|
255
260
|
|
|
261
|
+
export interface OracleConfigLoadDetails {
|
|
262
|
+
agentDir: string;
|
|
263
|
+
agentConfigPath: string;
|
|
264
|
+
agentConfigExists: boolean;
|
|
265
|
+
projectConfigPath: string;
|
|
266
|
+
projectConfigExists: boolean;
|
|
267
|
+
effectiveAuthConfigPath: string;
|
|
268
|
+
effectiveAuthScope: "agent";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function getOracleConfigLoadDetails(cwd: string): OracleConfigLoadDetails {
|
|
272
|
+
const agentDir = getAgentDir();
|
|
273
|
+
const agentConfigPath = join(agentDir, "extensions", "oracle.json");
|
|
274
|
+
const projectConfigPath = join(cwd, ".pi", "extensions", "oracle.json");
|
|
275
|
+
return {
|
|
276
|
+
agentDir,
|
|
277
|
+
agentConfigPath,
|
|
278
|
+
agentConfigExists: existsSync(agentConfigPath),
|
|
279
|
+
projectConfigPath,
|
|
280
|
+
projectConfigExists: existsSync(projectConfigPath),
|
|
281
|
+
effectiveAuthConfigPath: agentConfigPath,
|
|
282
|
+
effectiveAuthScope: "agent",
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function formatOracleAuthConfigRemediation(details: OracleConfigLoadDetails): string {
|
|
287
|
+
if (!details.projectConfigExists) {
|
|
288
|
+
return `Set auth.chromeProfile / auth.chromeCookiePath in ${details.effectiveAuthConfigPath}.`;
|
|
289
|
+
}
|
|
290
|
+
return (
|
|
291
|
+
`Set auth.chromeProfile / auth.chromeCookiePath in ${details.effectiveAuthConfigPath}. ` +
|
|
292
|
+
`Project overrides are also read from ${details.projectConfigPath}, but auth.* is loaded from ${details.effectiveAuthConfigPath}.`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function formatOracleAuthConfigSummary(details: OracleConfigLoadDetails): string {
|
|
297
|
+
const lines = [
|
|
298
|
+
`Effective oracle auth config: ${details.effectiveAuthConfigPath} (agent dir: ${details.agentDir}${details.agentConfigExists ? "" : "; create this file to override auth.*"})`,
|
|
299
|
+
];
|
|
300
|
+
if (details.projectConfigExists) {
|
|
301
|
+
lines.push(
|
|
302
|
+
`Project oracle config also loaded: ${details.projectConfigPath} ` +
|
|
303
|
+
`(project scope can override ${[...PROJECT_OVERRIDE_KEYS].join("/")} only; auth.* still comes from ${details.effectiveAuthConfigPath}).`,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
return lines.join("\n");
|
|
307
|
+
}
|
|
308
|
+
|
|
256
309
|
export const DEFAULT_CONFIG: OracleConfig = {
|
|
257
310
|
defaults: {
|
|
258
311
|
preset: "pro_extended",
|
|
@@ -509,7 +562,8 @@ function validateOracleConfig(value: unknown): OracleConfig {
|
|
|
509
562
|
}
|
|
510
563
|
|
|
511
564
|
export function loadOracleConfig(cwd: string): OracleConfig {
|
|
512
|
-
const
|
|
513
|
-
const
|
|
565
|
+
const details = getOracleConfigLoadDetails(cwd);
|
|
566
|
+
const globalConfig = readJson(details.agentConfigPath);
|
|
567
|
+
const projectConfig = filterProjectConfig(readJson(details.projectConfigPath));
|
|
514
568
|
return validateOracleConfig(deepMerge(deepMerge(DEFAULT_CONFIG, globalConfig), projectConfig));
|
|
515
569
|
}
|