pi-oracle 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +7 -0
  3. package/docs/ORACLE_DESIGN.md +1 -1
  4. package/docs/ORACLE_ISOLATED_PI_VALIDATION.md +249 -0
  5. package/docs/ORACLE_RECOVERY_DRILL.md +5 -4
  6. package/extensions/oracle/index.ts +8 -1
  7. package/extensions/oracle/lib/commands.ts +11 -24
  8. package/extensions/oracle/lib/config.ts +5 -0
  9. package/extensions/oracle/lib/jobs.ts +117 -217
  10. package/extensions/oracle/lib/locks.ts +41 -209
  11. package/extensions/oracle/lib/poller.ts +14 -51
  12. package/extensions/oracle/lib/queue.ts +75 -112
  13. package/extensions/oracle/lib/runtime.ts +60 -14
  14. package/extensions/oracle/lib/tools.ts +70 -67
  15. package/extensions/oracle/shared/job-coordination-helpers.d.mts +84 -0
  16. package/extensions/oracle/shared/job-coordination-helpers.mjs +168 -0
  17. package/extensions/oracle/shared/job-lifecycle-helpers.d.mts +130 -0
  18. package/extensions/oracle/shared/job-lifecycle-helpers.mjs +377 -0
  19. package/extensions/oracle/shared/job-observability-helpers.d.mts +59 -0
  20. package/extensions/oracle/shared/job-observability-helpers.mjs +143 -0
  21. package/extensions/oracle/shared/process-helpers.d.mts +20 -0
  22. package/extensions/oracle/shared/process-helpers.mjs +128 -0
  23. package/extensions/oracle/shared/state-coordination-helpers.d.mts +43 -0
  24. package/extensions/oracle/shared/state-coordination-helpers.mjs +381 -0
  25. package/extensions/oracle/worker/artifact-heuristics.mjs +5 -0
  26. package/extensions/oracle/worker/auth-bootstrap.mjs +100 -139
  27. package/extensions/oracle/worker/auth-cookie-policy.mjs +5 -0
  28. package/extensions/oracle/worker/auth-flow-helpers.d.mts +41 -0
  29. package/extensions/oracle/worker/auth-flow-helpers.mjs +165 -0
  30. package/extensions/oracle/worker/chatgpt-flow-helpers.d.mts +13 -0
  31. package/extensions/oracle/worker/chatgpt-flow-helpers.mjs +85 -0
  32. package/extensions/oracle/worker/chatgpt-ui-helpers.d.mts +33 -0
  33. package/extensions/oracle/worker/chatgpt-ui-helpers.mjs +292 -0
  34. package/extensions/oracle/worker/run-job.mjs +235 -380
  35. package/extensions/oracle/worker/state-locks.mjs +31 -216
  36. package/package.json +14 -5
  37. package/prompts/oracle.md +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 - 2026-04-12
4
+
5
+ ### Added
6
+ - repeatable isolated local-extension `pi` validation guidance for oracle release verification, including smoke workflows that load the in-repo extension source directly
7
+ - 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
8
+ - shared worker/auth validation helpers, shared concurrency primitives, shared lifecycle reducers, and shared observability formatters to keep extension and worker behavior aligned
9
+ - extracted sanity-harness support and poller suites with typed helper scaffolding and repeated-run stability coverage
10
+
11
+ ### Changed
12
+ - oracle whole-repo archiving now excludes local tool state like `.pi/`, `.oracle-context/`, `.cursor/`, and `.scratchpad.md` by default while preserving explicitly requested paths
13
+ - lock/lease recovery, queue promotion, process identity handling, and lifecycle transitions now flow through shared helper modules instead of duplicated inline implementations
14
+ - 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
15
+ - release validation now expects isolated local-extension `pi` smoke tests and a stronger local oracle verification gate before shipping
16
+
17
+ ### Fixed
18
+ - archive input resolution now rejects symlink escapes outside the repo root and preserves safer repo-boundary handling for targeted archives
19
+ - 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
20
+ - 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
21
+ - detached oracle workers and poller flows now report clearer lifecycle breadcrumbs, wake-up settlement state, and failure context during fast-fail auth/bootstrap scenarios
22
+ - 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
23
+
24
+ ## 0.3.4 - 2026-04-11
25
+
26
+ ### Changed
27
+ - oracle archive defaults now exclude nested `secrets/` and `.secrets/` directories anywhere in the repo unless they are explicitly requested
28
+ - package metadata now reflects the current runtime floor and platform support (`node >=22`, `darwin`) and local release automation runs `verify:oracle` through `npm test` / `prepublishOnly`
29
+
30
+ ### Fixed
31
+ - model verification now distinguishes `thinking`, `pro`, `instant`, and `instant_auto_switch` more conservatively instead of accepting mismatched presets on partial UI evidence
32
+ - artifact-only assistant responses can now complete without timing out on missing plain-text bodies
33
+ - `/oracle-auth` diagnostics now write into a unique private temp directory per run instead of fixed `/tmp/oracle-auth.*` paths
34
+ - sanity coverage now exercises shared ChatGPT UI helpers directly, verifies nested secret exclusion, and guards the new auth diagnostics path handling
35
+
3
36
  ## 0.3.3 - 2026-04-11
