@monotykamary/pi-better-openai 0.2.11 → 0.2.12

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.12
4
+
5
+ - Validate against Pi 1.0.2, where `openai` supports ChatGPT subscription OAuth and `openai-codex` is labeled legacy; preserve the native OpenAI provider and transport.
6
+ - Keep Ultrafast API-only: do not inject it for the new OpenAI subscription login, and retain Standard/Fast behavior.
7
+ - Label separately authenticated Codex usage on OpenAI models and warn that it is not verified against the active OpenAI account.
8
+ - Clarify that usage, banked resets, images, web search, and live voice still need `/login openai-codex`; never substitute direct-OpenAI OAuth credentials for Codex backend auth.
9
+ - Add auth-boundary regressions and an isolated real-host subscription/API-key compatibility probe.
10
+
3
11
  ## 0.2.11
4
12
 
5
13
  - Auto-redeem banked Codex resets ten minutes before expiry instead of one minute, with matching settings and redemption-time displays.
package/README.md CHANGED
@@ -20,13 +20,15 @@ pi install npm:@monotykamary/pi-better-openai
20
20
 
21
21
  ## Authentication
22
22
 
23
- Usage display, image generation, and live voice require pi's `openai-codex` OAuth credentials.
23
+ Pi 1.0.2 supports **Sign in with ChatGPT** under `/login openai`, using the native Responses API at `api.openai.com`. Use `openai/*` models for that subscription login or an OpenAI API key; the extension preserves pi's native authentication and transport. Standard/Fast tier overrides work with both login methods on supported models.
24
24
 
25
- 1. In pi, run `/login openai-codex`.
26
- 2. Verify subscription usage with `/openai-usage`, or open `/openai-settings` and check **Diagnostics**.
25
+ Pi labels `openai-codex` **legacy**, but this extension's usage polling, banked resets, image generation, web search, and live voice still call separate `chatgpt.com/backend-api` endpoints. They require the legacy Codex login, not the new direct-OpenAI OAuth grant. Credentials are never copied or substituted between providers.
26
+
27
+ 1. For those backend features, also run `/login openai-codex`. You can keep an `openai/*` model selected.
28
+ 2. Inspect the Codex account's usage with `/openai-usage`, or open `/openai-settings` and check **Diagnostics**. While using `openai/*`, the footer labels it **Codex Usage**: it is not verified against your active OpenAI login and could belong to a different account. For your OpenAI subscription, check [ChatGPT usage](https://chatgpt.com/settings/usage).
27
29
  3. The extension reads auth from pi's agent auth store, normally `~/.pi/agent/auth.json`. Do not copy, paste, or commit values from this file.
28
30
  4. If `PI_CODING_AGENT_DIR` is set, the auth store, global extension config, and global generated-image directory use that agent directory instead of `~/.pi/agent`. A leading `~/` is expanded to your home directory.
