pi-oracle 0.7.9 → 0.7.10
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 +9 -0
- package/README.md +7 -2
- package/docs/ORACLE_DESIGN.md +11 -5
- package/extensions/oracle/lib/tools.ts +133 -24
- package/package.json +1 -1
- package/prompts/oracle.md +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.7.10 - 2026-06-13
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- added explicit existing ChatGPT browser-thread targeting for `/oracle`, `oracle_preflight`, and `oracle_submit` through optional `chatGptConversationId`, accepting raw ChatGPT conversation ids or full `https://chatgpt.com/c/...` / `https://chat.openai.com/c/...` URLs while preserving fresh-thread defaults when omitted
|
|
9
|
+
|
|
10
|
+
### Validation
|
|
11
|
+
- verified existing-thread preflight and submit flows in isolated `pi` sessions, including persisted `chatUrl` / `conversationId` job metadata with a fake worker
|
|
12
|
+
- ran the full `npm run verify:oracle` release gate
|
|
13
|
+
|
|
5
14
|
## 0.7.9 - 2026-06-11
|
|
6
15
|
|
|
7
16
|
### Fixed
|
package/README.md
CHANGED
|
@@ -134,6 +134,10 @@ If the wake-up does not arrive, run:
|
|
|
134
134
|
/oracle-followup <job-id> Tighten the migration plan around rollback risk, and include the most relevant surrounding files/docs as long as the archive stays comfortably within the 250 MiB limit.
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
+
```text
|
|
138
|
+
/oracle Continue existing ChatGPT conversation 6a28ab5c-e4d4-83e8-b8be-dd39f38a26d6. Review the current auth code and include enough surrounding context to propose concrete fixes.
|
|
139
|
+
```
|
|
140
|
+
|
|
137
141
|
## How it works
|
|
138
142
|
|
|
139
143
|
```mermaid
|
|
@@ -153,13 +157,14 @@ Key design choices:
|
|
|
153
157
|
- **Tools own execution.** `oracle_submit` builds the archive, admits or queues the job, starts the worker, and returns immediately.
|
|
154
158
|
- **Auth uses a seed profile.** `/oracle-auth` imports cookies into an isolated seed profile; each job clones that seed into its own temporary runtime profile.
|
|
155
159
|
- **Follow-ups preserve provider thread state.** `/oracle-followup <job-id> ...` resolves the prior job's saved provider URL and submits the next prompt with `followUpJobId`.
|
|
160
|
+
- **Existing ChatGPT browser threads are opt-in.** Normal `/oracle` jobs still start a fresh provider thread. When the user explicitly provides a ChatGPT conversation id or `https://chatgpt.com/c/...` URL, the agent passes `chatGptConversationId` so `oracle_submit` opens that existing thread in the isolated runtime.
|
|
156
161
|
- **Wake-up is best effort, storage is durable.** A missed wake-up does not lose the result.
|
|
157
162
|
|
|
158
163
|
## Commands and tools
|
|
159
164
|
|
|
160
165
|
User-facing commands:
|
|
161
166
|
|
|
162
|
-
- `/oracle <request>` — prepare context and dispatch a ChatGPT or Grok web oracle job
|
|
167
|
+
- `/oracle <request>` — prepare context and dispatch a ChatGPT or Grok web oracle job. If the request explicitly includes an existing ChatGPT conversation id/URL, the agent can continue that browser-created thread; otherwise `/oracle` starts a fresh thread as before.
|
|
163
168
|
- `/oracle-followup <job-id> <request>` — continue an earlier oracle job in the same provider thread
|
|
164
169
|
- `/oracle-auth [chatgpt|grok]` — sync provider cookies into the isolated oracle auth seed profile
|
|
165
170
|
- `/oracle-read [job-id]` — inspect job status and saved response preview
|
|
@@ -171,7 +176,7 @@ Agent-facing tools:
|
|
|
171
176
|
|
|
172
177
|
- `oracle_preflight`
|
|
173
178
|
- `oracle_auth`
|
|
174
|
-
- `oracle_submit`
|
|
179
|
+
- `oracle_submit` (`chatGptConversationId` is optional and only for explicitly continuing an existing ChatGPT browser conversation id/URL; omit it for the default fresh thread)
|
|
175
180
|
- `oracle_read`
|
|
176
181
|
- `oracle_cancel`
|
|
177
182
|
|
package/docs/ORACLE_DESIGN.md
CHANGED
|
@@ -157,7 +157,10 @@ The authenticated seed profile remains the source of truth for future oracle run
|
|
|
157
157
|
Agent-facing submissions resolve a provider first. ChatGPT submissions use **`preset`**; the canonical registry is `ORACLE_SUBMIT_PRESETS` in `extensions/oracle/lib/config.ts`. Grok submissions use **`mode: "heavy"`** today and reject ChatGPT-only presets. For ChatGPT, **`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 provider/model fields and using configured defaults unless the task or user explicitly asks for one. It also biases toward context-rich archives up to the provider 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.
|
|
158
158
|
|
|
159
159
|
1. resolve the provider and preset/mode (submit-time or config default) into an execution snapshot
|
|
160
|
-
2. resolve optional
|
|
160
|
+
2. resolve optional thread targeting:
|
|
161
|
+
- `followUpJobId` into a prior oracle job `chatUrl` and `conversationId`, or
|
|
162
|
+
- `chatGptConversationId` into a user/browser-created ChatGPT `https://chatgpt.com/c/<id>` URL
|
|
163
|
+
Omit both for the default fresh-thread behavior.
|
|
161
164
|
3. build the archive first into a temporary path
|
|
162
165
|
4. allocate a unique runtime:
|
|
163
166
|
- `runtimeId`
|
|
@@ -166,7 +169,7 @@ Agent-facing submissions resolve a provider first. ChatGPT submissions use **`pr
|
|
|
166
169
|
5. under the global admission lock, first promote any older queued jobs that can now run
|
|
167
170
|
6. if runtime capacity is still available:
|
|
168
171
|
- acquire the runtime lease
|
|
169
|
-
- acquire the conversation lease for follow-
|
|
172
|
+
- acquire the conversation lease for same-thread jobs, including follow-ups and explicit existing ChatGPT conversation ids
|
|
170
173
|
- create `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/...` job state as `submitted`
|
|
171
174
|
7. otherwise create `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/...` job state as `queued`
|
|
172
175
|
8. move the prepared archive into the job directory with a unique filename
|
|
@@ -184,7 +187,8 @@ Per job:
|
|
|
184
187
|
- the job’s `runtimeProfileDir`
|
|
185
188
|
- headless by default
|
|
186
189
|
3. open either:
|
|
187
|
-
- the saved `chatUrl` for follow-up jobs,
|
|
190
|
+
- the saved `chatUrl` for follow-up jobs,
|
|
191
|
+
- the normalized `chatGptConversationId` URL for explicit existing ChatGPT browser threads, or
|
|
188
192
|
- the configured provider URL
|
|
189
193
|
4. classify page state before touching the UI
|
|
190
194
|
5. fail fast on:
|
|
@@ -495,11 +499,13 @@ Same-thread continuity is persisted as data, not runtime browser state.
|
|
|
495
499
|
|
|
496
500
|
Approach:
|
|
497
501
|
|
|
498
|
-
- expose `/oracle-followup <job-id> <request>` as the user-facing way to continue
|
|
502
|
+
- expose `/oracle-followup <job-id> <request>` as the user-facing way to continue an oracle-created provider thread later
|
|
503
|
+
- allow `/oracle`/`oracle_submit` to opt into a browser-created ChatGPT thread only when the user explicitly supplies `chatGptConversationId` as a raw id or `https://chatgpt.com/c/...` URL
|
|
499
504
|
- store `chatUrl` only after the conversation URL stabilizes
|
|
500
505
|
- derive and persist `conversationId` from that URL when possible
|
|
501
506
|
- for a follow-up job, resolve `followUpJobId` to the prior `chatUrl`
|
|
502
|
-
-
|
|
507
|
+
- for an explicit existing ChatGPT thread, normalize `chatGptConversationId` to `https://chatgpt.com/c/<id>` without requiring prior oracle job state
|
|
508
|
+
- acquire a conversation lease before launching the same-thread job
|
|
503
509
|
- launch a fresh isolated browser using a fresh runtime clone of the auth seed
|
|
504
510
|
- open that URL
|
|
505
511
|
- continue there if authentication and page-state checks pass
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
resolveOracleConfigForProvider,
|
|
26
26
|
resolveOracleGrokMode,
|
|
27
27
|
resolveOracleSubmitPreset,
|
|
28
|
+
type OracleConfig,
|
|
28
29
|
type OracleProvider,
|
|
29
30
|
} from "./config.js";
|
|
30
31
|
import {
|
|
@@ -94,11 +95,21 @@ const ORACLE_SUBMIT_PARAMS = Type.Object({
|
|
|
94
95
|
}),
|
|
95
96
|
),
|
|
96
97
|
followUpJobId: Type.Optional(Type.String({ description: "Earlier oracle job id whose chat thread should be continued." })),
|
|
98
|
+
chatGptConversationId: Type.Optional(Type.String({
|
|
99
|
+
description: "Existing ChatGPT conversation id, or full https://chatgpt.com/c/... URL, to continue. Omit for default behavior: starting a fresh oracle thread. Do not combine with followUpJobId.",
|
|
100
|
+
minLength: 1,
|
|
101
|
+
pattern: "^.*\\S.*$",
|
|
102
|
+
})),
|
|
97
103
|
});
|
|
98
104
|
|
|
99
105
|
const ORACLE_PREFLIGHT_PARAMS = Type.Object({
|
|
100
106
|
provider: Type.Optional(Type.String({ description: `Provider readiness to check. Omit to use the configured default provider. Supported providers: ${ORACLE_PROVIDERS.join(", ")}.` })),
|
|
101
107
|
followUpJobId: Type.Optional(Type.String({ description: "Earlier oracle job id whose provider/thread readiness should be checked." })),
|
|
108
|
+
chatGptConversationId: Type.Optional(Type.String({
|
|
109
|
+
description: "Existing ChatGPT conversation id, or full https://chatgpt.com/c/... URL, whose provider/thread readiness should be checked. Do not combine with followUpJobId.",
|
|
110
|
+
minLength: 1,
|
|
111
|
+
pattern: "^.*\\S.*$",
|
|
112
|
+
})),
|
|
102
113
|
});
|
|
103
114
|
|
|
104
115
|
const ORACLE_AUTH_PARAMS = Type.Object({
|
|
@@ -750,12 +761,59 @@ export function getQueueAdmissionFailure(args: {
|
|
|
750
761
|
return undefined;
|
|
751
762
|
}
|
|
752
763
|
|
|
753
|
-
|
|
764
|
+
type OracleConversationTarget = {
|
|
754
765
|
followUpToJobId?: string;
|
|
755
766
|
chatUrl?: string;
|
|
756
767
|
conversationId?: string;
|
|
757
768
|
provider?: "chatgpt" | "grok";
|
|
758
|
-
|
|
769
|
+
label?: string;
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
const CHATGPT_CONVERSATION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9-]{7,}$/;
|
|
773
|
+
const CHATGPT_CONVERSATION_URL_HOSTS = new Set(["chatgpt.com", "chat.openai.com"]);
|
|
774
|
+
|
|
775
|
+
function chatGptConversationOrigin(config: Pick<OracleConfig, "browser">): string {
|
|
776
|
+
try {
|
|
777
|
+
const parsed = new URL(config.browser.chatUrl);
|
|
778
|
+
if (CHATGPT_CONVERSATION_URL_HOSTS.has(parsed.hostname.toLowerCase())) return parsed.origin;
|
|
779
|
+
} catch {
|
|
780
|
+
// Fall through to the canonical ChatGPT origin.
|
|
781
|
+
}
|
|
782
|
+
return "https://chatgpt.com";
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
export function resolveChatGptConversationReference(
|
|
786
|
+
rawReference: string | undefined,
|
|
787
|
+
config: Pick<OracleConfig, "browser">,
|
|
788
|
+
): { chatUrl: string; conversationId: string } | undefined {
|
|
789
|
+
if (rawReference === undefined) return undefined;
|
|
790
|
+
const reference = rawReference.trim();
|
|
791
|
+
if (!reference) throw new Error("ChatGPT conversation id must be a non-empty string");
|
|
792
|
+
|
|
793
|
+
try {
|
|
794
|
+
const parsed = new URL(reference);
|
|
795
|
+
const host = parsed.hostname.toLowerCase();
|
|
796
|
+
const conversationId = parseConversationId(parsed.toString());
|
|
797
|
+
if (parsed.protocol !== "https:" || !CHATGPT_CONVERSATION_URL_HOSTS.has(host) || !conversationId) {
|
|
798
|
+
throw new Error();
|
|
799
|
+
}
|
|
800
|
+
return {
|
|
801
|
+
chatUrl: `${parsed.origin}/c/${conversationId}`,
|
|
802
|
+
conversationId,
|
|
803
|
+
};
|
|
804
|
+
} catch {
|
|
805
|
+
if (!CHATGPT_CONVERSATION_ID_PATTERN.test(reference)) {
|
|
806
|
+
throw new Error(`Invalid ChatGPT conversation id or URL: ${rawReference}`);
|
|
807
|
+
}
|
|
808
|
+
const origin = chatGptConversationOrigin(config);
|
|
809
|
+
return {
|
|
810
|
+
chatUrl: `${origin}/c/${reference}`,
|
|
811
|
+
conversationId: reference,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function resolveFollowUp(previousJobId: string | undefined, cwd: string): OracleConversationTarget {
|
|
759
817
|
if (!previousJobId) return {};
|
|
760
818
|
const previous = readJob(previousJobId);
|
|
761
819
|
if (!previous) {
|
|
@@ -770,14 +828,37 @@ function resolveFollowUp(previousJobId: string | undefined, cwd: string): {
|
|
|
770
828
|
if (!previous.chatUrl) {
|
|
771
829
|
throw new Error(`Follow-up oracle job ${previousJobId} has no persisted chat URL`);
|
|
772
830
|
}
|
|
831
|
+
const conversationId = previous.conversationId || parseConversationId(previous.chatUrl);
|
|
773
832
|
return {
|
|
774
833
|
followUpToJobId: previous.id,
|
|
775
834
|
chatUrl: previous.chatUrl,
|
|
776
|
-
conversationId
|
|
835
|
+
conversationId,
|
|
777
836
|
provider: previous.selection?.provider === "grok" ? "grok" : "chatgpt",
|
|
837
|
+
label: `follow-up job ${previous.id}`,
|
|
778
838
|
};
|
|
779
839
|
}
|
|
780
840
|
|
|
841
|
+
function resolveConversationTarget(args: {
|
|
842
|
+
followUpJobId?: string;
|
|
843
|
+
chatGptConversationId?: string;
|
|
844
|
+
cwd: string;
|
|
845
|
+
config: OracleConfig;
|
|
846
|
+
}): OracleConversationTarget {
|
|
847
|
+
if (args.followUpJobId !== undefined && args.chatGptConversationId !== undefined) {
|
|
848
|
+
throw new Error("Pass either followUpJobId or chatGptConversationId, not both");
|
|
849
|
+
}
|
|
850
|
+
if (args.chatGptConversationId !== undefined) {
|
|
851
|
+
const target = resolveChatGptConversationReference(args.chatGptConversationId, args.config);
|
|
852
|
+
if (!target) return {};
|
|
853
|
+
return {
|
|
854
|
+
...target,
|
|
855
|
+
provider: "chatgpt",
|
|
856
|
+
label: `ChatGPT conversation ${target.conversationId}`,
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
return resolveFollowUp(args.followUpJobId, args.cwd);
|
|
860
|
+
}
|
|
861
|
+
|
|
781
862
|
type OracleToolName = "oracle_auth" | "oracle_submit" | "oracle_read" | "oracle_cancel";
|
|
782
863
|
type OracleToolErrorSource = OracleToolName | "oracle_preflight";
|
|
783
864
|
type OracleQueueSnapshot = { queued: boolean; position?: number; depth?: number };
|
|
@@ -1036,6 +1117,23 @@ function buildOracleToolErrorDetails(toolName: OracleToolErrorSource, error: unk
|
|
|
1036
1117
|
};
|
|
1037
1118
|
}
|
|
1038
1119
|
|
|
1120
|
+
if ((toolName === "oracle_submit" || toolName === "oracle_preflight") && message === "Pass either followUpJobId or chatGptConversationId, not both") {
|
|
1121
|
+
return {
|
|
1122
|
+
code: "oracle_thread_target_conflict",
|
|
1123
|
+
message,
|
|
1124
|
+
suggestedNextStep: "Retry with exactly one same-thread target: followUpJobId for an oracle-created thread, or chatGptConversationId for an existing ChatGPT browser thread.",
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
if ((toolName === "oracle_submit" || toolName === "oracle_preflight") && (message === "ChatGPT conversation id must be a non-empty string" || message.startsWith("Invalid ChatGPT conversation id or URL: "))) {
|
|
1129
|
+
return {
|
|
1130
|
+
code: "invalid_chatgpt_conversation_id",
|
|
1131
|
+
message,
|
|
1132
|
+
rejectedValue: typeof params.chatGptConversationId === "string" ? params.chatGptConversationId : undefined,
|
|
1133
|
+
suggestedNextStep: "Retry with a ChatGPT conversation id like 6a28ab5c-e4d4-83e8-b8be-dd39f38a26d6 or a full https://chatgpt.com/c/... URL, or omit chatGptConversationId to start a fresh thread.",
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1039
1137
|
if (toolName === "oracle_submit" && message.startsWith("Follow-up oracle job not found: ")) {
|
|
1040
1138
|
return {
|
|
1041
1139
|
code: "follow_up_job_not_found",
|
|
@@ -1164,7 +1262,7 @@ function isProjectTrusted(ctx: ExtensionContext): boolean {
|
|
|
1164
1262
|
return (ctx as { isProjectTrusted?: () => boolean }).isProjectTrusted?.() ?? true;
|
|
1165
1263
|
}
|
|
1166
1264
|
|
|
1167
|
-
async function runOraclePreflight(ctx: ExtensionContext, params: { provider?: unknown; followUpJobId?: unknown } = {}): Promise<OraclePreflightDetails> {
|
|
1265
|
+
async function runOraclePreflight(ctx: ExtensionContext, params: { provider?: unknown; followUpJobId?: unknown; chatGptConversationId?: unknown } = {}): Promise<OraclePreflightDetails> {
|
|
1168
1266
|
const sessionFile = getSessionFile(ctx);
|
|
1169
1267
|
if (!hasPersistedSessionFile(sessionFile)) {
|
|
1170
1268
|
return {
|
|
@@ -1184,14 +1282,18 @@ async function runOraclePreflight(ctx: ExtensionContext, params: { provider?: un
|
|
|
1184
1282
|
let provider: OracleProvider | undefined;
|
|
1185
1283
|
try {
|
|
1186
1284
|
const followUpJobId = params.followUpJobId;
|
|
1285
|
+
const chatGptConversationId = params.chatGptConversationId;
|
|
1187
1286
|
if (followUpJobId !== undefined && typeof followUpJobId !== "string") {
|
|
1188
1287
|
throw new Error("oracle_preflight followUpJobId must be a string");
|
|
1189
1288
|
}
|
|
1289
|
+
if (chatGptConversationId !== undefined && typeof chatGptConversationId !== "string") {
|
|
1290
|
+
throw new Error("oracle_preflight chatGptConversationId must be a string");
|
|
1291
|
+
}
|
|
1190
1292
|
const baseConfig = loadOracleConfig(ctx.cwd, { projectConfigTrusted: isProjectTrusted(ctx) });
|
|
1191
|
-
const
|
|
1192
|
-
provider = normalizeOracleProvider(params.provider,
|
|
1193
|
-
if (
|
|
1194
|
-
throw new Error(
|
|
1293
|
+
const target = resolveConversationTarget({ followUpJobId, chatGptConversationId, cwd: ctx.cwd, config: baseConfig });
|
|
1294
|
+
provider = normalizeOracleProvider(params.provider, target.provider ?? baseConfig.defaults.provider, "oracle_preflight");
|
|
1295
|
+
if (target.provider && provider !== target.provider) {
|
|
1296
|
+
throw new Error(`${target.label ?? "Oracle conversation"} requires provider ${target.provider}; cannot check it with ${provider}.`);
|
|
1195
1297
|
}
|
|
1196
1298
|
config = resolveOracleConfigForProvider(baseConfig, provider);
|
|
1197
1299
|
} catch (error) {
|
|
@@ -1201,14 +1303,14 @@ async function runOraclePreflight(ctx: ExtensionContext, params: { provider?: un
|
|
|
1201
1303
|
session: { persisted: true, sessionFile },
|
|
1202
1304
|
config: { ready: false },
|
|
1203
1305
|
auth: { ready: false },
|
|
1204
|
-
error: buildOracleToolErrorDetails("oracle_preflight", error, {}),
|
|
1306
|
+
error: buildOracleToolErrorDetails("oracle_preflight", error, asRecord(params) ?? {}),
|
|
1205
1307
|
};
|
|
1206
1308
|
}
|
|
1207
1309
|
|
|
1208
1310
|
try {
|
|
1209
1311
|
await assertOracleSubmitPrerequisites(config);
|
|
1210
1312
|
} catch (error) {
|
|
1211
|
-
const errorDetails = buildOracleToolErrorDetails("oracle_preflight", error, {});
|
|
1313
|
+
const errorDetails = buildOracleToolErrorDetails("oracle_preflight", error, asRecord(params) ?? {});
|
|
1212
1314
|
return {
|
|
1213
1315
|
ready: false,
|
|
1214
1316
|
provider,
|
|
@@ -1251,7 +1353,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1251
1353
|
description: "Check whether oracle is ready in this session before spending time gathering context or preparing a submission.",
|
|
1252
1354
|
promptSnippet: "Check oracle readiness before expensive /oracle preparation.",
|
|
1253
1355
|
promptGuidelines: [
|
|
1254
|
-
"Call oracle_preflight before doing expensive /oracle preparation. Pass provider='grok' when the user explicitly asks for Grok,
|
|
1356
|
+
"Call oracle_preflight before doing expensive /oracle preparation. Pass provider='grok' when the user explicitly asks for Grok, followUpJobId for same-thread follow-ups from oracle-created jobs, or chatGptConversationId when the user explicitly provides an existing ChatGPT browser conversation id/URL. If ready is false, stop immediately and report the suggested next step instead of reading files or crafting archive inputs.",
|
|
1255
1357
|
],
|
|
1256
1358
|
parameters: ORACLE_PREFLIGHT_PARAMS,
|
|
1257
1359
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
@@ -1299,10 +1401,12 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1299
1401
|
label: "Oracle Submit",
|
|
1300
1402
|
description:
|
|
1301
1403
|
"Dispatch a background ChatGPT or Grok web oracle job after gathering context. Always pass a prompt and exact project-relative archive inputs. " +
|
|
1302
|
-
"Optional provider: set `provider` to `grok` when the user asks for Grok; Grok currently supports only Heavy. Optional ChatGPT model: set parameter `preset`, or omit it for configured defaults; canonical preset ids are listed in the README and ORACLE_SUBMIT_PRESETS registry, and matching labels are normalized at submit time."
|
|
1404
|
+
"Optional provider: set `provider` to `grok` when the user asks for Grok; Grok currently supports only Heavy. Optional ChatGPT model: set parameter `preset`, or omit it for configured defaults; canonical preset ids are listed in the README and ORACLE_SUBMIT_PRESETS registry, and matching labels are normalized at submit time. " +
|
|
1405
|
+
"Optional thread target: pass `chatGptConversationId` only when the user explicitly provides an existing ChatGPT browser conversation id/URL to continue; omit it for the default fresh oracle thread.",
|
|
1303
1406
|
promptSnippet: "Dispatch a background ChatGPT or Grok web oracle job after gathering repo context.",
|
|
1304
1407
|
promptGuidelines: [
|
|
1305
1408
|
"Gather context before calling oracle_submit.",
|
|
1409
|
+
"If the user explicitly provides an existing ChatGPT browser conversation id or https://chatgpt.com/c/... URL, pass it as chatGptConversationId and force provider='chatgpt'; otherwise omit chatGptConversationId so oracle_submit starts a fresh thread by default.",
|
|
1306
1410
|
"If the immediately preceding oracle run failed because ChatGPT or Grok login is required or the worker explicitly said to rerun /oracle-auth, call oracle_auth once before retrying the submission; pass provider='grok' for Grok retries. Do not loop auth refreshes.",
|
|
1307
1411
|
"Prefer context-rich archives up to the provider ceiling because more relevant surrounding context is usually better than less: 250 MB for ChatGPT and 200 MiB for Grok.",
|
|
1308
1412
|
"By default, archive the whole repo by passing '.' for broad or unclear requests; 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.",
|
|
@@ -1328,10 +1432,15 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1328
1432
|
const originSessionFile = requirePersistedSessionFile(getSessionFile(ctx), "submit oracle jobs");
|
|
1329
1433
|
const projectId = getProjectId(projectCwd);
|
|
1330
1434
|
const sessionId = getSessionId(originSessionFile, projectId);
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1435
|
+
const target = resolveConversationTarget({
|
|
1436
|
+
followUpJobId: params.followUpJobId,
|
|
1437
|
+
chatGptConversationId: params.chatGptConversationId,
|
|
1438
|
+
cwd: projectCwd,
|
|
1439
|
+
config: baseConfig,
|
|
1440
|
+
});
|
|
1441
|
+
const provider = normalizeOracleProvider(params.provider, target.provider ?? baseConfig.defaults.provider, "oracle_submit");
|
|
1442
|
+
if (target.provider && provider !== target.provider) {
|
|
1443
|
+
throw new Error(`${target.label ?? "Oracle conversation"} requires provider ${target.provider}; cannot continue it with ${provider}.`);
|
|
1335
1444
|
}
|
|
1336
1445
|
if (provider === "grok" && typeof params.preset === "string") {
|
|
1337
1446
|
throw new Error("oracle_submit preset is only valid for ChatGPT. For Grok, use provider='grok' and mode='heavy'.");
|
|
@@ -1340,7 +1449,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1340
1449
|
? resolveOracleGrokMode(normalizeGrokMode(params.mode, baseConfig.defaults.grokMode))
|
|
1341
1450
|
: resolveOracleSubmitPreset(typeof params.preset === "string" ? coerceOracleSubmitPresetId(params.preset) : baseConfig.defaults.preset);
|
|
1342
1451
|
const config = resolveOracleConfigForProvider(baseConfig, provider);
|
|
1343
|
-
const targetChatUrl =
|
|
1452
|
+
const targetChatUrl = target.chatUrl;
|
|
1344
1453
|
// Validate caller-specified archive paths before surfacing unrelated local setup failures such as a missing auth seed profile.
|
|
1345
1454
|
resolveArchiveInputs(projectCwd, params.files);
|
|
1346
1455
|
await assertOracleSubmitPrerequisites(config);
|
|
@@ -1405,7 +1514,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1405
1514
|
prompt: params.prompt,
|
|
1406
1515
|
files: params.files,
|
|
1407
1516
|
selection,
|
|
1408
|
-
followUpToJobId:
|
|
1517
|
+
followUpToJobId: target.followUpToJobId,
|
|
1409
1518
|
chatUrl: targetChatUrl,
|
|
1410
1519
|
requestSource: "tool",
|
|
1411
1520
|
},
|
|
@@ -1425,18 +1534,18 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1425
1534
|
}
|
|
1426
1535
|
|
|
1427
1536
|
runtimeLeaseAcquired = true;
|
|
1428
|
-
if (
|
|
1537
|
+
if (target.conversationId) {
|
|
1429
1538
|
const conversationAttempt = await tryAcquireConversationLease({
|
|
1430
1539
|
jobId,
|
|
1431
|
-
conversationId:
|
|
1540
|
+
conversationId: target.conversationId,
|
|
1432
1541
|
projectId,
|
|
1433
1542
|
sessionId,
|
|
1434
1543
|
createdAt: admittedAt,
|
|
1435
1544
|
});
|
|
1436
1545
|
if (!conversationAttempt.acquired) {
|
|
1437
1546
|
throw new Error(
|
|
1438
|
-
`Oracle conversation ${
|
|
1439
|
-
"Concurrent
|
|
1547
|
+
`Oracle conversation ${target.conversationId} is already in use by job ${conversationAttempt.blocker?.jobId ?? "unknown"}. ` +
|
|
1548
|
+
"Concurrent jobs targeting the same ChatGPT thread are not allowed.",
|
|
1440
1549
|
);
|
|
1441
1550
|
}
|
|
1442
1551
|
conversationLeaseAcquired = true;
|
|
@@ -1448,7 +1557,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1448
1557
|
prompt: params.prompt,
|
|
1449
1558
|
files: params.files,
|
|
1450
1559
|
selection,
|
|
1451
|
-
followUpToJobId:
|
|
1560
|
+
followUpToJobId: target.followUpToJobId,
|
|
1452
1561
|
chatUrl: targetChatUrl,
|
|
1453
1562
|
requestSource: "tool",
|
|
1454
1563
|
},
|
|
@@ -1563,7 +1672,7 @@ export function registerOracleTools(pi: ExtensionAPI, workerPath: string, authWo
|
|
|
1563
1672
|
runtimeId: runtimeLeaseAcquired ? runtime.runtimeId : undefined,
|
|
1564
1673
|
runtimeProfileDir: runtimeLeaseAcquired ? runtime.runtimeProfileDir : undefined,
|
|
1565
1674
|
runtimeSessionName: workerSpawned ? runtime.runtimeSessionName : undefined,
|
|
1566
|
-
conversationId: conversationLeaseAcquired ?
|
|
1675
|
+
conversationId: conversationLeaseAcquired ? target.conversationId : undefined,
|
|
1567
1676
|
}).catch(() => ({ attempted: [], warnings: [] }));
|
|
1568
1677
|
if (job && cleanupReport.warnings.length > 0) {
|
|
1569
1678
|
await appendCleanupWarnings(job.id, cleanupReport.warnings).catch(() => undefined);
|
package/package.json
CHANGED
package/prompts/oracle.md
CHANGED
|
@@ -13,7 +13,7 @@ Hard requirements:
|
|
|
13
13
|
- After a successful or queued `oracle_submit`, your final answer must be only a terse dispatch summary with the job id and response path. Do not ask questions, offer to watch/poll/read, list next steps, or continue working.
|
|
14
14
|
|
|
15
15
|
Required workflow:
|
|
16
|
-
1. Call `oracle_preflight` immediately. If the user says to use Grok, pass `provider: "grok"` to `oracle_preflight`. If the user says to use ChatGPT, pass `provider: "chatgpt"`. If the user says to use ChatGPT Instant, pass `provider: "chatgpt"` and later call `oracle_submit` with `preset: "instant"`.
|
|
16
|
+
1. Call `oracle_preflight` immediately. If the user says to use Grok, pass `provider: "grok"` to `oracle_preflight`. If the user says to use ChatGPT, pass `provider: "chatgpt"`. If the user says to use ChatGPT Instant, pass `provider: "chatgpt"` and later call `oracle_submit` with `preset: "instant"`. If the user explicitly provides an existing ChatGPT conversation id or `https://chatgpt.com/c/...` URL, pass it as `chatGptConversationId` to `oracle_preflight` and force `provider: "chatgpt"`. Omit `chatGptConversationId` unless the user explicitly asks to continue an existing browser-created ChatGPT thread.
|
|
17
17
|
2. If `oracle_preflight` reports `ready: false`, stop before any expensive prep. Do not read files, search the codebase, prepare archive inputs, or call `oracle_auth` automatically. Report the blocking issue plus the suggested next step.
|
|
18
18
|
3. Understand the request and decide whether it is explicitly narrow or genuinely broad.
|
|
19
19
|
4. Gather enough repo context to choose archive inputs and write a strong oracle prompt. Bias toward context-rich submissions when they fit within the provider archive ceiling: 250 MB for ChatGPT, 200 MiB for Grok.
|
|
@@ -21,12 +21,13 @@ Required workflow:
|
|
|
21
21
|
6. If the request is broad, architectural, release-oriented, or otherwise repo-wide, gather broader context and usually archive `.`.
|
|
22
22
|
7. Choose archive inputs for the oracle job.
|
|
23
23
|
8. Craft a concise but complete oracle prompt for the selected web provider.
|
|
24
|
-
9. Call `oracle_submit` with the prompt and exact archive inputs. Do not ask for confirmation before this submit step unless `oracle_preflight` or `oracle_submit` returns a blocker that requires user action.
|
|
24
|
+
9. Call `oracle_submit` with the prompt and exact archive inputs. Include `chatGptConversationId` only when the user explicitly provided an existing ChatGPT conversation id/URL to continue; otherwise omit it so the default remains a fresh oracle thread. Do not ask for confirmation before this submit step unless `oracle_preflight` or `oracle_submit` returns a blocker that requires user action.
|
|
25
25
|
10. Stop immediately after dispatching the oracle job. “Stop” means no follow-up questions, no offers to poll/watch/read, and no extra next-step list.
|
|
26
26
|
|
|
27
27
|
Oracle provider/model (`oracle_submit`):
|
|
28
28
|
- If the user says to use Grok (for example “Use the oracle to Grok about ...”), pass **`provider: "grok"`**. Grok currently supports only **`mode: "heavy"`**; omit `mode` unless the user explicitly says Heavy.
|
|
29
29
|
- If the user says ChatGPT, pass **`provider: "chatgpt"`**. Never route a ChatGPT request to Grok.
|
|
30
|
+
- If the user provides an existing ChatGPT conversation id such as `6a28ab5c-e4d4-83e8-b8be-dd39f38a26d6` or a `https://chatgpt.com/c/...` URL, pass it as **`chatGptConversationId`** to both `oracle_preflight` and `oracle_submit`, and pass **`provider: "chatgpt"`**. This is only for explicit existing browser-created ChatGPT threads; omit it for normal `/oracle` jobs.
|
|
30
31
|
- Otherwise omit **`provider`** to use the configured default provider, or pass **`provider: "chatgpt"`** only when needed for clarity.
|
|
31
32
|
- To choose a specific ChatGPT model, pass **`preset`** with one of the allowed ids from the canonical preset registry.
|
|
32
33
|
- Matching human-readable preset labels and common hyphen/space variants are also accepted and normalized automatically, but prefer canonical ids when readily available.
|