4
37
 
5
38
  ### Added
package/README.md CHANGED
@@ -147,6 +147,7 @@ Project config should only override safe, non-privileged settings.
147
147
  ## Requirements
148
148
 
149
149
  - macOS
150
+ - Node.js 22 or newer
150
151
  - Google Chrome installed
151
152
  - ChatGPT already signed into a local Chrome profile
152
153
  - `pi` 0.65.0 or newer
@@ -194,6 +195,7 @@ Project config should only override safe, non-privileged settings.
194
195
 
195
196
  - `docs/ORACLE_DESIGN.md` — architecture, lifecycle, queueing, persistence, presets, and recovery behavior
196
197
  - `docs/ORACLE_RECOVERY_DRILL.md` — safe expired-auth recovery validation drill
198
+ - `docs/ORACLE_ISOLATED_PI_VALIDATION.md` — repeatable isolated `pi` session smoke test for local-extension validation
197
199
 
198
200
  ## Privacy / local data
199
201
 
@@ -209,12 +211,17 @@ Review the code and design docs before using it with sensitive material.
209
211
  ```bash
210
212
  npm run check:oracle-extension
211
213
  npm run typecheck
214
+ npm run typecheck:worker-helpers
212
215
  npm run sanity:oracle
213
216
  npm run pack:check
217
+ # conventional local gate
218
+ npm test
214
219
  # or all at once
215
220
  npm run verify:oracle
216
221
  ```
217
222
 
223
+ `npm publish` is also guarded locally via `prepublishOnly` and will run `npm run verify:oracle` before publishing.
224
+
218
225
  ## Beta caveats
219
226
 
220
227
  The highest-risk areas to monitor are:
@@ -585,7 +585,7 @@ Remaining non-blocking hardening work:
585
585
 
586
586
  Recent proof points:
587
587
  - expired-auth drill fail path: `a2460bc1-7d89-4041-b67d-39680d310325`
588
- - `/oracle-auth` repair evidence: `/tmp/oracle-auth.log`
588
+ - `/oracle-auth` repair evidence: the per-run `/tmp/pi-oracle-auth-*/oracle-auth.log` bundle path printed by `/oracle-auth`
589
589
  - expired-auth drill post-repair success: `fa26a2a7-0057-4a21-b3e0-71c1d020facf`
590
590
  - successful multi-artifact completion: `b6b3599c-6b91-4315-adfa-8a83aa5eda9b`
591
591
  - repo-owned sanity harness: `npm run sanity:oracle`