29
- 5. When [pi-multiprovider](https://github.com/monotykamary/pi-multiprovider) 0.8.0+ pools several `openai-codex` accounts, the session's active account (chosen with `/switch-account`) is resolved first for usage display, image generation, web search, and live voice; the usage widget refreshes on every switch and whenever a resumed session restores the account, so it never keeps billing the account the session used before. Without that extension, credential resolution is unchanged.
31
+ 5. When [pi-multiprovider](https://github.com/monotykamary/pi-multiprovider) 0.8.0+ pools several `openai-codex` accounts, the session's active **Codex** account (chosen with `/switch-account`) is resolved first for usage display, banked resets, image generation, web search, and live voice; the usage widget refreshes on Codex account switches and session restores. An independently selected `openai` account does not select the Codex backend account. Without that extension, credential resolution is unchanged.
30
32
 
31
33
  ## Features
32
34
 
@@ -145,7 +147,7 @@ Setting `image.enabled`, `websearch.enabled`, or `decisions.enabled` to `false`
145
147
 
146
148
  `/openai-tier fast` requests `service_tier: "priority"`. `/openai-tier standard` explicitly requests `"default"`, clearing an inherited Fast/Ultrafast request tier on OpenAI providers. Legacy disabled configurations without an explicit tier leave payloads untouched. `/fast` and the `--fast` flag never select Ultrafast.
147
149
 
148
- `/openai-tier ultrafast` explicitly opts into **6x Standard token prices** for `openai/gpt-6-astra` over the Responses API. Only the documented global (`https://api.openai.com/v1`) and US (`https://us.api.openai.com/v1`) endpoints are enabled. EU/regional endpoints, custom proxies, other models, and Codex subscription Ultrafast are not enabled without verified support. This does not promise account entitlement or available rate limits. Unsupported selections remain requested but inactive; the extension does not inject a lower-tier fallback or retry a rejected request.
150
+ `/openai-tier ultrafast` explicitly opts into **6x Standard token prices** for **API-key-authenticated** `openai/gpt-6-astra` over the Responses API. Only the documented global (`https://api.openai.com/v1`) and US (`https://us.api.openai.com/v1`) endpoints are enabled. EU/regional endpoints, custom proxies, other models, and ChatGPT subscription Ultrafast (both `openai` OAuth and legacy `openai-codex`) are not enabled without verified support. This does not promise account entitlement or available rate limits. Unsupported selections remain requested but inactive; the extension does not inject a lower-tier fallback or retry a rejected request.
149
151
 
150
152
  The footer shows `fast` or `ultrafast` only when supported by the current model. Diagnostics distinguish the requested tier and last injected payload from server-confirmed service or billing. **Pi's host cost estimates may omit the Ultrafast premium**; use OpenAI billing for actual charges. The host's native transport is preserved; WebSockets are recommended by OpenAI but HTTP is also supported. See [Ultrafast documentation](https://developers.openai.com/api/docs/guides/ultrafast-mode) and [pricing](https://developers.openai.com/api/docs/pricing?latest-pricing=ultrafast).
151
153
 
@@ -188,6 +190,8 @@ Example tool input:
188
190
 
189
191
  ## Codex model fallbacks
190
192
 
193
+ These fallbacks remain scoped to the legacy provider. The native `openai` provider and its catalog are not replaced or redirected to Codex.
194
+
191
195
  The extension adds `gpt-6.1-sol`, `gpt-6-astra`, `gpt-daybreak-blue-latest`, and `gpt-daybreak-red-latest` to the built-in `openai-codex` provider without requiring local `models.json` entries. Existing built-in models remain available, and metadata from pi's live catalog takes precedence when pi publishes an official entry with the same ID.
192
196
 
193
197
  The GPT-6.1 Sol fallback uses Codex's conservative 272K context default, 128K output limit, and published short/long-context pricing. It maps Pi's `minimal` level to `low` and disables `off`; the model does not accept `none` or `minimal` reasoning efforts. See [model documentation](https://developers.openai.com/api/docs/models/gpt-6.1-sol) and the [upstream Codex catalog change](https://github.com/openai/codex/commit/b1e72963c3b71a9265a551e54beff078384efed9).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monotykamary/pi-better-openai",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "description": "Improve OpenAI in pi with fast mode, usage stats, realtime voice, image generation, and footer polish.",
5
5
  "keywords": [
6
6
  "fast",
@@ -65,9 +65,9 @@
65
65
  "ws": "8.21.2"
66
66
  },
67
67
  "devDependencies": {
68
- "@earendil-works/pi-ai": "1.0.0",
69
- "@earendil-works/pi-coding-agent": "1.0.0",
70
- "@earendil-works/pi-tui": "1.0.0",
68
+ "@earendil-works/pi-ai": "1.0.2",
69
+ "@earendil-works/pi-coding-agent": "1.0.2",
70
+ "@earendil-works/pi-tui": "1.0.2",
71
71
  "@types/node": "~22.19.0",
72
72
  "@types/proxy-from-env": "1.0.4",
73
73
  "@types/ws": "8.18.1",
@@ -89,8 +89,8 @@
89
89
  "@oh-my-pi/pi-natives-win32-x64": "17.2.9"
90
90
  },
91
91
  "overrides": {
92
- "@earendil-works/pi-agent-core": "1.0.0",
93
- "@earendil-works/pi-ai": "1.0.0",
92
+ "@earendil-works/pi-agent-core": "1.0.2",
93
+ "@earendil-works/pi-ai": "1.0.2",
94
94
  "brace-expansion": "5.0.11",
95
95
  "nanoid": "3.3.18",
96
96
  "postcss": "8.5.25",
package/src/codex-auth.ts CHANGED
@@ -6,6 +6,9 @@ import { piAgentDir } from "./paths.ts";
6
6
 
7
7
  export const AUTH_FILE = join(piAgentDir(), "auth.json");
8
8
 
9
+ export const CODEX_AUTH_REQUIRED =
10
+ "Missing openai-codex OAuth credentials. Run /login openai-codex for this Codex backend feature. /login openai uses separate ChatGPT subscription credentials for api.openai.com.";
11
+
9
12
  export type CodexCredentials = {
10
13
  accessToken: string;
11
14
  accountId: string;
@@ -145,6 +148,8 @@ export async function getCodexCredentials(
145
148
  // Fall back to pi-owned credential resolution.
146
149
  }
147
150
  }
151
+ // Deliberately do not fall back to "openai": its direct ChatGPT OAuth grant
152
+ // targets api.openai.com, not the legacy chatgpt.com/backend-api endpoints.
148
153
  const registryRequest = ctx?.modelRegistry?.getApiKeyForProvider(CODEX_PROVIDER_ID);
149
154
  const registryToken = registryRequest
150
155
  ? await waitForSignal(
@@ -3,7 +3,7 @@ import type { ResolvedConfig, ServiceTier, SupportedModel } from "./config.ts";
3
3
  import { isRecord } from "./config.ts";
4
4
 
5
5
  export const ULTRAFAST_NOTICE =
6
- "Ultrafast uses 6x Standard token prices for API GPT-6 Astra (global/US only). Codex subscription access is not verified. Host cost estimates may exclude tier premiums.";
6
+ "Ultrafast uses 6x Standard token prices for API GPT-6 Astra (global/US only). ChatGPT subscription access (openai OAuth or legacy openai-codex) is not verified. Host cost estimates may exclude tier premiums.";
7
7
 
8
8
  export function currentModelKey(ctx: ExtensionContext): string {
9
9
  return ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "none";
@@ -22,7 +22,8 @@ export function supportsUltrafast(ctx: ExtensionContext): boolean {
22
22
  if (
23
23
  model?.provider !== "openai" ||
24
24
  model.id !== "gpt-6-astra" ||
25
- model.api !== "openai-responses"
25
+ model.api !== "openai-responses" ||
26
+ ctx.modelRegistry.isUsingOAuth(model)
26
27
  )
27
28
  return false;
28
29
  try {
package/src/image.ts CHANGED
@@ -7,6 +7,7 @@ import { Box, Container, Image, Text } from "@earendil-works/pi-tui";
7
7
  import sharp from "sharp";
8
8
  import { isRecord, normalizeImageModel, type ResolvedConfig } from "./config.ts";
9
9
  import {
10
+ CODEX_AUTH_REQUIRED,
10
11
  extractAccountIdFromJwt,
11
12
  getCodexCredentials,
12
13
  type CodexCredentialsWithSource,
@@ -129,7 +130,7 @@ async function getCredentials(
129
130
  ): Promise<CodexImageCredentials> {
130
131
  const credentials = await getCodexCredentials(ctx, signal);
131
132
  if (credentials) return credentials;
132
- throw new Error("Missing openai-codex OAuth credentials. Run /login openai-codex.");
133
+ throw new Error(CODEX_AUTH_REQUIRED);
133
134
  }
134
135
 
135
136
  function resolveModel(params: Pick<ToolParams, "model">, cfg: ResolvedConfig): string {
@@ -2,7 +2,7 @@ import { HttpsProxyAgent } from "https-proxy-agent";
2
2
  import { getProxyForUrl } from "proxy-from-env";
3
3
  import { fetch as undiciFetch, ProxyAgent } from "undici";
4
4
  import WebSocket, { type RawData } from "ws";
5
- import type { CodexCredentials } from "../codex-auth.ts";
5
+ import { CODEX_AUTH_REQUIRED, type CodexCredentials } from "../codex-auth.ts";
6
6
  import { generateCodexAttestation } from "./attestation.ts";
7
7
  import { loadLiveNative, type LiveNativeBindings, type LiveWebRtcPeerInstance } from "./native.ts";
8
8
  import {
@@ -186,7 +186,7 @@ export class CodexLiveTransport {
186
186
  async #signal(offer: string): Promise<LiveSignalingResult> {
187
187
  const credentials = await this.#options.getCredentials(this.#operationSignal);
188
188
  if (!credentials) {
189
- throw new Error("Missing openai-codex OAuth credentials. Run /login openai-codex.");
189
+ throw new Error(CODEX_AUTH_REQUIRED);
190
190
  }
191
191
  const attestation = await generateCodexAttestation(this.#native);
192
192
  const headers = buildLiveHeaders(
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { getCodexCredentials } from "./codex-auth.ts";
2
+ import { CODEX_AUTH_REQUIRED, getCodexCredentials } from "./codex-auth.ts";
3
3
  import { sanitizeDiagnosticError } from "./format.ts";
4
4
  import { reserveBankedResetRedemption } from "./reset-guard.ts";
5
5
  import {
@@ -91,7 +91,7 @@ export class ResetController {
91
91
  this.cache = { credits, updatedAt: Date.now() };
92
92
  this.error = undefined;
93
93
  } else {
94
- this.error = "OpenAI Codex credentials unavailable.";
94
+ this.error = CODEX_AUTH_REQUIRED;
95
95
  }
96
96
  } catch (error) {
97
97
  if (generation !== this.generation || signal.aborted) return;
@@ -130,7 +130,7 @@ export class ResetController {
130
130
  const signal = this.requestSignal(ctx);
131
131
  const credentials = await getCodexCredentials(ctx, signal);
132
132
  signal.throwIfAborted();
133
- if (!credentials) throw new Error("OpenAI Codex credentials unavailable.");
133
+ if (!credentials) throw new Error(CODEX_AUTH_REQUIRED);
134
134
  if (!reserveBankedResetRedemption(credentials.accountId, creditId))
135
135
  throw new Error(
136
136
  "A banked reset was already attempted recently; no additional credit was spent.",
@@ -14,6 +14,7 @@ import {
14
14
  usageSegments,
15
15
  } from "./usage.ts";
16
16
  import { currentModelKey } from "./fast-controller.ts";
17
+ import { CODEX_AUTH_REQUIRED } from "./codex-auth.ts";
17
18
 
18
19
  export function isOpenAISubscriptionModel(
19
20
  ctx: ExtensionContext,
@@ -77,13 +78,19 @@ export class UsageController {
77
78
  cfg = this.getConfig(ctx),
78
79
  isUsingOAuth?: boolean,
79
80
  ): UsageSegment[] | undefined {
80
- return this.usageSnapshot &&
81
- !this.usageError &&
82
- this.usageSnapshot.scope === usageScopeForModel(ctx.model?.id) &&
83
- cfg.usage.enabled &&
84
- isOpenAISubscriptionModel(ctx, cfg, isUsingOAuth)
85
- ? usageSegments(this.usageSnapshot, cfg.usage)
86
- : undefined;
81
+ if (
82
+ !this.usageSnapshot ||
83
+ this.usageError ||
84
+ this.usageSnapshot.scope !== usageScopeForModel(ctx.model?.id) ||
85
+ !cfg.usage.enabled ||
86
+ !isOpenAISubscriptionModel(ctx, cfg, isUsingOAuth)
87
+ )
88
+ return undefined;
89
+ const segments = usageSegments(this.usageSnapshot, cfg.usage);
90
+ // The new OpenAI subscription login and the Codex backend login are independent.
91
+ // Never present a separately authenticated Codex account as the active OpenAI quota.
92
+ if (ctx.model?.provider === "openai") segments.unshift({ text: "Codex ", severity: "muted" });
93
+ return segments;
87
94
  }
88
95
 
89
96
  statusLine(
@@ -108,7 +115,11 @@ export class UsageController {
108
115
  this.usageUpdatedAt && Date.now() - this.usageUpdatedAt > cfg.usage.refreshIntervalMs * 2
109
116
  ? ` · stale ${formatResetCountdown((Date.now() - this.usageUpdatedAt) / 1000)}`
110
117
  : "";
111
- return `${formatUsageSnapshot(this.usageSnapshot, cfg.usage)}${stale}`;
118
+ const source =
119
+ ctx.model?.provider === "openai"
120
+ ? "Codex account (not verified against the active OpenAI login; OpenAI usage: https://chatgpt.com/settings/usage). "
121
+ : "";
122
+ return `${source}${formatUsageSnapshot(this.usageSnapshot, cfg.usage)}${stale}`;
112
123
  }
113
124
 
114
125
  formatDebug(ctx: ExtensionContext): string {
@@ -119,6 +130,7 @@ export class UsageController {
119
130
  `Current model: ${currentModelKey(ctx)}`,
120
131
  `Current model eligible: ${isOpenAISubscriptionModel(ctx, cfg)}`,
121
132
  `Requires subscription model: ${cfg.usage.showOnlyOnSubscriptionModels}`,
133
+ "Credential provider: openai-codex (independent of /login openai)",
122
134
  `Auth: ${auth ? "found" : "missing"}`,
123
135
  `Account ID: ${maskIdentifier(auth?.accountId) ?? "none"}`,
124
136
  `Last fetch: ${this.usageLastFetchAt ? new Date(this.usageLastFetchAt).toLocaleTimeString() : "never"}`,
@@ -214,9 +226,7 @@ export class UsageController {
214
226
 
215
227
  this.usageSnapshot = data ? parseUsageSnapshot(data, resolvedModelId) : undefined;
216
228
  this.usageUpdatedAt = this.usageSnapshot ? Date.now() : undefined;
217
- this.usageError = data
218
- ? undefined
219
- : `Missing openai-codex OAuth credentials in ${AUTH_FILE}.`;
229
+ this.usageError = data ? undefined : `${CODEX_AUTH_REQUIRED} Auth store: ${AUTH_FILE}.`;
220
230
  this.updateFooter(ctx);
221
231
  if (options?.notify)
222
232
  ctx.ui.notify(this.formatStatus(ctx), this.usageSnapshot ? "info" : "warning");
package/src/websearch.ts CHANGED
@@ -6,7 +6,11 @@ import {
6
6
  type ResolvedConfig,
7
7
  type WebsearchResponseLength,
8
8
  } from "./config.ts";
9
- import { getCodexCredentials, type CodexCredentialsWithSource } from "./codex-auth.ts";
9
+ import {
10
+ CODEX_AUTH_REQUIRED,
11
+ getCodexCredentials,
12
+ type CodexCredentialsWithSource,
13
+ } from "./codex-auth.ts";
10
14
  import { maskIdentifier, sanitizeDiagnosticError } from "./format.ts";
11
15
  import { registerOptionalTool, type OptionalTool } from "./optional-tool.ts";
12
16
 
@@ -90,10 +94,7 @@ async function getCredentials(
90
94
  ): Promise<CodexCredentialsWithSource> {
91
95
  const credentials = await getCodexCredentials(ctx, signal);
92
96
  if (credentials) return credentials;
93
- throw new WebSearchError(
94
- "authentication_required",
95
- "Missing openai-codex OAuth credentials. Run /login openai-codex.",
96
- );
97
+ throw new WebSearchError("authentication_required", CODEX_AUTH_REQUIRED);
97
98
  }
98
99
 
99
100
  export function validateSearchQuery(query: string): string {