feat(codex): unify ChatGPT subscription onto pi-ai's openai-codex-responses wire#155
feat(codex): unify ChatGPT subscription onto pi-ai's openai-codex-responses wire#155
Conversation
There was a problem hiding this comment.
Findings
-
[Major] Hardcoded UI values in the new ChatGPT login badge violate the token-only UI constraint and will be hard to keep visually consistent across themes, evidence
apps/desktop/src/renderer/src/components/ChatgptLoginCard.tsx:153.
Suggested fix:<span className="inline-flex items-center gap-[var(--space-1)] px-[var(--space-1_5)] py-[var(--space-0_5)] rounded-full border border-[var(--color-accent)] text-[var(--color-accent)] bg-transparent text-[var(--font-size-badge)] font-medium leading-none"> <Sparkles className="w-[var(--icon-xs)] h-[var(--icon-xs)]" /> </span>
-
[Major] New helper intentionally swallows non-codex credential read failures and returns an empty key, which is a silent fallback (policy says errors must surface or throw with context), evidence
apps/desktop/src/main/resolve-api-key.ts:41and lock-in testapps/desktop/src/main/resolve-api-key.test.ts:78.
Suggested fix:try { return deps.getApiKeyForProvider(providerId); } catch (err) { throw new CodesignError( `Failed to read API key for provider "${providerId}"`, ERROR_CODES.PROVIDER_AUTH_MISSING, { cause: err }, ); }
Summary
- Review mode: initial
- 2 issues found (2 Major), both on added/modified lines.
docs/VISION.mdanddocs/PRINCIPLES.md: Not found in repo/docs in this checkout.
Testing
- Not run (automation)
open-codesign Bot
| return ( | ||
| <div className="rounded-[var(--radius-lg)] border border-[var(--color-border)] border-l-[var(--size-accent-stripe)] border-l-[var(--color-accent)] bg-[var(--color-accent-tint)] px-[var(--space-3)] py-[var(--space-2_5)] flex items-center gap-[var(--space-3)]"> | ||
| <div className="min-w-0 flex-1 flex items-center gap-2 flex-wrap"> | ||
| <span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full border border-[var(--color-accent)] text-[var(--color-accent)] bg-transparent text-[var(--font-size-badge)] font-medium leading-none"> |
There was a problem hiding this comment.
Hardcoded utility values (gap-1, px-1.5, py-0.5, plus w-2.5/h-2.5 nearby) bypass the token system. This conflicts with the token-only UI constraint and makes theme-scale consistency harder to maintain. Please replace these with existing UI token vars (or add missing tokens in packages/ui).
| } | ||
| try { | ||
| return deps.getApiKeyForProvider(providerId); | ||
| } catch { |
There was a problem hiding this comment.
This catch returns '' for any non-codex credential read failure, which is a silent fallback. The project constraint requires surfacing/throwing errors with context. Please rethrow a structured CodesignError with cause instead of swallowing here.
f7cb805 to
1e4e9a7
Compare
There was a problem hiding this comment.
Findings
-
[Major] Hardcoded UI values in the new ChatGPT login badge violate the token-only UI constraint and will drift from theme/system scales, evidence
apps/desktop/src/renderer/src/components/ChatgptLoginCard.tsx:153.
Suggested fix:<span className="inline-flex items-center gap-[var(--space-1)] px-[var(--space-1_5)] py-[var(--space-0_5)] rounded-full border border-[var(--color-accent)] text-[var(--color-accent)] bg-transparent text-[var(--font-size-badge)] font-medium leading-none"> <Sparkles className="w-[var(--icon-xs)] h-[var(--icon-xs)]" /> {t('settings.providers.chatgptLogin.loggedInBadge')} </span>
-
[Major]
resolveActiveApiKeysilently swallows non-codex credential read failures and returns an empty key, which violates the no-silent-fallback constraint and hides keychain/config errors, evidenceapps/desktop/src/main/resolve-api-key.ts:41.
Suggested fix:try { return deps.getApiKeyForProvider(providerId); } catch (err) { throw new CodesignError( `Failed to read API key for provider "${providerId}"`, ERROR_CODES.PROVIDER_AUTH_MISSING, { cause: err }, ); }
Summary
- Review mode: follow-up after new commits
- 2 issues found (2 Major), both on added/modified lines in this head.
docs/VISION.mdanddocs/PRINCIPLES.md: Not found in repo/docs.
Testing
- Not run (automation)
open-codesign Bot
| return ( | ||
| <div className="rounded-[var(--radius-lg)] border border-[var(--color-border)] border-l-[var(--size-accent-stripe)] border-l-[var(--color-accent)] bg-[var(--color-accent-tint)] px-[var(--space-3)] py-[var(--space-2_5)] flex items-center gap-[var(--space-3)]"> | ||
| <div className="min-w-0 flex-1 flex items-center gap-2 flex-wrap"> | ||
| <span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full border border-[var(--color-accent)] text-[var(--color-accent)] bg-transparent text-[var(--font-size-badge)] font-medium leading-none"> |
There was a problem hiding this comment.
Token-only UI constraint: this new badge uses hardcoded scale values (gap-1, px-1.5, py-0.5, w-2.5/h-2.5). Please switch these to token vars (or add missing tokens in packages/ui) to keep sizing/theme consistency.
| ); | ||
| } | ||
| } | ||
| try { |
There was a problem hiding this comment.
This catch returns `` for any non-codex key-read error, which is a silent fallback. Please throw a structured CodesignError with context/cause so keychain/config failures surface explicitly.
1e4e9a7 to
274bf90
Compare
|
Addressed both Major findings: 1. Hardcoded UI values ( 2. Silent fallback in Test updates: replaced the two "returns empty string" tests with three covering the new shape — wrap-with-cause, pass-through-existing-CodesignError, and non-Error rejection fallback. 1295 tests across 10 packages pass; |
There was a problem hiding this comment.
Findings
- [Major] Keyless providers regress to hard failure because
PROVIDER_KEY_MISSINGis no longer swallowed in the new keyless path.resolveActiveApiKeynow passes through existingCodesignErrors (includingPROVIDER_KEY_MISSINGfrom onboarding), butresolveApiKeyForActiveonly swallowsPROVIDER_AUTH_MISSING; this breaksrequiresApiKey: falseproviders on generate/apply/title despiteallowKeyless=true, evidenceapps/desktop/src/main/index.ts:187,apps/desktop/src/main/resolve-api-key.ts:49,apps/desktop/src/main/onboarding-ipc.ts:150.
Suggested fix:// apps/desktop/src/main/index.ts if ( allowKeyless && providerId !== CHATGPT_CODEX_PROVIDER_ID && err instanceof CodesignError && (err.code === ERROR_CODES.PROVIDER_AUTH_MISSING || err.code === ERROR_CODES.PROVIDER_KEY_MISSING) ) { return ''; }
Summary
- Review mode: follow-up after new commits
- 1 issue found (1 Major).
docs/VISION.mdanddocs/PRINCIPLES.md: Not found in repo/docs.- Residual risk: no regression test currently asserts keyless providers still proceed when secret lookup throws
PROVIDER_KEY_MISSING.
Testing
- Not run (automation)
open-codesign Bot
| allowKeyless && | ||
| providerId !== CHATGPT_CODEX_PROVIDER_ID && | ||
| err instanceof CodesignError && | ||
| err.code === 'PROVIDER_AUTH_MISSING' |
There was a problem hiding this comment.
Keyless fallback now only swallows PROVIDER_AUTH_MISSING, but resolveActiveApiKey passes through PROVIDER_KEY_MISSING from getApiKeyForProvider. For allowKeyless providers this turns missing secret into a hard failure. Please also swallow PROVIDER_KEY_MISSING here (non-codex only).
274bf90 to
63394b3
Compare
There was a problem hiding this comment.
Findings
- [Major] Keyless providers still fail hard on missing stored secret —
resolveActiveApiKeynow preservesCodesignError(PROVIDER_KEY_MISSING), but keyless fallback inresolveApiKeyForActiveonly swallowsPROVIDER_AUTH_MISSING; providers withrequiresApiKey: falsecan regress from keyless flow to blocking error, evidenceapps/desktop/src/main/index.ts:187,apps/desktop/src/main/resolve-api-key.ts:49,apps/desktop/src/main/onboarding-ipc.ts:148.
Suggested fix:if ( allowKeyless && providerId !== CHATGPT_CODEX_PROVIDER_ID && err instanceof CodesignError && (err.code === ERROR_CODES.PROVIDER_AUTH_MISSING || err.code === ERROR_CODES.PROVIDER_KEY_MISSING) ) { return ''; }
Summary
- Review mode: follow-up after new commits
- 1 issue found (1 Major).
docs/VISION.mdanddocs/PRINCIPLES.md: Not found in repo/docs.- Residual risk/testing gap: no integration test in this PR asserts that
requiresApiKey: falseproviders continue with empty bearer when secret lookup throwsPROVIDER_KEY_MISSING.
Testing
- Not run (automation)
open-codesign Bot
| allowKeyless && | ||
| providerId !== CHATGPT_CODEX_PROVIDER_ID && | ||
| err instanceof CodesignError && | ||
| err.code === 'PROVIDER_AUTH_MISSING' |
There was a problem hiding this comment.
Keyless fallback here still only handles PROVIDER_AUTH_MISSING. Since resolveActiveApiKey now passes through PROVIDER_KEY_MISSING, requiresApiKey: false providers can fail hard instead of proceeding with empty bearer. Please include PROVIDER_KEY_MISSING in the swallow condition for non-codex providers.
63394b3 to
6a8dd59
Compare
|
Fixed. Real regression — Widened the keyless-swallow to accept both missing-credential codes, and moved the wrapper into
14 tests in |
There was a problem hiding this comment.
Findings
- [Major] Silent fallback hides ChatGPT OAuth status failures — the status bootstrap catch path suppresses the error and renders a logged-out state, so auth/backend failures are not surfaced to users and violate the no-silent-fallback constraint, evidence
apps/desktop/src/renderer/src/components/ChatgptLoginCard.tsx:105.
Suggested fix:useEffect(() => { if (!window.codesign) return; void window.codesign.codexOAuth .status() .then((s) => { if (mountedRef.current) setStatus(s); }) .catch((err) => { if (mountedRef.current) setStatus(null); pushToast({ variant: 'error', title: t('settings.providers.chatgptLogin.statusFailedTitle'), description: err instanceof Error ? err.message : t('settings.providers.chatgptLogin.unknownError'), }); }); }, [pushToast, t]);
Summary
- Review mode: follow-up after new commits
- 1 issue found (1 Major).
docs/VISION.mdanddocs/PRINCIPLES.md: Not found in repo/docs.
Testing
- Not run (automation). Suggested: add a
ChatgptLoginCardunit test asserting a toast is emitted whencodexOAuth.status()rejects.
open-codesign Bot
| .then((s) => { | ||
| if (mountedRef.current) setStatus(s); | ||
| }) | ||
| .catch(() => { |
There was a problem hiding this comment.
This catch path currently swallows OAuth/status failures and silently downgrades to not-logged-in. Please surface the error (toast or inline state) so failures are visible instead of falling back quietly.
6a8dd59 to
63920cb
Compare
|
Fixed. The Extracted the fetch into a testable Added 5 unit tests in
1306 tests total pass; |
There was a problem hiding this comment.
Findings
- [Major] Boot migration failure is silently downgraded to a log warning — this violates the “no silent fallbacks” constraint and can leave stale codex provider config unresolved without any user-visible signal, evidence
apps/desktop/src/main/index.ts:1051.
Suggested fix:// Prefer surfacing migration failure to the existing boot error dialog path. await migrateStaleCodexEntryIfNeeded();
Summary
- Review mode: follow-up after new commits
- 1 issue found (1 Major).
docs/VISION.mdanddocs/PRINCIPLES.md: Not found in repo/docs.
Testing
- Not run (automation).
open-codesign Bot
| try { | ||
| await migrateStaleCodexEntryIfNeeded(); | ||
| } catch (err) { | ||
| getLogger('main').warn('boot.migrate_stale_codex_failed', { |
There was a problem hiding this comment.
[Major] This catch path turns migration failures into a log-only warning. That is a silent fallback for users and can leave stale wire/baseUrl in place with no visible remediation path. Please let this throw into the existing boot error handling flow (or surface a user-visible error) instead of swallowing it.
…ponses wire Phase 2 of the Codex ChatGPT subscription login. Phase 1 shipped a self-rolled client (SSE parser, ChatGPT headers, JWT accountId extraction, 401 retry) behind a "coming soon" UI gate. pi-ai 0.67.68 ships all of that as a first-class `openai-codex-responses` wire, so this deletes our version and routes ChatGPT subscription through the same `core → pi-agent-core → pi-ai` path every other provider uses. ## Schema + routing - `packages/shared`: extend `WireApiSchema` and `CanonicalWire` with `openai-codex-responses`; promote `CHATGPT_CODEX_PROVIDER_ID` to shared so `provider-settings` and the OAuth module reference the same literal without a module cycle. - `canonicalBaseUrl` passes codex URLs through untouched (pi-ai's wire appends `/codex/responses` itself); `modelsEndpointUrl` throws for codex (no discoverable /models endpoint — providers use modelsHint). - `packages/core`, `packages/providers`: `apiForWire` + `synthesizeWireModel` recognize the new wire; the 4 duplicated `'openai-chat' | …` unions consolidated onto the shared `WireApi`. ## Desktop wiring - New `apps/desktop/src/main/resolve-api-key.ts`: DI helper that routes ChatGPT provider id to the token store's auto-refreshing access token, other providers to the keychain-backed API key. Codex auth failures surface as `CodesignError(PROVIDER_AUTH_MISSING)` for consistent renderer error-code routing. 7 unit tests cover the fork. - `main/index.ts`: `resolveActiveApiKeyFromState` replaces the inline `isChatgptCodex` validate / dispatch branches in all 4 IPC handlers. Legacy `codesign:generate` no longer rejects codex. - Long agent runs: `GenerateInput.getApiKey` is a new optional async getter; the desktop passes it only for codex so pi-agent-core calls back into the token store on each LLM round-trip (auto-refreshes within the 5-min buffer). Mid-run sign-out errors are captured in a closure and rethrown from the post-agent branch so the structured PROVIDER_AUTH_MISSING code survives pi-agent-core's plain-string failure-message flattening. ## Registration + migration - `codex-oauth-ipc.ts`: provider entry registers `wire=openai-codex-responses`, bare `baseUrl=https://chatgpt.com/backend-api`, and the full 9-model catalog (gpt-5.1 → gpt-5.4-mini), flagship-first. - `migrateStaleCodexEntryIfNeeded()` runs once at boot and rewrites any Phase-1-shaped entry so feat-branch testers don't need to sign out and back in. No-op when the entry is absent or already canonical. ## UI - `ChatgptLoginCard.tsx`: flipped out of "coming soon" mode back to the full login/status/logout flow with i18n in both locales. ## Deletions (~963 LOC of Phase 1 code now provided by pi-ai) - `apps/desktop/src/main/codex-generate.ts` + test - `apps/desktop/src/main/codex-title.ts` - `packages/providers/src/codex/client.ts` + test OAuth-side code (`oauth.ts`, `oauth-server.ts`, `token-store.ts`) is unchanged — still the only codex-specific code, and it sits outside the generation link. pnpm -r typecheck, 1293 tests across 10 packages, and pnpm lint all green. Signed-off-by: hqhq1025 <[email protected]>
63920cb to
79c064f
Compare
|
Fixed. Removed the
|
There was a problem hiding this comment.
Findings
- No issues found in added/modified lines that meet the reporting confidence bar.
Summary
- Review mode: follow-up after new commits
- No new blocker/major/minor issues identified in the latest head diff.
- Previously flagged silent-fallback risk is resolved: boot migration now bubbles failure into the existing boot-error dialog path (
apps/desktop/src/main/index.ts:1055). docs/VISION.mdanddocs/PRINCIPLES.md: Not found in repo/docs.- Residual risk/testing gap: this PR removes many unit tests in
apps/desktop/src/renderer/src/components/Settings.test.tswhile inlining banner-branch logic inSettings.tsx, so those branches now rely more on integration behavior than direct unit coverage.
Testing
- Not run (automation).
open-codesign Bot
Summary
Phase 2 of the Codex ChatGPT subscription login. Phase 1 shipped a self-rolled client (SSE parser, ChatGPT headers, JWT accountId extraction, 401 retry) behind a "coming soon" UI gate. pi-ai 0.67.68 ships all of that as a first-class
openai-codex-responseswire, so this PR deletes our version and routes ChatGPT subscription through the samecore → pi-agent-core → pi-aipath every other provider already uses.isChatgptCodexbranches.generate/generateTitle/applyCommentall resolve codex exactly like Anthropic or OpenAI — the only codex-specific code left is the OAuth login flow (oauth.ts/oauth-server.ts/token-store.ts), and that sits outside the generation link.GenerateInput.getApiKeyis a new optional async getter; the desktop passes it for codex so pi-agent-core re-reads the token between turns (auto-refreshing within the token store's 5-min buffer). Mid-run sign-out errors propagate the originalCodesignError(PROVIDER_AUTH_MISSING)rather than being flattened into a plainPROVIDER_ERROR.migrateStaleCodexEntryIfNeeded()rewrites any Phase-1-shaped provider entry so feat-branch testers don't need to sign out/in.ChatgptLoginCardreturns to the three-state login/status/logout flow with i18n in both locales.Net diff: +758 / −1104, mostly deletions of Phase 1 self-rolled code pi-ai now provides.
PRINCIPLES §5b
CHATGPT_CODEX_PROVIDER_ID+WireApilive in@open-codesign/sharedso future wire additions are one-line changes; OAuth provider entry is system-managed (rewritten idempotently on login/migrate)generateViaAgentcode path for every provider; no provider-specific dispatch branchesTest plan
pnpm -r typecheck— greenpnpm -r test— 1289 tests pass across 10 packages (+11 new tests: base-url codex behavior,resolve-api-keyDI helper,migrateStaleCodexEntryIfNeededfork, agent.ts per-turn getApiKey + capture/rethrow)pnpm lint— greenCommits
b8bb1b2Main implementation — delete self-rolled client, register pi-ai wire, flip UI375b9c5First review pass — WireApi dedup, structured auth error, modelsHint ordering, +4 testsdde3bf3Second pass — migration,CHATGPT_CODEX_PROVIDER_IDshared,resolve-api-keyDI + 7 tests, per-turn getApiKey8c443b9Third pass — capture+rethrow so mid-agent-run auth errors preserve their structured code; +3 agent tests412f159Fourth pass — remove deadcomingSooni18n key, update changeset296ac25Drop obsolete internal Phase 2 resume docKnown follow-ups (not blocking)
complete()inpackages/providersalready surfaces them viaGenerateResult, but the desktop UI's cost display still reads zero because Phase 1 hardcoded it).imageinput blocks; thecomplete()shim currently only forwards text. Tracked separately.