@@ -0,0 +1,249 @@
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
+ ## Preset requirement
33
+
34
+ Use either:
35
+
36
+ - `instant`
37
+ - `thinking_light`
38
+
39
+ The examples below use `instant` because it is the fastest smoke-test preset.
40
+
41
+ ## Prerequisites
42
+
43
+ - `pi` installed locally
44
+ - `tmux` installed locally
45
+ - run from the repository root
46
+
47
+ ## Repeatable smoke test
48
+
49
+ ```bash
50
+ set -euo pipefail
51
+
52
+ REPO="$PWD"
53
+ TEST_ROOT="/tmp/pi-oracle-isolated-tests-$$"
54
+
55
+ TEST1_AGENT="$TEST_ROOT/agent1"
56
+ TEST1_SESSIONS="$TEST_ROOT/sessions1"
57
+ TEST1_JOBS="$TEST_ROOT/jobs1"
58
+ TEST2_AGENT="$TEST_ROOT/agent2"
59
+ TEST2_SESSIONS="$TEST_ROOT/sessions2"
60
+ TEST2_JOBS="$TEST_ROOT/jobs2"
61
+
62
+ FIXTURE="$TEST_ROOT/symlink-fixture"
63
+ OUTSIDE="$TEST_ROOT/outside"
64
+
65
+ SESSION1="pi-oracle-test1"
66
+ SESSION2="pi-oracle-test2"
67
+
68
+ mkdir -p \
69
+ "$TEST1_AGENT" "$TEST1_SESSIONS" "$TEST1_JOBS" \
70
+ "$TEST2_AGENT" "$TEST2_SESSIONS" "$TEST2_JOBS" \
71
+ "$FIXTURE" "$OUTSIDE"
72
+
73
+ echo 'secret' > "$OUTSIDE/secret.txt"
74
+ ln -s "$OUTSIDE" "$FIXTURE/linked-outside"
75
+
76
+ 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.'
77
+ 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.'
78
+
79
+ cleanup() {
80
+ tmux kill-session -t "$SESSION1" 2>/dev/null || true
81
+ tmux kill-session -t "$SESSION2" 2>/dev/null || true
82
+ }
83
+ trap cleanup EXIT
84
+ cleanup
85
+
86
+ 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'"
87
+ tmux new-session -d -s "$SESSION1" "$TMUX_CMD1"
88
+ sleep 8
89
+ tmux send-keys -t "$SESSION1":0.0 "$PROMPT1" Enter
90
+ sleep 35
91
+
92
+ echo '--- pane:test1'
93
+ tmux capture-pane -p -S -220 -t "$SESSION1":0.0 | tail -n 160
94
+
95
+ JOB_DIR1="$(find "$TEST1_JOBS" -maxdepth 1 -type d -name 'oracle-*' | sort | tail -n 1 || true)"
96
+ echo "--- latest job dir:test1 ${JOB_DIR1:-<none>}"
97
+
98
+ if [ -n "${JOB_DIR1:-}" ] && [ -f "$JOB_DIR1/job.json" ]; then
99
+ ARCHIVE1="$(python3 - <<'PY' "$JOB_DIR1/job.json"
100
+ import json,sys
101
+ with open(sys.argv[1]) as f:
102
+ print(json.load(f)['archivePath'])
103
+ PY
104
+ )"
105
+ echo "--- archive:test1 $ARCHIVE1"
106
+ tar --zstd -tf "$ARCHIVE1" | head -n 80
107
+ LIST="$(mktemp)"
108
+ tar --zstd -tf "$ARCHIVE1" > "$LIST"
109
+ for path in .pi/settings.json .oracle-context .cursor .scratchpad.md README.md; do
110
+ if grep -E -q "^${path}$|^${path}/" "$LIST"; then
111
+ echo "FOUND $path"
112
+ else
113
+ echo "MISSING $path"
114
+ fi
115
+ done
116
+ rm -f "$LIST"
117
+ fi
118
+
119
+ 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'"
120
+ tmux new-session -d -s "$SESSION2" "$TMUX_CMD2"
121
+ sleep 8
122
+ tmux send-keys -t "$SESSION2":0.0 "$PROMPT2" Enter
123
+ sleep 25
124
+
125
+ echo '--- pane:test2'
126
+ tmux capture-pane -p -S -220 -t "$SESSION2":0.0 | tail -n 160
127
+
128
+ echo '--- jobs created:test2'
129
+ find "$TEST2_JOBS" -maxdepth 1 -type d -name 'oracle-*' | sort || true
130
+
131
+ echo "TEST_ROOT=$TEST_ROOT"
132
+ ```
133
+
134
+ ## Expected results
135
+
136
+ ### Test 1: whole-repo archive exclusions
137
+
138
+ Expected behavior:
139
+
140
+ - the isolated `pi` session loads the local extension successfully
141
+ - `oracle_submit` creates a job and an archive path under the isolated jobs dir
142
+ - the archive should exclude:
143
+ - `.pi/`
144
+ - `.oracle-context/`
145
+ - `.cursor/`
146
+ - `.scratchpad.md`
147
+ - the archive should still include normal repo files such as `README.md`
148
+
149
+ Notes:
150
+
151
+ - this smoke test does not require `/oracle-auth`
152
+ - without an auth seed profile, the worker fails after archive creation, which is useful because the archive remains on disk for inspection
153
+
154
+ ### Test 2: symlink escape rejection
155
+
156
+ Expected behavior:
157
+
158
+ - `oracle_submit` rejects `linked-outside/secret.txt`
159
+ - the error should say the archive input must resolve inside the project cwd without symlink escapes
160
+ - no oracle job directory should be created for the rejected submit
161
+
162
+ ## Additional failure-mode smoke tests
163
+
164
+ ### `/oracle-auth` should fail fast when `agent-browser` hangs
165
+
166
+ Use this when validating timeout hardening around auth/bootstrap browser commands.
167
+
168
+ ```bash
169
+ set -euo pipefail
170
+
171
+ REPO="$PWD"
172
+ TEST_ROOT="/tmp/pi-oracle-auth-timeout-$$"
173
+ AGENT_DIR="$TEST_ROOT/agent"
174
+ SESSION_DIR="$TEST_ROOT/sessions"
175
+ JOBS_DIR="$TEST_ROOT/jobs"
176
+ FAKE_BROWSER="$TEST_ROOT/agent-browser"
177
+ SESSION_NAME="pi-oracle-auth-timeout"
178
+
179
+ mkdir -p "$AGENT_DIR/extensions" "$SESSION_DIR" "$JOBS_DIR"
180
+
181
+ cat > "$AGENT_DIR/extensions/oracle.json" <<JSON
182
+ {
183
+ "auth": {
184
+ "chromeCookiePath": "$TEST_ROOT/missing-cookies.sqlite"
185
+ }
186
+ }
187
+ JSON
188
+
189
+ cat > "$FAKE_BROWSER" <<'SH'
190
+ #!/bin/sh
191
+ trap 'exit 0' TERM INT
192
+ while :; do sleep 1; done
193
+ SH
194
+ chmod +x "$FAKE_BROWSER"
195
+
196
+ cleanup() {
197
+ tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true
198
+ }
199
+ trap 'cleanup; rm -rf "$TEST_ROOT"' EXIT
200
+ cleanup
201
+
202
+ 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'"
203
+
204
+ tmux new-session -d -s "$SESSION_NAME" "$TMUX_CMD"
205
+ sleep 8
206
+ tmux send-keys -t "$SESSION_NAME":0.0 '/oracle-auth' Enter
207
+ sleep 12
208
+
209
+ tmux capture-pane -p -S -220 -t "$SESSION_NAME":0.0 | tail -n 140
210
+ ```
211
+
212
+ Expected behavior:
213
+
214
+ - the isolated `pi` session loads the local extension successfully
215
+ - `/oracle-auth` returns with an error instead of hanging indefinitely
216
+ - the output should mention the missing ChatGPT session-token cookies or the configured cookie source problem
217
+ - the session should remain usable after the command failure
218
+
219
+ ## Switching to `thinking_light`
220
+
221
+ To run the same smoke test with `thinking_light`, change both prompts from:
222
+
223
+ ```text
224
+ preset "instant"
225
+ ```
226
+
227
+ to:
228
+
229
+ ```text
230
+ preset "thinking_light"
231
+ ```
232
+
233
+ ## Cleanup
234
+
235
+ The snippet already kills the temporary `tmux` sessions on exit.
236
+
237
+ To remove the temporary files after inspection:
238
+
239
+ ```bash
240
+ rm -rf "$TEST_ROOT"
241
+ ```
242
+
243
+ ## Minimum pre-commit evidence
244
+
245
+ Before committing code changes, keep evidence for:
246
+
247
+ - `npm test` passing
248
+ - isolated `pi` session validation using this workflow
249
+ - any agent feedback gathered during the isolated run if it exposed clunky or unclear behavior
@@ -105,10 +105,11 @@ For the failed run:
105
105
  - any failure diagnostics under that job dir
