|
1 | | -# AGENTS.md |
| 1 | +# PROJECT KNOWLEDGE BASE |
2 | 2 |
|
3 | | -This file provides coding guidance for AI agents (including Claude Code, Codex, and others) when working with code in this repository. |
| 3 | +Generated: 2026-01-29 |
| 4 | +Commit: 693b0cc |
| 5 | +Branch: main |
4 | 6 |
|
5 | | -## Overview |
| 7 | +## OVERVIEW |
| 8 | +OpenCode plugin that swaps OpenAI SDK calls to the ChatGPT Codex backend with multi-account OAuth. |
6 | 9 |
|
7 | | -This is an **opencode plugin** that enables OAuth authentication with OpenAI's ChatGPT Plus/Pro Codex backend. It allows users to access `gpt-5.2-codex`, `gpt-5.1-codex`, `gpt-5.1-codex-max`, `gpt-5.1-codex-mini`, `gpt-5.2`, and `gpt-5.1` models through their ChatGPT subscription instead of using OpenAI Platform API credits. Legacy GPT-5.0 models are automatically normalized to their GPT-5.1 equivalents. |
8 | | - |
9 | | -**Key architecture principle**: 7-step fetch flow that intercepts opencode's OpenAI SDK requests, transforms them for the ChatGPT backend API, and handles OAuth token management. |
10 | | - |
11 | | -## Build & Test Commands |
| 10 | +## STRUCTURE |
| 11 | +``` |
| 12 | +./ |
| 13 | +├── index.ts # plugin entry point (not under src/) |
| 14 | +├── lib/ # main source (auth, request, prompts, config) |
| 15 | +├── test/ # vitest suites |
| 16 | +├── scripts/ # install + build helpers |
| 17 | +├── assets/ # static assets |
| 18 | +├── config/ # OpenCode config examples |
| 19 | +├── docs/ # architecture docs/diagrams |
| 20 | +├── dist/ # build output (generated) |
| 21 | +└── SECURITY.md # vuln reporting rules |
| 22 | +``` |
12 | 23 |
|
| 24 | +## WHERE TO LOOK |
| 25 | +| Task | Location | Notes | |
| 26 | +| --- | --- | --- | |
| 27 | +| Fetch flow orchestration | `index.ts` | 7-step request pipeline |
| 28 | +| OAuth flow + tokens | `lib/auth/auth.ts` | PKCE, refresh, JWT decode |
| 29 | +| OAuth callback server | `lib/auth/server.ts` | binds port 1455 |
| 30 | +| Request mutation | `lib/request/request-transformer.ts` | model normalization + prompts |
| 31 | +| Request helpers | `lib/request/fetch-helpers.ts` | headers, rate limit handling |
| 32 | +| SSE response handling | `lib/request/response-handler.ts` | SSE to JSON |
| 33 | +| Prompt fetching/cache | `lib/prompts/codex.ts` | GitHub release ETag cache |
| 34 | +| Config parsing | `lib/config.ts` | CODEX_MODE + options |
| 35 | +| Tests | `test/` | vitest globals enabled |
| 36 | + |
| 37 | +## CONVENTIONS |
| 38 | +- Source lives in `index.ts` and `lib/`; `dist/` is generated. |
| 39 | +- ESLint flat config: no `any`, unused args must be prefixed with `_`. |
| 40 | +- Test files relax lint rules; see `eslint.config.js`. |
| 41 | +- Build must copy `lib/oauth-success.html` into `dist/lib/` (see `scripts/copy-oauth-success.js`). |
| 42 | + |
| 43 | +## ANTI-PATTERNS (THIS PROJECT) |
| 44 | +- Do not edit `dist/` outputs or `tmp*` directories. |
| 45 | +- Do not open public security issues; follow `SECURITY.md` for reporting. |
| 46 | + |
| 47 | +## COMMANDS |
13 | 48 | ```bash |
14 | | -# Build (compiles TypeScript + copies HTML file) |
15 | 49 | npm run build |
16 | | - |
17 | | -# Type checking only (no build) |
18 | 50 | npm run typecheck |
19 | | - |
20 | | -# Run all tests |
21 | 51 | npm test |
22 | | - |
23 | | -# Watch mode for TDD |
24 | | -npm run test:watch |
25 | | - |
26 | | -# Interactive test UI |
27 | | -npm run test:ui |
28 | | - |
29 | | -# Coverage report |
30 | | -npm run test:coverage |
| 52 | +npm run lint |
31 | 53 | ``` |
32 | 54 |
|
33 | | -**Important**: The build script has a critical step that copies `lib/oauth-success.html` to `dist/lib/`. This HTML file is required for the OAuth callback flow. |
34 | | - |
35 | | -## Code Architecture |
36 | | - |
37 | | -### Plugin Flow (index.ts) |
38 | | - |
39 | | -The main entry point orchestrates a **7-step fetch flow**: |
40 | | - |
41 | | -1. **Token Management**: Check token expiration, refresh if needed |
42 | | -2. **URL Rewriting**: Transform OpenAI Platform API URLs → ChatGPT backend API (`https://chatgpt.com/backend-api/codex/responses`) |
43 | | -3. **Request Transformation**: |
44 | | - - Normalize model names (all variants → `gpt-5.2`, `gpt-5.2-codex`, `gpt-5.1`, `gpt-5.1-codex`, `gpt-5.1-codex-max`, `gpt-5.1-codex-mini`, `gpt-5`, `gpt-5-codex`, or `codex-mini-latest`) |
45 | | - - Inject Codex system instructions from latest GitHub release |
46 | | - - Apply reasoning configuration (effort, summary, verbosity) |
47 | | - - Add CODEX_MODE bridge prompt (default) or tool remap message (legacy) |
48 | | - - Filter OpenCode system prompts when in CODEX_MODE |
49 | | - - Filter conversation history (remove `rs_*` IDs for stateless operation) |
50 | | -4. **Headers**: Add OAuth token + ChatGPT account ID |
51 | | -5. **Request Execution**: Send to Codex backend |
52 | | -6. **Response Logging**: Optional debug logging (ENABLE_PLUGIN_REQUEST_LOGGING=1) |
53 | | -7. **Response Handling**: Convert SSE to JSON (non-tool requests) or pass through |
54 | | - |
55 | | -### Module Organization |
56 | | - |
57 | | -**Core Plugin** (`index.ts`) |
58 | | -- Plugin definition and main fetch orchestration |
59 | | -- OAuth loader (extracts ChatGPT account ID from JWT) |
60 | | -- Configuration loading and CODEX_MODE determination |
61 | | - |
62 | | -**Authentication** (`lib/auth/`) |
63 | | -- `auth.ts`: OAuth flow (PKCE, token exchange, JWT decoding, refresh) |
64 | | -- `server.ts`: Local HTTP server for OAuth callback (port 1455) |
65 | | -- `browser.ts`: Platform-specific browser opening |
66 | | - |
67 | | -**Request Handling** (`lib/request/`) |
68 | | -- `fetch-helpers.ts`: 10 focused helper functions for main fetch flow |
69 | | -- `request-transformer.ts`: Body transformations (model normalization, reasoning config, input filtering) |
70 | | -- `response-handler.ts`: SSE to JSON conversion |
71 | | - |
72 | | -**Prompts** (`lib/prompts/`) |
73 | | -- `codex.ts`: Fetches Codex instructions from GitHub (ETag-cached), tool remap message |
74 | | -- `codex-opencode-bridge.ts`: CODEX_MODE bridge prompt for CLI parity |
75 | | - |
76 | | -**Configuration** (`lib/`) |
77 | | -- `config.ts`: Plugin config loading, CODEX_MODE determination |
78 | | -- `constants.ts`: All magic values, URLs, error messages |
79 | | -- `types.ts`: TypeScript type definitions |
80 | | -- `logger.ts`: Debug logging (controlled by env var) |
81 | | - |
82 | | -### Key Design Patterns |
83 | | - |
84 | | -**1. Stateless Operation**: Uses `store: false` + `include: ["reasoning.encrypted_content"]` |
85 | | -- Allows multi-turn conversations without server-side storage |
86 | | -- Encrypted reasoning content persists context across turns |
87 | | - |
88 | | -**2. CODEX_MODE** (enabled by default): |
89 | | -- **Priority**: `CODEX_MODE` env var > `~/.opencode/openai-codex-auth-config.json` > default (true) |
90 | | -- When enabled: Filters out OpenCode system prompts, adds Codex-OpenCode bridge prompt with Task tool & MCP awareness |
91 | | -- When disabled: Uses legacy tool remap message |
92 | | -- Bridge prompt (~550 tokens): Tool mappings, available tools, working style, **Task tool/sub-agent awareness**, **MCP tool awareness** |
93 | | -- **Prompt verification**: Caches OpenCode's codex.txt from GitHub (ETag-based) to verify exact prompt removal, with fallback to text signature matching |
94 | | - |
95 | | -**3. Configuration Merging**: |
96 | | -- Global options (`provider.openai.options`) + per-model options (`provider.openai.models[name].options`) |
97 | | -- Model-specific options override global |
98 | | -- Plugin defaults: `reasoningEffort: "medium"`, `reasoningSummary: "auto"`, `textVerbosity: "medium"` |
99 | | - |
100 | | -**4. Model Normalization** (GPT-5.0 → GPT-5.1 migration): |
101 | | -- All `gpt-5.2-codex*` variants → `gpt-5.2-codex` (newest Codex model, supports xhigh) |
102 | | -- All `gpt-5.1-codex-max*` variants → `gpt-5.1-codex-max` |
103 | | -- All `gpt-5.1-codex*` variants → `gpt-5.1-codex` |
104 | | -- All `gpt-5.1-codex-mini*` variants → `gpt-5.1-codex-mini` |
105 | | -- All `gpt-5.2` variants → `gpt-5.2` |
106 | | -- All `gpt-5.1` variants → `gpt-5.1` |
107 | | -- **Legacy mappings** (GPT-5.0 being phased out): |
108 | | - - `gpt-5-codex*` variants → `gpt-5.1-codex` |
109 | | - - `gpt-5-codex-mini*` or `codex-mini-latest` → `gpt-5.1-codex-mini` |
110 | | - - `gpt-5*` variants (including `gpt-5-mini`, `gpt-5-nano`) → `gpt-5.1` |
111 | | -- `minimal` effort auto-normalized to `low` for Codex families (including GPT-5.2 Codex) and clamped to `medium` (or `high` when requested) for Codex Mini |
112 | | - |
113 | | -**5. Model-Specific Prompt Selection**: |
114 | | -- Different prompts for different model families (matching Codex CLI): |
115 | | - - `gpt-5.2-codex*` → `gpt-5.2-codex_prompt.md` (117 lines, Codex CLI agent prompt) |
116 | | - - `gpt-5.1-codex-max*` → `gpt-5.1-codex-max_prompt.md` (117 lines, frontend design guidelines) |
117 | | - - `gpt-5.1-codex*`, `codex-*` → `gpt_5_codex_prompt.md` (105 lines, coding focus) |
118 | | - - `gpt-5.2*` → `gpt_5_2_prompt.md` (GPT‑5.2 general family) |
119 | | - - `gpt-5.1*` → `gpt_5_1_prompt.md` (368 lines, full behavioral guidance) |
120 | | -- `getModelFamily()` determines prompt selection based on normalized model |
121 | | - |
122 | | -**6. Codex Instructions Caching**: |
123 | | -- Fetches from latest release tag (not main branch) |
124 | | -- ETag-based HTTP conditional requests per model family |
125 | | -- Separate cache files per family: `gpt-5.2-codex-instructions.md`, `codex-max-instructions.md`, `codex-instructions.md`, `gpt-5.2-instructions.md`, `gpt-5.1-instructions.md` |
126 | | -- Cache invalidation when release tag changes |
127 | | -- Falls back to bundled version if GitHub unavailable |
128 | | - |
129 | | -## Development Patterns |
130 | | - |
131 | | -### Adding New Configuration Options |
132 | | - |
133 | | -1. Add to `ConfigOptions` interface in `lib/types.ts` |
134 | | -2. Update `transformRequestBody()` in `lib/request/request-transformer.ts` |
135 | | -3. Add tests in `test/request-transformer.test.ts` |
136 | | -4. Document in README.md configuration section |
137 | | - |
138 | | -### Modifying Request Transformation |
139 | | - |
140 | | -All request transformations go through `transformRequestBody()`: |
141 | | -- Input filtering: `filterInput()`, `filterOpenCodeSystemPrompts()` |
142 | | -- Message injection: `addCodexBridgeMessage()` or `addToolRemapMessage()` |
143 | | -- Reasoning config: `getReasoningConfig()` (follows Codex CLI defaults, not opencode defaults) |
144 | | -- Model config: `getModelConfig()` (merges global + per-model options) |
145 | | - |
146 | | -### OAuth Flow Modifications |
147 | | - |
148 | | -OAuth implementation follows OpenAI Codex CLI patterns: |
149 | | -- Client ID: `app_EMoamEEZ73f0CkXaXp7hrann` |
150 | | -- PKCE with S256 challenge |
151 | | -- Special params: `codex_cli_simplified_flow=true`, `originator=codex_cli_rs` |
152 | | -- Callback server on port 1455 (matches Codex CLI) |
153 | | - |
154 | | -### Testing Strategy |
155 | | - |
156 | | -- **191 comprehensive tests** covering all modules |
157 | | -- Test files mirror source structure (`test/auth.test.ts` ↔ `lib/auth/auth.ts`) |
158 | | -- Mock-heavy testing (no actual network calls or file I/O in tests) |
159 | | -- Focus on edge cases: token expiration, model normalization, input filtering, CODEX_MODE toggling |
160 | | - |
161 | | -## Important Configuration Differences |
162 | | - |
163 | | -This plugin **intentionally differs from opencode defaults** because it accesses ChatGPT backend API (not OpenAI Platform API): |
164 | | - |
165 | | -| Setting | opencode Default | This Plugin Default | Reason | |
166 | | -|---------|-----------------|---------------------|--------| |
167 | | -| `reasoningEffort` | "high" (gpt-5) | "medium" (Codex Max defaults to "high") | Matches Codex CLI default and Codex Max capabilities | |
168 | | -| `textVerbosity` | "low" (gpt-5) | "medium" | Matches Codex CLI default | |
169 | | -| `reasoningSummary` | "detailed" | "auto" | Matches Codex CLI default | |
170 | | -| gpt-5-codex config | (excluded) | Full support | opencode excludes gpt-5-codex from auto-config | |
171 | | -| `store` | true | false | Required for ChatGPT backend | |
172 | | -| `include` | (not set) | `["reasoning.encrypted_content"]` | Required for stateless operation | |
173 | | - |
174 | | -## File Paths & Locations |
175 | | - |
176 | | -- **Plugin config**: `~/.opencode/openai-codex-auth-config.json` |
177 | | -- **Cache dir**: `~/.opencode/cache/` |
178 | | - - `codex-instructions.md` (Codex CLI instructions from GitHub) |
179 | | - - `codex-instructions-meta.json` (ETag + release tag for Codex instructions) |
180 | | - - `opencode-codex.txt` (OpenCode system prompt from GitHub, for verification) |
181 | | - - `opencode-codex-meta.json` (ETag for OpenCode prompt) |
182 | | -- **Debug logs**: `~/.opencode/logs/codex-plugin/` (when `ENABLE_PLUGIN_REQUEST_LOGGING=1`) |
183 | | -- **OAuth callback**: `http://localhost:1455/auth/callback` |
184 | | - |
185 | | -## Environment Variables |
186 | | - |
187 | | -- `CODEX_MODE`: Override config file (1=enable, 0=disable) |
188 | | -- `ENABLE_PLUGIN_REQUEST_LOGGING`: Enable detailed request logging (1=enable) |
189 | | - |
190 | | -## TypeScript Configuration |
191 | | - |
192 | | -- Target: ES2022 |
193 | | -- Module: ES2022 with bundler resolution |
194 | | -- Output: `./dist/` |
195 | | -- Strict mode enabled |
196 | | -- Declaration files generated |
197 | | -- Source maps enabled |
198 | | -- Excludes: `test/`, `node_modules/`, `dist/` |
199 | | - |
200 | | -## Dependencies |
201 | | - |
202 | | -**Production**: |
203 | | -- `@openauthjs/openauth` (OAuth PKCE implementation) |
204 | | - |
205 | | -**Development**: |
206 | | -- `@opencode-ai/plugin` (peer dependency) |
207 | | -- `vitest` (testing framework) |
208 | | -- TypeScript |
209 | | - |
210 | | -**Zero external runtime dependencies** - only uses Node.js built-ins for file I/O, HTTP, crypto. |
| 55 | +## NOTES |
| 56 | +- OAuth callback server binds `http://127.0.0.1:1455/auth/callback`. |
| 57 | +- ChatGPT backend requires stateless requests (`store: false`, include encrypted reasoning). |
0 commit comments