pi-oracle 0.6.0 → 0.6.2
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 +15 -0
- package/README.md +2 -0
- package/docs/ORACLE_DESIGN.md +6 -4
- package/extensions/oracle/lib/jobs.ts +8 -1
- package/extensions/oracle/lib/tools.ts +28 -9
- package/package.json +1 -1
- package/prompts/oracle-followup.md +4 -2
- package/prompts/oracle.md +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.6.2 - 2026-04-13
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- `/oracle` and `/oracle-followup` now treat pre-dispatch `archive_too_large` submit failures as retryable archive-selection misses instead of immediate dead ends, so agents can automatically narrow the archive and retry before surfacing the problem to the user
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- archive-too-large submit errors now explain the 250 MB limit in human-readable terms, state that dispatch stopped before launch, and describe an ordered retry plan for shrinking the archive
|
|
12
|
+
- oracle tool guidance, docs, and sanity coverage now align on only ending the turn after successful/queued `oracle_submit` results instead of accidentally stopping on a retryable oversize failure
|
|
13
|
+
|
|
14
|
+
## 0.6.1 - 2026-04-13
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- whole-repo archive expansion now merges very large entry groups iteratively instead of using spread/flat patterns that can overflow the JavaScript call stack during `oracle_submit`
|
|
18
|
+
- oracle sanity coverage now guards the large-entry merge path so broad archive submissions regress to a real archive/env error instead of `Maximum call stack size exceeded`
|
|
19
|
+
|
|
5
20
|
## 0.6.0 - 2026-04-13
|
|
6
21
|
|
|
7
22
|
### Added
|
package/README.md
CHANGED
|
@@ -54,6 +54,8 @@ The `/oracle` prompt now runs an early oracle preflight before it gathers repo c
|
|
|
54
54
|
|
|
55
55
|
For explicitly narrow requests, `/oracle` should still prefer a context-rich relevant archive up to the 250 MB ceiling, including nearby tests, docs, config, and adjacent modules when that can improve answer quality. Reserve tightly minimal archives for an explicit user request for a tight archive, privacy-sensitive material, or size-constrained cases. It should also omit `preset` and use the configured default model unless the task clearly needs a different one.
|
|
56
56
|
|
|
57
|
+
If a local archive still exceeds the 250 MB limit after default exclusions and automatic whole-repo pruning, the agent should treat that as a retryable archive-selection failure: shrink the archive automatically, retry with a smaller relevant slice, and explain what it cut only if it still cannot fit after the allowed retry budget.
|
|
58
|
+
|
|
57
59
|
If you miss the wake-up, the result is still saved durably in the oracle job directory and can be read later.
|
|
58
60
|
|
|
59
61
|
## Example requests
|
package/docs/ORACLE_DESIGN.md
CHANGED
|
@@ -115,9 +115,11 @@ Instead it instructs the agent to:
|
|
|
115
115
|
5. gather enough repo context to submit well and bias toward context-rich archives when they fit within the 250 MB ceiling
|
|
116
116
|
6. if the request is narrow, start from the directly relevant area but still include nearby tests, docs, config, and adjacent modules when they may improve answer quality
|
|
117
117
|
7. if the request is broad/repo-wide, gather broader context and usually archive `.`
|
|
118
|
-
8.
|
|
119
|
-
9.
|
|
120
|
-
10.
|
|
118
|
+
8. if `oracle_submit` fails before dispatch with an `archive_too_large` / upload-limit error, treat that as retryable: use the reported size summary plus any auto-pruned paths to cut scope and retry automatically with a smaller archive
|
|
119
|
+
9. stop retrying after at most two total submit attempts for the same request; if it still does not fit, report what was cut and why
|
|
120
|
+
10. craft the oracle prompt
|
|
121
|
+
11. call `oracle_submit`
|
|
122
|
+
12. stop and wait for the completion wake-up (best-effort; durable oracle response/artifact state is already persisted outside session history)
|
|
121
123
|
|
|
122
124
|
### `/oracle-auth`
|
|
123
125
|
|
|
@@ -148,7 +150,7 @@ The authenticated seed profile remains the source of truth for future oracle run
|
|
|
148
150
|
|
|
149
151
|
### `oracle_submit`
|
|
150
152
|
|
|
151
|
-
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. It also biases toward context-rich archives up to the 250 MB ceiling, narrowing only when the user explicitly asks for a tight archive, privacy/sensitivity requires it, or size pressure forces it.
|
|
153
|
+
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. It also biases toward context-rich archives up to the 250 MB ceiling, narrowing only when the user explicitly asks for a tight archive, privacy/sensitivity requires it, or size pressure forces it. When local archive creation still exceeds that ceiling after default exclusions and whole-repo auto-pruning, prompt guidance now treats the failure as a retryable archive-selection miss rather than a terminal dead end: agents should cut scope automatically, retry once or twice, and only surface the cut decisions if the archive still cannot fit.
|
|
152
154
|
|
|
153
155
|
1. resolve the preset (submit-time or config default) into an execution snapshot
|
|
154
156
|
2. resolve optional `followUpJobId` into a prior `chatUrl` and `conversationId`
|
|
@@ -43,6 +43,8 @@ export const ORACLE_NOTIFICATION_CLAIM_TTL_MS = 60_000;
|
|
|
43
43
|
export const ORACLE_WAKEUP_MAX_ATTEMPTS = 3;
|
|
44
44
|
export const ORACLE_WAKEUP_RETRY_DELAYS_MS = [0, 15_000, 60_000] as const;
|
|
45
45
|
export const ORACLE_WAKEUP_POST_SEND_RETENTION_MS = 2 * 60 * 1000;
|
|
46
|
+
const ORACLE_JOB_DIR_RM_MAX_RETRIES = 5;
|
|
47
|
+
const ORACLE_JOB_DIR_RM_RETRY_DELAY_MS = 50;
|
|
46
48
|
const ORACLE_COMPLETE_JOB_RETENTION_MS = 14 * 24 * 60 * 60 * 1000;
|
|
47
49
|
const ORACLE_FAILED_JOB_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
48
50
|
export const DEFAULT_ORACLE_JOBS_DIR = "/tmp";
|
|
@@ -507,7 +509,12 @@ export async function removeTerminalOracleJob(job: OracleJob): Promise<{ removed
|
|
|
507
509
|
}));
|
|
508
510
|
return { removed: false, cleanupReport };
|
|
509
511
|
}
|
|
510
|
-
await rm(getJobDir(current.id), {
|
|
512
|
+
await rm(getJobDir(current.id), {
|
|
513
|
+
recursive: true,
|
|
514
|
+
force: true,
|
|
515
|
+
maxRetries: ORACLE_JOB_DIR_RM_MAX_RETRIES,
|
|
516
|
+
retryDelay: ORACLE_JOB_DIR_RM_RETRY_DELAY_MS,
|
|
517
|
+
});
|
|
511
518
|
return { removed: true, cleanupReport };
|
|
512
519
|
});
|
|
513
520
|
}
|
|
@@ -155,6 +155,20 @@ type ArchiveCreationResult = {
|
|
|
155
155
|
includedEntries: string[];
|
|
156
156
|
};
|
|
157
157
|
|
|
158
|
+
function appendArchiveEntries(target: string[], source: Iterable<string>): void {
|
|
159
|
+
for (const entry of source) target.push(entry);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function mergeArchiveEntryGroups(groups: Iterable<Iterable<string>>): string[] {
|
|
163
|
+
const merged: string[] = [];
|
|
164
|
+
for (const group of groups) appendArchiveEntries(merged, group);
|
|
165
|
+
return merged;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function mergeArchiveEntryGroupsForTesting(groups: Iterable<Iterable<string>>): string[] {
|
|
169
|
+
return mergeArchiveEntryGroups(groups);
|
|
170
|
+
}
|
|
171
|
+
|
|
158
172
|
function pathContainsSequence(relativePath: string, sequence: readonly string[]): boolean {
|
|
159
173
|
const segments = relativePath.split("/").filter(Boolean);
|
|
160
174
|
if (sequence.length === 0 || segments.length < sequence.length) return false;
|
|
@@ -268,7 +282,7 @@ async function expandArchiveEntries(cwd: string, relativePath: string, options?:
|
|
|
268
282
|
for (const child of children.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
269
283
|
const childRelative = child.name;
|
|
270
284
|
if (await shouldExcludeArchiveChild(join(cwd, childRelative), childRelative, child)) continue;
|
|
271
|
-
if (child.isDirectory()) results
|
|
285
|
+
if (child.isDirectory()) appendArchiveEntries(results, await expandArchiveEntries(cwd, childRelative));
|
|
272
286
|
else results.push(childRelative);
|
|
273
287
|
}
|
|
274
288
|
return results;
|
|
@@ -284,7 +298,7 @@ async function expandArchiveEntries(cwd: string, relativePath: string, options?:
|
|
|
284
298
|
for (const child of children.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
285
299
|
const childRelative = posix.join(normalized, child.name);
|
|
286
300
|
if (await shouldExcludeArchiveChild(join(cwd, childRelative), childRelative, child, { forceInclude: options?.forceIncludeSubtree })) continue;
|
|
287
|
-
if (child.isDirectory()) results
|
|
301
|
+
if (child.isDirectory()) appendArchiveEntries(results, await expandArchiveEntries(cwd, childRelative, { forceIncludeSubtree: options?.forceIncludeSubtree }));
|
|
288
302
|
else results.push(childRelative);
|
|
289
303
|
}
|
|
290
304
|
return results;
|
|
@@ -294,11 +308,12 @@ async function resolveExpandedArchiveEntriesFromInputs(
|
|
|
294
308
|
cwd: string,
|
|
295
309
|
entries: Array<{ absolute: string; relative: string }>,
|
|
296
310
|
): Promise<string[]> {
|
|
297
|
-
|
|
311
|
+
const expandedGroups = await Promise.all(entries.map(async (entry) => {
|
|
298
312
|
const statEntry = await lstat(entry.absolute);
|
|
299
313
|
const forceIncludeSubtree = statEntry.isDirectory() && entry.relative !== "." && shouldExcludeArchivePath(entry.relative, true);
|
|
300
314
|
return expandArchiveEntries(cwd, entry.relative, { forceIncludeSubtree });
|
|
301
|
-
}))
|
|
315
|
+
}));
|
|
316
|
+
return Array.from(new Set(mergeArchiveEntryGroups(expandedGroups))).sort();
|
|
302
317
|
}
|
|
303
318
|
|
|
304
319
|
export async function resolveExpandedArchiveEntries(cwd: string, files: string[]): Promise<string[]> {
|
|
@@ -323,14 +338,15 @@ function formatArchiveOversizeError(args: {
|
|
|
323
338
|
const topLevel = summarizeTopLevelIncludedPaths(args.entrySizes);
|
|
324
339
|
const adaptiveCandidates = summarizeAdaptivePruneCandidates(args.entrySizes, args.adaptivePruneMinBytes).slice(0, 7);
|
|
325
340
|
return [
|
|
326
|
-
`Oracle archive exceeds ChatGPT upload limit after default exclusions${args.autoPrunedPrefixes.length > 0 ? " and automatic generic generated-output-dir pruning" : ""}
|
|
341
|
+
`Oracle archive exceeds ChatGPT upload limit (${formatBytes(args.maxBytes)}) after default exclusions${args.autoPrunedPrefixes.length > 0 ? " and automatic generic generated-output-dir pruning" : ""}.`,
|
|
342
|
+
`The local archive measured ${formatBytes(args.archiveBytes)} (${args.archiveBytes} bytes), so submission stopped before dispatch.`,
|
|
327
343
|
args.autoPrunedPrefixes.length > 0 ? "Automatically pruned generic generated-output paths before failing:" : undefined,
|
|
328
344
|
...args.autoPrunedPrefixes.map((entry) => `- ${formatDirectoryLabel(entry.relativePath)} — ${formatBytes(entry.bytes)}`),
|
|
329
345
|
topLevel.length > 0 ? "Approx top-level included sizes:" : undefined,
|
|
330
346
|
...topLevel.map((entry) => `- ${entry.relativePath} — ${formatBytes(entry.bytes)}`),
|
|
331
347
|
adaptiveCandidates.length > 0 ? "Largest remaining generic generated-output-dir candidates:" : undefined,
|
|
332
348
|
...adaptiveCandidates.map((entry) => `- ${formatDirectoryLabel(entry.relativePath)} — ${formatBytes(entry.bytes)}`),
|
|
333
|
-
"
|
|
349
|
+
"Recommended retry order: (1) remove the largest obviously irrelevant/generated/history/export content, (2) if it still does not fit, keep only the directly relevant subtrees plus adjacent docs/tests/config, (3) if it still does not fit, explain what was cut before asking the user.",
|
|
334
350
|
]
|
|
335
351
|
.filter(Boolean)
|
|
336
352
|
.join("\n");
|
|
@@ -863,7 +879,7 @@ function buildOracleToolErrorDetails(toolName: OracleToolErrorSource, error: unk
|
|
|
863
879
|
return {
|
|
864
880
|
code: "archive_too_large",
|
|
865
881
|
message,
|
|
866
|
-
suggestedNextStep: "Retry with
|
|
882
|
+
suggestedNextStep: "This failure is retryable. Retry automatically with fewer selected files: first remove the largest obviously irrelevant/generated/history/export content, then if needed narrow to the directly relevant subtrees plus adjacent docs/tests/config, and if it still does not fit explain what was cut before asking the user.",
|
|
867
883
|
};
|
|
868
884
|
}
|
|
869
885
|
|
|
@@ -1057,9 +1073,12 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1057
1073
|
"Do not default to a one-file archive for a single function, file, or stack trace if the relevant surrounding context still fits comfortably within the limit.",
|
|
1058
1074
|
"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.",
|
|
1059
1075
|
"If a submitted oracle job later fails because upload is rejected, retry smaller: remove the largest obviously irrelevant/generated content first, then narrow to modified files plus adjacent files plus directly relevant subtrees, then explain the cut or ask the user if still needed.",
|
|
1060
|
-
"If oracle_submit
|
|
1076
|
+
"If oracle_submit fails before dispatch with details.error.code === 'archive_too_large' or an upload-limit message, treat that as retryable and retry automatically with a smaller archive.",
|
|
1077
|
+
"For archive_too_large retries, use the reported top-level size summary and any auto-pruned paths to decide what to cut: first remove the largest obviously irrelevant/generated/history/export content, then if needed narrow to the directly relevant subtrees plus adjacent docs/tests/config.",
|
|
1078
|
+
"Do not loop forever: stop after at most two total oracle_submit attempts for the same request, and if it still does not fit explain what was cut and why.",
|
|
1079
|
+
"For any other submit-time error, stop and report the error instead of retrying automatically.",
|
|
1061
1080
|
"If oracle_submit returns a queued job instead of an immediately dispatched one, treat that as success and stop exactly the same way.",
|
|
1062
|
-
"
|
|
1081
|
+
"After a successful or queued oracle_submit, stop; do not continue the task while the oracle job is running. If oracle_submit failed with retryable archive_too_large, narrow the archive and retry first.",
|
|
1063
1082
|
"Use `preset` as the only model-selection parameter on oracle_submit. Canonical ids are preferred, and matching human-readable preset labels are normalized automatically. Omit preset to use the configured default.",
|
|
1064
1083
|
],
|
|
1065
1084
|
parameters: ORACLE_SUBMIT_PARAMS,
|
package/package.json
CHANGED
|
@@ -39,10 +39,12 @@ Rules:
|
|
|
39
39
|
- 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.
|
|
40
40
|
- 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.
|
|
41
41
|
- 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.
|
|
42
|
+
- If `oracle_submit` fails before dispatch with `details.error.code === "archive_too_large"` or an upload-limit message, that failure is retryable. Use the reported top-level size summary and any auto-pruned paths to choose a smaller archive and retry automatically.
|
|
43
|
+
- For archive-too-large retries, cut scope in this order: (1) if you used `.`, remove the largest obviously irrelevant/generated/history/export/report content while preserving relevant source/docs/config/tests, (2) if it still does not fit, archive only the directly relevant subtrees plus adjacent docs/tests/config, (3) if it still does not fit after at most two total `oracle_submit` attempts, report what you cut and why.
|
|
42
44
|
- 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.
|
|
43
|
-
-
|
|
45
|
+
- For any other `oracle_submit` submit-time error, stop and report the error. Do not retry automatically.
|
|
44
46
|
- 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.
|
|
45
|
-
- After `oracle_submit
|
|
47
|
+
- After a successful or queued `oracle_submit`, end your turn. Do not keep working while the oracle runs. If `oracle_submit` failed with a retryable archive-too-large error, shrink the archive and retry first.
|
|
46
48
|
|
|
47
49
|
User request:
|
|
48
50
|
$@
|
package/prompts/oracle.md
CHANGED
|
@@ -35,10 +35,12 @@ Rules:
|
|
|
35
35
|
- 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.
|
|
36
36
|
- 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.
|
|
37
37
|
- 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.
|
|
38
|
+
- If `oracle_submit` fails before dispatch with `details.error.code === "archive_too_large"` or an upload-limit message, that failure is retryable. Use the reported top-level size summary and any auto-pruned paths to choose a smaller archive and retry automatically.
|
|
39
|
+
- For archive-too-large retries, cut scope in this order: (1) if you used `.`, remove the largest obviously irrelevant/generated/history/export/report content while preserving relevant source/docs/config/tests, (2) if it still does not fit, archive only the directly relevant subtrees plus adjacent docs/tests/config, (3) if it still does not fit after at most two total `oracle_submit` attempts, report what you cut and why.
|
|
38
40
|
- 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.
|
|
39
|
-
-
|
|
41
|
+
- For any other `oracle_submit` submit-time error, stop and report the error. Do not retry automatically.
|
|
40
42
|
- 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.
|
|
41
|
-
- After oracle_submit
|
|
43
|
+
- After a successful or queued `oracle_submit`, end your turn. Do not keep working while the oracle runs. If `oracle_submit` failed with a retryable archive-too-large error, shrink the archive and retry first.
|
|
42
44
|
|
|
43
45
|
User request:
|
|
44
46
|
$@
|