106
106
 
107
107
  For the repair:
108
- - `/tmp/oracle-auth.log`
109
- - `/tmp/oracle-auth.url.txt`
110
- - `/tmp/oracle-auth.snapshot.txt`
111
- - `/tmp/oracle-auth.body.txt`
108
+ - the per-run `/tmp/pi-oracle-auth-*/` diagnostics directory printed by `/oracle-auth`
109
+ - `oracle-auth.log`
110
+ - `oracle-auth.url.txt`
111
+ - `oracle-auth.snapshot.txt`
112
+ - `oracle-auth.body.txt`
112
113
 
113
114
  For the successful rerun:
114
115
  - `/tmp/oracle-<job-id>/job.json`
@@ -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
- console.error("Oracle startup maintenance failed:", error);
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,15 @@
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";
2
7
  import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
8
+ import { formatOracleJobSummary } from "../shared/job-observability-helpers.mjs";
3
9
  import { loadOracleConfig } from "./config.js";
4
10
  import {
5
11
  cancelOracleJob,
12
+ getJobDir,
6
13
  isOpenOracleJob,
7
14
  isTerminalOracleJob,
8
15
  listJobsForCwd,
@@ -22,30 +29,10 @@ function summarizeJob(jobId: string): string {
22
29
  const job = readJob(jobId);
23
30
  if (!job) return `Oracle job ${jobId} not found.`;
24
31
 
25
- const queuePosition = job.status === "queued" ? getQueuePosition(job.id) : undefined;
26
- return [
27
- `job: ${job.id}`,
28
- `status: ${job.status}`,
29
- `phase: ${job.phase}`,
30
- `created: ${job.createdAt}`,
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");
32
+ return formatOracleJobSummary(job, {
33
+ queuePosition: job.status === "queued" ? getQueuePosition(job.id) : undefined,
34
+ artifactsPath: `${getJobDir(job.id)}/artifacts`,
35
+ });
49
36
  }
50
37
 
51
38
  function getLatestJobId(cwd: string): string | undefined {
@@ -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";