@oh-my-pi/pi-coding-agent 17.0.3 → 17.0.5
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 +88 -35
- package/dist/cli.js +3540 -3521
- package/dist/types/advisor/advise-tool.d.ts +3 -0
- package/dist/types/advisor/runtime.d.ts +7 -1
- package/dist/types/async/job-manager.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +7 -0
- package/dist/types/config/model-resolver.d.ts +8 -0
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/models-config.d.ts +6 -0
- package/dist/types/dap/client.d.ts +10 -0
- package/dist/types/dap/types.d.ts +6 -5
- package/dist/types/extensibility/extensions/runner.d.ts +4 -2
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +16 -0
- package/dist/types/launch/spawn-options.d.ts +10 -0
- package/dist/types/launch/spawn-options.test.d.ts +1 -0
- package/dist/types/mnemopi/state.d.ts +32 -9
- package/dist/types/modes/components/status-line/component.d.ts +2 -3
- package/dist/types/modes/components/status-line/types.d.ts +3 -1
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/components/transcript-container.d.ts +4 -3
- package/dist/types/modes/components/tree-selector.d.ts +6 -2
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/print-mode.d.ts +4 -0
- package/dist/types/modes/rpc/rpc-input.d.ts +5 -0
- package/dist/types/modes/rpc/rpc-mode.d.ts +1 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +19 -1
- package/dist/types/session/messages.d.ts +6 -0
- package/dist/types/task/types.d.ts +6 -6
- package/dist/types/tools/browser/aria/aria-snapshot.d.ts +17 -7
- package/dist/types/tools/browser/cmux/socket-client.d.ts +2 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -0
- package/dist/types/tools/gh.d.ts +5 -3
- package/dist/types/tools/hub/index.d.ts +1 -1
- package/dist/types/tools/hub/jobs.d.ts +1 -0
- package/dist/types/tools/hub/types.d.ts +2 -0
- package/dist/types/tools/tool-timeouts.d.ts +1 -1
- package/dist/types/utils/git.d.ts +33 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +150 -0
- package/src/advisor/advise-tool.ts +4 -0
- package/src/advisor/runtime.ts +38 -14
- package/src/async/job-manager.ts +3 -0
- package/src/cli/bench-cli.ts +8 -2
- package/src/cli/dry-balance-cli.ts +1 -0
- package/src/config/model-registry.ts +89 -8
- package/src/config/model-resolver.ts +78 -14
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings.ts +3 -1
- package/src/dap/client.ts +168 -1
- package/src/dap/config.ts +51 -1
- package/src/dap/session.ts +575 -234
- package/src/dap/types.ts +6 -5
- package/src/discovery/agents.ts +2 -2
- package/src/extensibility/extensions/runner.ts +6 -4
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +26 -0
- package/src/launch/broker.ts +34 -31
- package/src/launch/client.ts +7 -1
- package/src/launch/spawn-options.test.ts +31 -0
- package/src/launch/spawn-options.ts +17 -0
- package/src/lsp/types.ts +5 -1
- package/src/main.ts +17 -4
- package/src/mcp/transports/stdio.test.ts +9 -1
- package/src/mnemopi/backend.ts +1 -1
- package/src/mnemopi/embed-worker.ts +8 -7
- package/src/mnemopi/state.ts +45 -18
- package/src/modes/components/ask-dialog.ts +137 -73
- package/src/modes/components/status-line/component.ts +2 -2
- package/src/modes/components/status-line/segments.ts +20 -3
- package/src/modes/components/status-line/types.ts +3 -1
- package/src/modes/components/tool-execution.ts +5 -0
- package/src/modes/components/transcript-container.ts +18 -111
- package/src/modes/components/tree-selector.ts +10 -4
- package/src/modes/controllers/event-controller.ts +1 -2
- package/src/modes/controllers/input-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +29 -8
- package/src/modes/interactive-mode.ts +40 -8
- package/src/modes/noninteractive-dispose.test.ts +12 -1
- package/src/modes/print-mode.ts +21 -9
- package/src/modes/rpc/rpc-input.ts +38 -0
- package/src/modes/rpc/rpc-mode.ts +7 -2
- package/src/modes/setup-wizard/scenes/sign-in.ts +3 -1
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +3 -2
- package/src/prompts/tools/browser.md +3 -2
- package/src/prompts/tools/debug.md +2 -7
- package/src/prompts/tools/github.md +6 -1
- package/src/prompts/tools/hub.md +4 -2
- package/src/prompts/tools/task-async-contract.md +1 -0
- package/src/prompts/tools/task.md +9 -2
- package/src/sdk.ts +38 -6
- package/src/session/agent-session.ts +395 -162
- package/src/session/messages.test.ts +91 -0
- package/src/session/messages.ts +248 -110
- package/src/session/session-loader.ts +31 -3
- package/src/slash-commands/builtin-registry.ts +1 -0
- package/src/stt/recorder.ts +68 -55
- package/src/task/executor.ts +59 -33
- package/src/task/index.ts +46 -9
- package/src/task/types.ts +11 -8
- package/src/task/worktree.ts +10 -0
- package/src/tools/browser/aria/aria-snapshot.ts +36 -8
- package/src/tools/browser/cmux/cmux-tab.ts +2 -1
- package/src/tools/browser/cmux/socket-client.ts +139 -3
- package/src/tools/browser/run-cancellation.ts +4 -0
- package/src/tools/browser/tab-protocol.ts +2 -0
- package/src/tools/browser/tab-supervisor.ts +21 -11
- package/src/tools/browser/tab-worker.ts +199 -33
- package/src/tools/debug.ts +3 -0
- package/src/tools/gh.ts +40 -2
- package/src/tools/hub/index.ts +4 -1
- package/src/tools/hub/jobs.ts +42 -1
- package/src/tools/hub/types.ts +2 -0
- package/src/tools/tool-timeouts.ts +1 -1
- package/src/utils/git.ts +237 -0
- package/src/web/search/index.ts +9 -5
- package/src/web/search/providers/codex.ts +195 -99
|
@@ -1,28 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenAI Codex Web Search Provider
|
|
3
3
|
*
|
|
4
|
-
* Uses Codex
|
|
5
|
-
*
|
|
6
|
-
* broker is the sole refresh authority — this module never opens a sibling
|
|
7
|
-
* SQLite store, never POSTs the broker sentinel to an OpenAI token endpoint.
|
|
4
|
+
* Uses the configured Codex Responses transport for proxy/API-key setups and
|
|
5
|
+
* the official ChatGPT backend for OAuth logins.
|
|
8
6
|
*/
|
|
9
7
|
import * as os from "node:os";
|
|
10
|
-
import {
|
|
11
|
-
|
|
8
|
+
import {
|
|
9
|
+
type AuthStorage,
|
|
10
|
+
type FetchImpl,
|
|
11
|
+
type Model,
|
|
12
|
+
type OAuthAccess,
|
|
13
|
+
withAuth,
|
|
14
|
+
withOAuthAccess,
|
|
15
|
+
} from "@oh-my-pi/pi-ai";
|
|
12
16
|
import { applyCodexResponsesLiteShape } from "@oh-my-pi/pi-ai/providers/openai-codex/request-transformer";
|
|
13
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
createOpenAICodexCompatibilityMetadata,
|
|
19
|
+
resolveCodexResponsesUrl,
|
|
20
|
+
} from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
|
|
14
21
|
import { getBundledModels } from "@oh-my-pi/pi-catalog/models";
|
|
15
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
CODEX_BASE_URL,
|
|
24
|
+
CODEX_CLIENT_VERSION,
|
|
25
|
+
getCodexAccountId,
|
|
26
|
+
OPENAI_HEADER_VALUES,
|
|
27
|
+
OPENAI_HEADERS,
|
|
28
|
+
} from "@oh-my-pi/pi-catalog/wire/codex";
|
|
16
29
|
import { $env, readSseJson } from "@oh-my-pi/pi-utils";
|
|
17
30
|
import packageJson from "../../../../package.json" with { type: "json" };
|
|
31
|
+
import type { ModelRegistry } from "../../../config/model-registry";
|
|
18
32
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
19
33
|
import { SearchProviderError } from "../../../web/search/types";
|
|
20
34
|
import type { SearchParams } from "./base";
|
|
21
35
|
import { SearchProvider } from "./base";
|
|
22
36
|
import { classifyProviderHttpError, withHardTimeout } from "./utils";
|
|
23
37
|
|
|
24
|
-
const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
|
|
25
|
-
const CODEX_RESPONSES_PATH = "/codex/responses";
|
|
26
38
|
const FALLBACK_MODEL = "gpt-5.5";
|
|
27
39
|
const DEFAULT_MODEL_PREFERENCES = [
|
|
28
40
|
"gpt-5.6-luna",
|
|
@@ -37,7 +49,6 @@ const DEFAULT_MODEL_PREFERENCES = [
|
|
|
37
49
|
"gpt-5.1-codex",
|
|
38
50
|
"gpt-5-codex-mini",
|
|
39
51
|
];
|
|
40
|
-
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
41
52
|
const DEFAULT_INSTRUCTIONS =
|
|
42
53
|
"You are a helpful assistant with web search capabilities. Search the web to answer the user's question accurately and cite your sources.";
|
|
43
54
|
|
|
@@ -48,6 +59,21 @@ interface CodexModelCandidate {
|
|
|
48
59
|
catalogModel?: CodexSearchModel;
|
|
49
60
|
}
|
|
50
61
|
|
|
62
|
+
interface CodexSearchTransport {
|
|
63
|
+
baseUrl: string;
|
|
64
|
+
url: string;
|
|
65
|
+
headers: Record<string, string>;
|
|
66
|
+
customEndpoint: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface CodexSearchResult {
|
|
70
|
+
answer: string;
|
|
71
|
+
sources: SearchSource[];
|
|
72
|
+
model: string;
|
|
73
|
+
requestId: string;
|
|
74
|
+
usage?: { inputTokens: number; outputTokens: number; totalTokens: number };
|
|
75
|
+
}
|
|
76
|
+
|
|
51
77
|
function getBundledCodexModels(): CodexSearchModel[] {
|
|
52
78
|
const models: CodexSearchModel[] = [];
|
|
53
79
|
for (const model of getBundledModels("openai-codex")) {
|
|
@@ -295,18 +321,6 @@ function extractTextSources(text: string): SearchSource[] {
|
|
|
295
321
|
return sources;
|
|
296
322
|
}
|
|
297
323
|
|
|
298
|
-
/**
|
|
299
|
-
* Extracts account ID from a Codex access token.
|
|
300
|
-
* @param accessToken - JWT access token
|
|
301
|
-
* @returns Account ID string, or null if not found
|
|
302
|
-
*/
|
|
303
|
-
function getAccountIdFromJwt(accessToken: string): string | null {
|
|
304
|
-
const payload = decodeJwt(accessToken);
|
|
305
|
-
const auth = payload?.[JWT_CLAIM_PATH] as { chatgpt_account_id?: string } | undefined;
|
|
306
|
-
const accountId = auth?.chatgpt_account_id;
|
|
307
|
-
return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
324
|
/**
|
|
311
325
|
* Resolve a Codex bearer + accountId through {@link AuthStorage} — the single
|
|
312
326
|
* refresh authority. Returns `null` when no OAuth credential is configured,
|
|
@@ -320,25 +334,55 @@ async function findCodexAuth(
|
|
|
320
334
|
): Promise<{ access: OAuthAccess; accountId: string } | null> {
|
|
321
335
|
const access = await authStorage.getOAuthAccess("openai-codex", sessionId, { signal });
|
|
322
336
|
if (!access) return null;
|
|
323
|
-
const accountId = access.accountId ??
|
|
337
|
+
const accountId = access.accountId ?? getCodexAccountId(access.accessToken);
|
|
324
338
|
if (!accountId) return null;
|
|
325
339
|
return { access, accountId };
|
|
326
340
|
}
|
|
327
341
|
|
|
342
|
+
function resolveCodexSearchTransport(modelRegistry: ModelRegistry | undefined, modelId: string): CodexSearchTransport {
|
|
343
|
+
const registryModel = modelRegistry?.find("openai-codex", modelId);
|
|
344
|
+
const bundledModel = getBundledCodexModels().find(model => model.id === modelId);
|
|
345
|
+
const providerBaseUrl = modelRegistry?.getProviderBaseUrl("openai-codex");
|
|
346
|
+
let baseUrl = providerBaseUrl ?? registryModel?.baseUrl ?? CODEX_BASE_URL;
|
|
347
|
+
if (registryModel?.baseUrl && registryModel.baseUrl !== (bundledModel?.baseUrl ?? CODEX_BASE_URL)) {
|
|
348
|
+
baseUrl = registryModel.baseUrl;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const url = resolveCodexResponsesUrl(baseUrl);
|
|
352
|
+
return {
|
|
353
|
+
baseUrl,
|
|
354
|
+
url,
|
|
355
|
+
headers: {
|
|
356
|
+
...(modelRegistry?.getProviderHeaders("openai-codex") ?? {}),
|
|
357
|
+
...(registryModel?.headers ?? {}),
|
|
358
|
+
},
|
|
359
|
+
customEndpoint: url !== resolveCodexResponsesUrl(CODEX_BASE_URL),
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
328
363
|
/**
|
|
329
364
|
* Builds HTTP headers for Codex API requests.
|
|
330
365
|
*/
|
|
331
|
-
function buildCodexHeaders(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}
|
|
366
|
+
function buildCodexHeaders(
|
|
367
|
+
accessToken: string,
|
|
368
|
+
accountId: string | undefined,
|
|
369
|
+
configuredHeaders: Record<string, string>,
|
|
370
|
+
): Headers {
|
|
371
|
+
const headers = new Headers(configuredHeaders);
|
|
372
|
+
headers.delete("x-api-key");
|
|
373
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
374
|
+
if (accountId) {
|
|
375
|
+
headers.set(OPENAI_HEADERS.ACCOUNT_ID, accountId);
|
|
376
|
+
} else {
|
|
377
|
+
headers.delete(OPENAI_HEADERS.ACCOUNT_ID);
|
|
378
|
+
}
|
|
379
|
+
headers.set(OPENAI_HEADERS.BETA, OPENAI_HEADER_VALUES.BETA_RESPONSES);
|
|
380
|
+
headers.set(OPENAI_HEADERS.ORIGINATOR, OPENAI_HEADER_VALUES.ORIGINATOR_CODEX);
|
|
381
|
+
headers.set(OPENAI_HEADERS.VERSION, CODEX_CLIENT_VERSION);
|
|
382
|
+
headers.set("User-Agent", `pi/${packageJson.version} (${os.platform()} ${os.release()}; ${os.arch()})`);
|
|
383
|
+
headers.set("Accept", "text/event-stream");
|
|
384
|
+
headers.set("Content-Type", "application/json");
|
|
385
|
+
return headers;
|
|
342
386
|
}
|
|
343
387
|
|
|
344
388
|
/**
|
|
@@ -348,7 +392,7 @@ function buildCodexHeaders(accessToken: string, accountId: string): Record<strin
|
|
|
348
392
|
* overrides from the default ChatGPT-account model-selection path.
|
|
349
393
|
*/
|
|
350
394
|
async function callCodexSearch(
|
|
351
|
-
auth: { accessToken: string; accountId
|
|
395
|
+
auth: { accessToken: string; accountId?: string },
|
|
352
396
|
query: string,
|
|
353
397
|
options: {
|
|
354
398
|
signal?: AbortSignal;
|
|
@@ -357,16 +401,10 @@ async function callCodexSearch(
|
|
|
357
401
|
model: CodexModelCandidate;
|
|
358
402
|
sessionId?: string;
|
|
359
403
|
fetch?: FetchImpl;
|
|
404
|
+
transport: CodexSearchTransport;
|
|
360
405
|
},
|
|
361
|
-
): Promise<{
|
|
362
|
-
|
|
363
|
-
sources: SearchSource[];
|
|
364
|
-
model: string;
|
|
365
|
-
requestId: string;
|
|
366
|
-
usage?: { inputTokens: number; outputTokens: number; totalTokens: number };
|
|
367
|
-
}> {
|
|
368
|
-
const url = `${CODEX_BASE_URL}${CODEX_RESPONSES_PATH}`;
|
|
369
|
-
const headers = buildCodexHeaders(auth.accessToken, auth.accountId);
|
|
406
|
+
): Promise<CodexSearchResult> {
|
|
407
|
+
const headers = buildCodexHeaders(auth.accessToken, auth.accountId, options.transport.headers);
|
|
370
408
|
|
|
371
409
|
const requestedModel = options.model.modelId;
|
|
372
410
|
const usesResponsesLite = options.model.catalogModel?.useResponsesLite === true;
|
|
@@ -397,15 +435,18 @@ async function callCodexSearch(
|
|
|
397
435
|
requestKind: "turn",
|
|
398
436
|
startNewTurn: true,
|
|
399
437
|
});
|
|
400
|
-
|
|
401
|
-
|
|
438
|
+
for (const name in metadata.headers) {
|
|
439
|
+
const value = metadata.headers[name];
|
|
440
|
+
if (value !== undefined) headers.set(name, value);
|
|
441
|
+
}
|
|
442
|
+
headers.set(OPENAI_HEADERS.RESPONSES_LITE, "true");
|
|
402
443
|
body.client_metadata = metadata.clientMetadata;
|
|
403
444
|
body.reasoning = { context: "all_turns" };
|
|
404
445
|
applyCodexResponsesLiteShape(body);
|
|
405
446
|
}
|
|
406
447
|
|
|
407
448
|
const fetchImpl = options.fetch ?? fetch;
|
|
408
|
-
const response = await fetchImpl(url, {
|
|
449
|
+
const response = await fetchImpl(options.transport.url, {
|
|
409
450
|
method: "POST",
|
|
410
451
|
headers,
|
|
411
452
|
body: JSON.stringify(body),
|
|
@@ -528,6 +569,39 @@ async function callCodexSearch(
|
|
|
528
569
|
};
|
|
529
570
|
}
|
|
530
571
|
|
|
572
|
+
async function runCodexSearchCandidates(options: {
|
|
573
|
+
auth: { accessToken: string; accountId?: string };
|
|
574
|
+
params: SearchParams;
|
|
575
|
+
modelCandidates: CodexModelCandidate[];
|
|
576
|
+
modelWasConfigured: boolean;
|
|
577
|
+
transport: CodexSearchTransport;
|
|
578
|
+
}): Promise<CodexSearchResult> {
|
|
579
|
+
let lastError: unknown;
|
|
580
|
+
for (let index = 0; index < options.modelCandidates.length; index += 1) {
|
|
581
|
+
const candidate = options.modelCandidates[index];
|
|
582
|
+
if (!candidate) continue;
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
return await callCodexSearch(options.auth, options.params.query, {
|
|
586
|
+
signal: options.params.signal,
|
|
587
|
+
systemPrompt: options.params.systemPrompt,
|
|
588
|
+
searchContextSize: "high",
|
|
589
|
+
model: candidate,
|
|
590
|
+
sessionId: options.params.sessionId,
|
|
591
|
+
fetch: options.params.fetch,
|
|
592
|
+
transport: options.transport,
|
|
593
|
+
});
|
|
594
|
+
} catch (error) {
|
|
595
|
+
lastError = error;
|
|
596
|
+
const isLastCandidate = index === options.modelCandidates.length - 1;
|
|
597
|
+
if (options.modelWasConfigured || isLastCandidate || !shouldRetryWithNextDefaultModel(error)) {
|
|
598
|
+
throw error;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
throw lastError ?? new Error("Codex search failed without returning a result");
|
|
603
|
+
}
|
|
604
|
+
|
|
531
605
|
/**
|
|
532
606
|
* Executes a web search using OpenAI Codex's built-in web search tool.
|
|
533
607
|
*
|
|
@@ -541,55 +615,81 @@ async function callCodexSearch(
|
|
|
541
615
|
* rejects.
|
|
542
616
|
*/
|
|
543
617
|
export async function searchCodex(params: SearchParams): Promise<SearchResponse> {
|
|
544
|
-
const seed = await findCodexAuth(params.authStorage, params.sessionId, params.signal);
|
|
545
|
-
if (!seed) {
|
|
546
|
-
throw new Error(
|
|
547
|
-
"No Codex OAuth credentials found. Login with 'omp /login openai-codex' to enable Codex web search.",
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
618
|
const configuredModel = getConfiguredModel();
|
|
552
619
|
const modelCandidates = configuredModel ? [configuredModel] : getDefaultModelCandidates();
|
|
620
|
+
const firstCandidate = modelCandidates[0];
|
|
621
|
+
if (!firstCandidate) {
|
|
622
|
+
throw new SearchProviderError("codex", "No Codex web search model is configured.");
|
|
623
|
+
}
|
|
624
|
+
const transport = resolveCodexSearchTransport(params.modelRegistry, firstCandidate.modelId);
|
|
625
|
+
|
|
626
|
+
let result: CodexSearchResult;
|
|
627
|
+
if (transport.customEndpoint) {
|
|
628
|
+
// ModelRegistry resolves command-backed provider keys before consulting
|
|
629
|
+
// its AuthStorage, so a lower-priority OAuth origin is irrelevant when
|
|
630
|
+
// that command source is configured.
|
|
631
|
+
const credentialSource = params.modelRegistry?.authStorage ?? params.authStorage;
|
|
632
|
+
const credentialOrigin = credentialSource.getCredentialOrigin("openai-codex");
|
|
633
|
+
const hasCommandBackedKey = params.modelRegistry?.hasCommandBackedApiKey("openai-codex") === true;
|
|
634
|
+
if (!hasCommandBackedKey && (credentialOrigin?.kind === "oauth" || credentialOrigin?.kind === "env")) {
|
|
635
|
+
throw new SearchProviderError(
|
|
636
|
+
"codex",
|
|
637
|
+
`Refusing to send official Codex OAuth credentials to custom endpoint ${transport.baseUrl}. Configure an API key for provider "openai-codex".`,
|
|
638
|
+
);
|
|
639
|
+
}
|
|
553
640
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
641
|
+
const resolverOptions = {
|
|
642
|
+
sessionId: params.sessionId,
|
|
643
|
+
baseUrl: transport.baseUrl,
|
|
644
|
+
modelId: firstCandidate.modelId,
|
|
645
|
+
};
|
|
646
|
+
const keyOrResolver = params.modelRegistry
|
|
647
|
+
? params.modelRegistry.resolver("openai-codex", resolverOptions)
|
|
648
|
+
: params.authStorage.resolver("openai-codex", resolverOptions);
|
|
649
|
+
result = await withAuth(
|
|
650
|
+
keyOrResolver,
|
|
651
|
+
accessToken =>
|
|
652
|
+
runCodexSearchCandidates({
|
|
653
|
+
auth: { accessToken },
|
|
654
|
+
params,
|
|
655
|
+
modelCandidates,
|
|
656
|
+
modelWasConfigured: configuredModel !== undefined,
|
|
657
|
+
transport,
|
|
658
|
+
}),
|
|
659
|
+
{
|
|
660
|
+
signal: params.signal,
|
|
661
|
+
missingKeyMessage: 'Codex credentials not found. Configure an API key for provider "openai-codex".',
|
|
662
|
+
},
|
|
663
|
+
);
|
|
664
|
+
} else {
|
|
665
|
+
const seed = await findCodexAuth(params.authStorage, params.sessionId, params.signal);
|
|
666
|
+
if (!seed) {
|
|
667
|
+
throw new Error(
|
|
668
|
+
"No Codex OAuth credentials found. Login with 'omp /login openai-codex' to enable Codex web search.",
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
result = await withOAuthAccess(
|
|
673
|
+
params.authStorage,
|
|
674
|
+
"openai-codex",
|
|
675
|
+
access => {
|
|
676
|
+
// A refreshed/rotated credential can carry a different bearer and
|
|
677
|
+
// ChatGPT account id than the seed used to select the first attempt.
|
|
678
|
+
const accountId = access.accountId ?? getCodexAccountId(access.accessToken);
|
|
679
|
+
if (!accountId) {
|
|
680
|
+
throw new Error("Codex OAuth credential is missing a ChatGPT account id");
|
|
587
681
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
682
|
+
return runCodexSearchCandidates({
|
|
683
|
+
auth: { accessToken: access.accessToken, accountId },
|
|
684
|
+
params,
|
|
685
|
+
modelCandidates,
|
|
686
|
+
modelWasConfigured: configuredModel !== undefined,
|
|
687
|
+
transport,
|
|
688
|
+
});
|
|
689
|
+
},
|
|
690
|
+
{ sessionId: params.sessionId, signal: params.signal, seed: seed.access },
|
|
691
|
+
);
|
|
692
|
+
}
|
|
593
693
|
|
|
594
694
|
let sources = result.sources;
|
|
595
695
|
|
|
@@ -615,14 +715,10 @@ export async function searchCodex(params: SearchParams): Promise<SearchResponse>
|
|
|
615
715
|
}
|
|
616
716
|
|
|
617
717
|
/**
|
|
618
|
-
* Checks
|
|
718
|
+
* Checks whether Codex web search has an API key or OAuth credential.
|
|
619
719
|
*/
|
|
620
720
|
export async function hasCodexSearch(authStorage: AuthStorage): Promise<boolean> {
|
|
621
|
-
|
|
622
|
-
// `hasOAuth(...)` is a synchronous in-memory check that returns true as soon
|
|
623
|
-
// as a Codex OAuth credential is loaded, without driving the refresh
|
|
624
|
-
// pipeline. The actual refresh happens lazily in `searchCodex`.
|
|
625
|
-
return authStorage.hasOAuth("openai-codex");
|
|
721
|
+
return authStorage.hasAuth("openai-codex");
|
|
626
722
|
}
|
|
627
723
|
|
|
628
724
|
/** Search provider for OpenAI Codex web search. */
|