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
|
@@ -1,237 +1,52 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
function getLocksDir(stateDir) {
|
|
25
|
-
return join(stateDir, "locks");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function getLeasesDir(stateDir) {
|
|
29
|
-
return join(stateDir, "leases");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function lockPath(stateDir, kind, key) {
|
|
33
|
-
return join(getLocksDir(stateDir), leaseKey(kind, key));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function leasePath(stateDir, kind, key) {
|
|
37
|
-
return join(getLeasesDir(stateDir), leaseKey(kind, key));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getMetadataPath(path) {
|
|
41
|
-
return join(path, "metadata.json");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function writeMetadata(path, metadata) {
|
|
45
|
-
const targetPath = getMetadataPath(path);
|
|
46
|
-
const tempPath = join(path, `metadata.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`);
|
|
47
|
-
await writeFile(tempPath, `${JSON.stringify(metadata, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
48
|
-
await chmod(tempPath, 0o600).catch(() => undefined);
|
|
49
|
-
await rename(tempPath, targetPath);
|
|
50
|
-
await chmod(targetPath, 0o600).catch(() => undefined);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function createStateDirAtomically(parentDir, finalPath, metadata) {
|
|
54
|
-
const tempPath = join(parentDir, `.tmp-${basename(finalPath)}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}`);
|
|
55
|
-
await mkdir(tempPath, { recursive: false, mode: 0o700 });
|
|
56
|
-
try {
|
|
57
|
-
await writeMetadata(tempPath, metadata);
|
|
58
|
-
await rename(tempPath, finalPath);
|
|
59
|
-
} catch (error) {
|
|
60
|
-
await rm(tempPath, { recursive: true, force: true }).catch(() => undefined);
|
|
61
|
-
throw error;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function getMetadataState(path) {
|
|
66
|
-
const metadataPath = getMetadataPath(path);
|
|
67
|
-
if (!existsSync(metadataPath)) return "missing";
|
|
68
|
-
try {
|
|
69
|
-
JSON.parse(readFileSync(metadataPath, "utf8"));
|
|
70
|
-
return "present";
|
|
71
|
-
} catch {
|
|
72
|
-
return "invalid";
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function isIncompleteStateDirStale(path, now = Date.now()) {
|
|
77
|
-
try {
|
|
78
|
-
const stats = statSync(path);
|
|
79
|
-
const baselineMs = Math.max(stats.mtimeMs, stats.ctimeMs);
|
|
80
|
-
const graceMs = basename(path).startsWith(".tmp-") ? ORACLE_TMP_STATE_DIR_GRACE_MS : ORACLE_METADATA_WRITE_GRACE_MS;
|
|
81
|
-
return now - baselineMs >= graceMs;
|
|
82
|
-
} catch {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function isProcessAlive(pid) {
|
|
88
|
-
try {
|
|
89
|
-
process.kill(pid, 0);
|
|
90
|
-
return true;
|
|
91
|
-
} catch (error) {
|
|
92
|
-
if (error && typeof error === "object" && "code" in error && error.code === "ESRCH") return false;
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function isStateDirExistsError(error) {
|
|
98
|
-
return Boolean(error && typeof error === "object" && "code" in error && (error.code === "EEXIST" || error.code === "ENOTEMPTY"));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async function readLockProcessPid(path) {
|
|
102
|
-
const metadataPath = getMetadataPath(path);
|
|
103
|
-
if (!existsSync(metadataPath)) return undefined;
|
|
104
|
-
try {
|
|
105
|
-
const metadata = JSON.parse(await readFile(metadataPath, "utf8"));
|
|
106
|
-
return typeof metadata?.processPid === "number" && Number.isInteger(metadata.processPid) && metadata.processPid > 0
|
|
107
|
-
? metadata.processPid
|
|
108
|
-
: undefined;
|
|
109
|
-
} catch {
|
|
110
|
-
return undefined;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
async function maybeReclaimIncompleteStateDir(path, now = Date.now()) {
|
|
115
|
-
if (getMetadataState(path) === "present") return false;
|
|
116
|
-
if (!isIncompleteStateDirStale(path, now)) return false;
|
|
117
|
-
await rm(path, { recursive: true, force: true }).catch(() => undefined);
|
|
118
|
-
return true;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function maybeReclaimStaleLock(path, now = Date.now()) {
|
|
122
|
-
if (await maybeReclaimIncompleteStateDir(path, now)) return true;
|
|
123
|
-
const processPid = await readLockProcessPid(path);
|
|
124
|
-
if (!processPid || isProcessAlive(processPid)) return false;
|
|
125
|
-
await rm(path, { recursive: true, force: true }).catch(() => undefined);
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export async function acquireLock(stateDir, kind, key, metadata, timeoutMs = DEFAULT_WAIT_MS) {
|
|
130
|
-
const parentDir = getLocksDir(stateDir);
|
|
131
|
-
const path = join(parentDir, leaseKey(kind, key));
|
|
132
|
-
const deadline = Date.now() + timeoutMs;
|
|
133
|
-
await ensurePrivateDir(stateDir);
|
|
134
|
-
await ensurePrivateDir(parentDir);
|
|
135
|
-
|
|
136
|
-
while (Date.now() < deadline) {
|
|
137
|
-
try {
|
|
138
|
-
await createStateDirAtomically(parentDir, path, metadata);
|
|
139
|
-
return path;
|
|
140
|
-
} catch (error) {
|
|
141
|
-
if (!isStateDirExistsError(error)) throw error;
|
|
142
|
-
if (await maybeReclaimStaleLock(path)) continue;
|
|
143
|
-
}
|
|
144
|
-
await sleep(POLL_MS);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
throw new Error(`Timed out waiting for oracle ${kind} lock: ${key}`);
|
|
1
|
+
// Purpose: Provide worker-facing wrappers around the shared oracle state lock/lease coordination helpers.
|
|
2
|
+
// Responsibilities: Bind shared state helpers to the worker call signatures and re-export crash-recovery constants.
|
|
3
|
+
// Scope: Worker wrapper only; atomic lock/lease behavior lives in shared/state-coordination-helpers.mjs.
|
|
4
|
+
// Usage: Imported by worker entrypoints that need per-state-dir locks or lease metadata persistence.
|
|
5
|
+
// Invariants/Assumptions: Callers pass the oracle state directory explicitly so worker tests can isolate state safely.
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
acquireStateLock,
|
|
9
|
+
createStateLease,
|
|
10
|
+
listStateLeaseMetadata,
|
|
11
|
+
ORACLE_METADATA_WRITE_GRACE_MS,
|
|
12
|
+
ORACLE_TMP_STATE_DIR_GRACE_MS,
|
|
13
|
+
readStateLeaseMetadata,
|
|
14
|
+
releaseStateLease,
|
|
15
|
+
releaseStatePath,
|
|
16
|
+
withStateLock,
|
|
17
|
+
writeStateLeaseMetadata,
|
|
18
|
+
} from "../shared/state-coordination-helpers.mjs";
|
|
19
|
+
|
|
20
|
+
export { ORACLE_METADATA_WRITE_GRACE_MS, ORACLE_TMP_STATE_DIR_GRACE_MS };
|
|
21
|
+
|
|
22
|
+
export async function acquireLock(stateDir, kind, key, metadata, timeoutMs) {
|
|
23
|
+
return acquireStateLock(stateDir, kind, key, metadata, timeoutMs);
|
|
148
24
|
}
|
|
149
25
|
|
|
150
26
|
export async function releaseLock(path) {
|
|
151
|
-
|
|
152
|
-
await rm(path, { recursive: true, force: true }).catch(() => undefined);
|
|
27
|
+
await releaseStatePath(path);
|
|
153
28
|
}
|
|
154
29
|
|
|
155
30
|
export async function withLock(stateDir, kind, key, metadata, fn, timeoutMs) {
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
return await fn();
|
|
159
|
-
} finally {
|
|
160
|
-
await releaseLock(handle);
|
|
161
|
-
}
|
|
31
|
+
return withStateLock(stateDir, kind, key, metadata, fn, timeoutMs);
|
|
162
32
|
}
|
|
163
33
|
|
|
164
|
-
export async function createLease(stateDir, kind, key, metadata, timeoutMs
|
|
165
|
-
|
|
166
|
-
const path = join(parentDir, leaseKey(kind, key));
|
|
167
|
-
const deadline = Date.now() + timeoutMs;
|
|
168
|
-
await ensurePrivateDir(stateDir);
|
|
169
|
-
await ensurePrivateDir(parentDir);
|
|
170
|
-
|
|
171
|
-
while (Date.now() < deadline) {
|
|
172
|
-
try {
|
|
173
|
-
await createStateDirAtomically(parentDir, path, metadata);
|
|
174
|
-
return path;
|
|
175
|
-
} catch (error) {
|
|
176
|
-
if (!isStateDirExistsError(error)) throw error;
|
|
177
|
-
if (await maybeReclaimIncompleteStateDir(path)) continue;
|
|
178
|
-
if (getMetadataState(path) === "present") throw error;
|
|
179
|
-
}
|
|
180
|
-
await sleep(POLL_MS);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
throw new Error(`Timed out waiting for oracle ${kind} lease: ${key}`);
|
|
34
|
+
export async function createLease(stateDir, kind, key, metadata, timeoutMs) {
|
|
35
|
+
return createStateLease(stateDir, kind, key, metadata, timeoutMs);
|
|
184
36
|
}
|
|
185
37
|
|
|
186
38
|
export async function writeLeaseMetadata(stateDir, kind, key, metadata) {
|
|
187
|
-
|
|
188
|
-
const path = join(parentDir, leaseKey(kind, key));
|
|
189
|
-
await ensurePrivateDir(stateDir);
|
|
190
|
-
await ensurePrivateDir(parentDir);
|
|
191
|
-
if (existsSync(path)) {
|
|
192
|
-
await chmod(path, 0o700).catch(() => undefined);
|
|
193
|
-
await writeMetadata(path, metadata);
|
|
194
|
-
return path;
|
|
195
|
-
}
|
|
196
|
-
try {
|
|
197
|
-
await createStateDirAtomically(parentDir, path, metadata);
|
|
198
|
-
} catch (error) {
|
|
199
|
-
if (!isStateDirExistsError(error)) throw error;
|
|
200
|
-
if (await maybeReclaimIncompleteStateDir(path)) {
|
|
201
|
-
await createStateDirAtomically(parentDir, path, metadata);
|
|
202
|
-
} else {
|
|
203
|
-
await writeMetadata(path, metadata);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return path;
|
|
39
|
+
return writeStateLeaseMetadata(stateDir, kind, key, metadata);
|
|
207
40
|
}
|
|
208
41
|
|
|
209
42
|
export async function readLeaseMetadata(stateDir, kind, key) {
|
|
210
|
-
|
|
211
|
-
if (!existsSync(path)) return undefined;
|
|
212
|
-
try {
|
|
213
|
-
return JSON.parse(await readFile(path, "utf8"));
|
|
214
|
-
} catch {
|
|
215
|
-
return undefined;
|
|
216
|
-
}
|
|
43
|
+
return readStateLeaseMetadata(stateDir, kind, key);
|
|
217
44
|
}
|
|
218
45
|
|
|
219
46
|
export function listLeaseMetadata(stateDir, kind) {
|
|
220
|
-
|
|
221
|
-
if (!existsSync(dir)) return [];
|
|
222
|
-
return readdirSync(dir)
|
|
223
|
-
.filter((name) => name.startsWith(`${kind}-`))
|
|
224
|
-
.map((name) => join(dir, name, "metadata.json"))
|
|
225
|
-
.filter((path) => existsSync(path))
|
|
226
|
-
.flatMap((path) => {
|
|
227
|
-
try {
|
|
228
|
-
return [JSON.parse(readFileSync(path, "utf8"))];
|
|
229
|
-
} catch {
|
|
230
|
-
return [];
|
|
231
|
-
}
|
|
232
|
-
});
|
|
47
|
+
return listStateLeaseMetadata(stateDir, kind);
|
|
233
48
|
}
|
|
234
49
|
|
|
235
50
|
export async function releaseLease(stateDir, kind, key) {
|
|
236
|
-
await
|
|
51
|
+
await releaseStateLease(stateDir, kind, key);
|
|
237
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-oracle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "ChatGPT web-oracle extension for pi with isolated browser auth, async jobs, and project-context archives.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,11 +42,12 @@
|
|
|
42
42
|
]
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
|
-
"check:oracle-extension": "node --check extensions/oracle/worker/run-job.mjs && node --check extensions/oracle/worker/state-locks.mjs && node --check extensions/oracle/worker/artifact-heuristics.mjs && node --check extensions/oracle/worker/chatgpt-ui-helpers.mjs && node --check extensions/oracle/worker/auth-cookie-policy.mjs && node --check extensions/oracle/worker/auth-bootstrap.mjs && esbuild extensions/oracle/index.ts --bundle --platform=node --format=esm --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-ai --external:@sinclair/typebox --outfile=/tmp/pi-oracle-extension-check.js",
|
|
45
|
+
"check:oracle-extension": "node --check extensions/oracle/shared/process-helpers.mjs && node --check extensions/oracle/shared/state-coordination-helpers.mjs && node --check extensions/oracle/shared/job-coordination-helpers.mjs && node --check extensions/oracle/shared/job-lifecycle-helpers.mjs && node --check extensions/oracle/shared/job-observability-helpers.mjs && node --check extensions/oracle/worker/run-job.mjs && node --check extensions/oracle/worker/state-locks.mjs && node --check extensions/oracle/worker/artifact-heuristics.mjs && node --check extensions/oracle/worker/chatgpt-ui-helpers.mjs && node --check extensions/oracle/worker/chatgpt-flow-helpers.mjs && node --check extensions/oracle/worker/auth-flow-helpers.mjs && node --check extensions/oracle/worker/auth-cookie-policy.mjs && node --check extensions/oracle/worker/auth-bootstrap.mjs && esbuild extensions/oracle/index.ts --bundle --platform=node --format=esm --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-ai --external:@sinclair/typebox --outfile=/tmp/pi-oracle-extension-check.js",
|
|
46
46
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
47
|
+
"typecheck:worker-helpers": "tsc --noEmit -p tsconfig.worker-helpers.json",
|
|
47
48
|
"sanity:oracle": "node scripts/oracle-sanity-runner.mjs",
|
|
48
49
|
"pack:check": "npm pack --dry-run",
|
|
49
|
-
"verify:oracle": "npm run check:oracle-extension && npm run typecheck && npm run sanity:oracle && npm run pack:check",
|
|
50
|
+
"verify:oracle": "npm run check:oracle-extension && npm run typecheck && npm run typecheck:worker-helpers && npm run sanity:oracle && npm run pack:check",
|
|
50
51
|
"test": "npm run verify:oracle",
|
|
51
52
|
"prepublishOnly": "npm run verify:oracle"
|
|
52
53
|
},
|
package/prompts/oracle.md
CHANGED
|
@@ -6,29 +6,35 @@ You are preparing an /oracle job.
|
|
|
6
6
|
Do not answer the user's request directly yet.
|
|
7
7
|
|
|
8
8
|
Required workflow:
|
|
9
|
-
1.
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
4.
|
|
13
|
-
5.
|
|
14
|
-
6.
|
|
9
|
+
1. Call `oracle_preflight` immediately.
|
|
10
|
+
2. If `oracle_preflight` reports `ready: false`, stop immediately and report the blocking issue plus the suggested next step. Do not read files, search the codebase, or prepare archive inputs first.
|
|
11
|
+
3. Understand the request and decide whether it is explicitly narrow or genuinely broad.
|
|
12
|
+
4. Gather only the smallest repo context needed to choose archive inputs and write a strong oracle prompt.
|
|
13
|
+
5. If the user scope is explicit and narrow, prefer a minimal targeted read/search set and dispatch as soon as you have enough context. Do not broaden into repo-wide exploration unless the narrow pass proves insufficient.
|
|
14
|
+
6. If the request is broad, architectural, release-oriented, or otherwise repo-wide, gather broader context and usually archive `.`.
|
|
15
|
+
7. Choose archive inputs for the oracle job.
|
|
16
|
+
8. Craft a concise but complete oracle prompt for ChatGPT web.
|
|
17
|
+
9. Call `oracle_submit` with the prompt and exact archive inputs.
|
|
18
|
+
10. Stop immediately after dispatching the oracle job.
|
|
15
19
|
|
|
16
20
|
Oracle model (`oracle_submit`):
|
|
17
21
|
- To choose a specific ChatGPT model, pass **`preset`** with one of the allowed ids from the canonical preset registry.
|
|
18
22
|
- Matching human-readable preset labels and common hyphen/space variants are also accepted and normalized automatically, but prefer canonical ids when readily available.
|
|
19
23
|
- **Or** omit **`preset`** entirely to use the configured default model (from oracle config).
|
|
20
24
|
- **`preset`** is the only model-selection parameter on `oracle_submit`. Do not pass `modelFamily`, `effort`, or `autoSwitchToThinking`.
|
|
21
|
-
- If unsure
|
|
25
|
+
- If unsure, omit **`preset`** and use the configured default. Ask the user about model choice only when they explicitly want model control or the choice would materially change the result.
|
|
22
26
|
|
|
23
27
|
Rules:
|
|
28
|
+
- Use `oracle_preflight` before any expensive `/oracle` preparation so missing persisted-session or local auth/config blockers fail fast.
|
|
24
29
|
- Always include an archive. Do not submit without context files.
|
|
25
|
-
- By default, include the whole repository by passing
|
|
30
|
+
- By default, include the whole repository by passing `.` when the request is genuinely broad, repo-wide, or unclear after a quick narrow pass. Default archive exclusions apply automatically, including common bulky outputs and obvious credentials/private data like `.env` files, key material, credential dotfiles, local database files, and nested `secrets/` directories anywhere in the repo.
|
|
26
31
|
- Only limit file selection if the user explicitly requests it, if the task is clearly scoped to a smaller area, or if privacy/sensitivity requires it.
|
|
27
|
-
- For very targeted asks like reviewing one function
|
|
32
|
+
- For very targeted asks like reviewing one function, one file, one stack trace, or one narrowly scoped question, start with the smallest obviously sufficient archive and expand only if that first pass proves insufficient.
|
|
33
|
+
- Do not keep exploring once you already have enough context to submit well.
|
|
28
34
|
- If the request depends on git state or pending changes (for example code review, ship readiness, or release approval), create a tracked diff bundle file inside the repo (for example under `.pi/`) containing `git status` plus `git diff` output, include that file in the archive, and tell the oracle to use it because the `.git` directory is not included in oracle exports.
|
|
29
35
|
- When `files=["."]` and the post-exclusion archive is still too large, submit automatically prunes the largest nested directories matching generic generated-output names like `build/`, `dist/`, `out/`, `coverage/`, and `tmp/` outside obvious source roots like `src/` and `lib/` until the archive fits or no candidate remains. Successful submissions report what was pruned.
|
|
30
36
|
- If a submitted oracle job later fails because upload is rejected, retry with a smaller archive in this order: (1) remove the largest obviously irrelevant/generated content, (2) if still too large, include modified files plus adjacent files plus directly relevant subtrees, (3) if still too large, explain the cut or ask the user.
|
|
31
|
-
- Prefer the configured default (omit **`preset`**) unless the task clearly needs a different model; then choose a canonical **`preset`** id.
|
|
37
|
+
- Prefer the configured default (omit **`preset`**) unless the task clearly needs a different model or the user explicitly asked for one; then choose a canonical **`preset`** id.
|
|
32
38
|
- If `oracle_submit` itself fails because the local archive still exceeds the upload limit after default exclusions and automatic generic generated-output-dir pruning, or for any other submit-time error, stop and report the error. Do not retry automatically.
|
|
33
39
|
- If `oracle_submit` returns a queued job instead of an immediately dispatched one, treat that as success and end your turn exactly the same way.
|
|
34
40
|
- After oracle_submit returns, end your turn. Do not keep working while the oracle runs.
|