Skip to content

Commit 62ac8d0

Browse files
authored
Merge pull request #10 from ndycode/fix/multi-account-duplicate-accountid
feat: v4.6.0 - Context overflow handling and missing tool result injection
2 parents 33418a9 + 58fd6ae commit 62ac8d0

11 files changed

Lines changed: 621 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,27 @@ All notable changes to this project are documented here. Dates use the ISO forma
88

99
### Changed
1010

11-
## [4.3.1] - 2026-01-23
11+
## [4.6.0] - 2026-01-25
12+
13+
**Feature release**: Context overflow handling and missing tool result injection.
1214

1315
### Added
14-
- `openai-accounts-status --json` for scriptable status output with email/ID labels.
16+
- **Context Overflow Handler**: Gracefully handles "prompt too long" / context length exceeded errors:
17+
- Returns synthetic SSE response with helpful instructions instead of raw 400 error
18+
- Suggests `/compact`, `/clear`, or `/undo` commands to reduce context size
19+
- Prevents OpenCode session from getting locked on context overflow
20+
- New module: `lib/context-overflow.ts`
21+
- **Missing Tool Result Injection**: Automatically handles cancelled tool calls (ESC mid-execution):
22+
- Detects orphaned `function_call` items (calls without matching outputs)
23+
- Injects synthetic output: `"Operation cancelled by user"`
24+
- Prevents "missing tool_result" API errors when user cancels mid-tool
25+
- New function: `injectMissingToolOutputs()` in `lib/request/helpers/input-utils.ts`
26+
- **34 new unit tests** for context overflow and tool injection (now 379 total tests)
1527

16-
### Changed
17-
- Account labels now prefer email and show ID suffix when available; list/status outputs are columnized for readability.
18-
- Stored account emails are trimmed/lowercased when present.
19-
- Dependency refresh: @opencode-ai plugin/sdk 1.1.34, hono 4.11.5, vitest 4.0.18, @types/node 25.0.10, @typescript-eslint 8.53.1.
28+
### Technical Details
29+
- Context overflow detection matches patterns: `prompt_too_long`, `context_length_exceeded`, `maximum context length`, `token limit exceeded`, `too many tokens`
30+
- Synthetic SSE response includes proper message_start/content_block_delta/message_stop events
31+
- Tool injection preserves message order: outputs are placed immediately after their calls
2032

2133
## [4.5.0] - 2026-01-25
2234

@@ -48,6 +60,16 @@ All notable changes to this project are documented here. Dates use the ISO forma
4860
- Stale entries (>30s) are automatically cleaned up to prevent memory leaks
4961
- Auto-update check is non-blocking and fails silently to avoid disrupting plugin operation
5062

63+
## [4.3.1] - 2026-01-23
64+
65+
### Added
66+
- `openai-accounts-status --json` for scriptable status output with email/ID labels.
67+
68+
### Changed
69+
- Account labels now prefer email and show ID suffix when available; list/status outputs are columnized for readability.
70+
- Stored account emails are trimmed/lowercased when present.
71+
- Dependency refresh: @opencode-ai plugin/sdk 1.1.34, hono 4.11.5, vitest 4.0.18, @types/node 25.0.10, @typescript-eslint 8.53.1.
72+
5173
## [4.4.0] - 2026-01-25
5274

5375
**Feature release**: Intelligent rate-limit rotation with health-based account selection.

README.md

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Install the opencode-openai-codex-auth-multi plugin and add the OpenAI model def
5454
**Option B: One-command install**
5555

5656
```bash
57-
npx -y opencode-openai-codex-auth-multi@4.3.1
57+
npx -y opencode-openai-codex-auth-multi@latest
5858
```
5959

6060
This writes the config to `~/.config/opencode/opencode.json`, backs up existing config, and clears the plugin cache.
@@ -67,7 +67,7 @@ This writes the config to `~/.config/opencode/opencode.json`, backs up existing
6767

6868
```json
6969
{
70-
"plugin": ["opencode-openai-codex-auth-multi@4.3.1"]
70+
"plugin": ["opencode-openai-codex-auth-multi@latest"]
7171
}
7272
```
7373

@@ -99,7 +99,7 @@ This writes the config to `~/.config/opencode/opencode.json`, backs up existing
9999
2. Add the plugin to the `plugin` array:
100100
```json
101101
{
102-
"plugin": ["opencode-openai-codex-auth-multi@4.3.1"]
102+
"plugin": ["opencode-openai-codex-auth-multi@latest"]
103103
}
104104
```
105105

@@ -147,7 +147,7 @@ Add this to your `~/.config/opencode/opencode.json`:
147147
```json
148148
{
149149
"$schema": "https://opencode.ai/config.json",
150-
"plugin": ["opencode-openai-codex-auth-multi@4.3.1"],
150+
"plugin": ["opencode-openai-codex-auth-multi@latest"],
151151
"provider": {
152152
"openai": {
153153
"options": {
@@ -236,7 +236,7 @@ For legacy OpenCode (v1.0.209 and below), use `config/opencode-legacy.json` whic
236236

237237
## Multi-Account Setup
238238

239-
Add multiple ChatGPT accounts for higher combined quotas. The plugin automatically rotates between accounts when one is rate-limited.
239+
Add multiple ChatGPT accounts for higher combined quotas. The plugin uses **health-aware rotation** with automatic failover.
240240

241241
```bash
242242
opencode auth login # Run again to add more accounts
@@ -247,6 +247,12 @@ opencode auth login # Run again to add more accounts
247247
- `openai-accounts-switch` — Switch active account
248248
- `openai-accounts-status` — Show rate limit status
249249

250+
**How rotation works (v4.4.0+):**
251+
- Health scoring tracks success/failure per account
252+
- Token bucket prevents hitting rate limits
253+
- Hybrid selection prefers healthy accounts with available tokens
254+
- Always retries when all accounts are rate-limited (waits for reset)
255+
250256
**Storage:** `~/.opencode/openai-codex-accounts.json`
251257

252258
---
@@ -354,7 +360,7 @@ OpenCode uses `~/.config/opencode/` on **all platforms** including Windows.
354360
**Solutions:**
355361
1. Update plugin:
356362
```bash
357-
npx -y opencode-openai-codex-auth-multi@4.3.1
363+
npx -y opencode-openai-codex-auth-multi@latest
358364
```
359365
2. Ensure config has:
360366
```json
@@ -399,7 +405,7 @@ Works alongside oh-my-opencode. No special configuration needed.
399405
```json
400406
{
401407
"plugin": [
402-
"opencode-openai-codex-auth-multi@4.3.1",
408+
"opencode-openai-codex-auth-multi@latest",
403409
"oh-my-opencode@latest"
404410
]
405411
}
@@ -412,7 +418,7 @@ List this plugin BEFORE DCP:
412418
```json
413419
{
414420
"plugin": [
415-
"opencode-openai-codex-auth-multi@4.3.1",
421+
"opencode-openai-codex-auth-multi@latest",
416422
"@tarquinen/opencode-dcp@latest"
417423
]
418424
}
@@ -434,6 +440,14 @@ Create `~/.opencode/openai-codex-auth-config.json` for optional settings:
434440
|--------|---------|--------------|
435441
| `codexMode` | `true` | Uses Codex-OpenCode bridge prompt (synced with latest Codex CLI) |
436442

443+
### Retry Behavior (v4.4.0+)
444+
445+
| Option | Default | What it does |
446+
|--------|---------|--------------|
447+
| `retryAllAccountsRateLimited` | `true` | Wait and retry when all accounts are rate-limited |
448+
| `retryAllAccountsMaxWaitMs` | `0` | Max wait time (0 = unlimited) |
449+
| `retryAllAccountsMaxRetries` | `Infinity` | Max retry attempts |
450+
437451
### Environment Variables
438452

439453
```bash

docs/configuration.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,10 @@ Advanced plugin settings in `~/.opencode/openai-codex-auth-config.json`:
220220

221221
```json
222222
{
223-
"codexMode": true
223+
"codexMode": true,
224+
"retryAllAccountsRateLimited": true,
225+
"retryAllAccountsMaxWaitMs": 0,
226+
"retryAllAccountsMaxRetries": null
224227
}
225228
```
226229

@@ -229,15 +232,22 @@ Advanced plugin settings in `~/.opencode/openai-codex-auth-config.json`:
229232
| Option | Default | What it does |
230233
|--------|---------|--------------|
231234
| `codexMode` | `true` | Uses Codex-OpenCode bridge prompt (synced with Codex CLI) |
235+
| `retryAllAccountsRateLimited` | `true` | Wait and retry when all accounts are rate-limited |
236+
| `retryAllAccountsMaxWaitMs` | `0` | Max wait time in ms (0 = unlimited) |
237+
| `retryAllAccountsMaxRetries` | `Infinity` | Max retry attempts (null = unlimited) |
232238

233239
### Environment Variables
234240

235241
| Variable | Description |
236242
|----------|-------------|
237243
| `DEBUG_CODEX_PLUGIN=1` | Enable debug logging |
238244
| `ENABLE_PLUGIN_REQUEST_LOGGING=1` | Log all API requests |
245+
| `CODEX_PLUGIN_LOG_LEVEL=debug` | Set log level (debug, info, warn, error) |
239246
| `CODEX_MODE=0` | Temporarily disable bridge prompt |
240247
| `CODEX_MODE=1` | Temporarily enable bridge prompt |
248+
| `CODEX_AUTH_RETRY_ALL_RATE_LIMITED=0` | Disable wait-and-retry behavior |
249+
| `CODEX_AUTH_RETRY_ALL_MAX_WAIT_MS=30000` | Set max wait time |
250+
| `CODEX_AUTH_RETRY_ALL_MAX_RETRIES=1` | Set max retries |
241251

242252
---
243253

docs/development/ARCHITECTURE.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,62 @@ let include: Vec<String> = if reasoning.is_some() {
428428

429429
---
430430

431+
## Multi-Account Rotation (v4.4.0+)
432+
433+
### Health-Based Account Selection
434+
435+
The plugin tracks account health and uses intelligent rotation:
436+
437+
```
438+
Account Selection Flow:
439+
1. Score = (health × 2) + (tokens × 5) + (freshness × 0.1)
440+
2. Select account with highest score
441+
3. Consume token from bucket
442+
4. On success: health +1
443+
5. On rate limit: health -10, mark rate-limited
444+
6. On failure: health -20
445+
7. Passive recovery: +2 health/hour
446+
```
447+
448+
### Token Bucket Rate Limiting
449+
450+
Client-side rate limiting prevents hitting API limits:
451+
452+
| Parameter | Value |
453+
|-----------|-------|
454+
| Max tokens | 50 |
455+
| Regeneration | 6 tokens/min |
456+
| Consume per request | 1 token |
457+
458+
### Reason-Aware Backoff
459+
460+
Different rate limit reasons use different backoff multipliers:
461+
462+
| Reason | Multiplier | Description |
463+
|--------|------------|-------------|
464+
| `quota` | 3.0× | Daily quota exhausted |
465+
| `tokens` | 1.5× | Token limit hit |
466+
| `concurrent` | 0.5× | Concurrent request limit |
467+
| `unknown` | 1.0× | Default |
468+
469+
### RefreshQueue (v4.5.0+)
470+
471+
Prevents race conditions when multiple concurrent requests try to refresh the same token:
472+
473+
```typescript
474+
// Without RefreshQueue: N concurrent requests = N refresh attempts
475+
// With RefreshQueue: N concurrent requests = 1 refresh, N-1 await
476+
477+
const queue = getRefreshQueue();
478+
const tokens = await queue.queuedRefresh(refreshToken, async () => {
479+
return await actualRefresh(refreshToken);
480+
});
481+
```
482+
483+
**Source**: `lib/refresh-queue.ts`, `lib/rotation.ts`
484+
485+
---
486+
431487
## Performance Considerations
432488

433489
### Token Usage

index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import {
5555
} from "./lib/constants.js";
5656
import { logRequest, logDebug } from "./lib/logger.js";
5757
import { checkAndNotify } from "./lib/auto-update-checker.js";
58+
import { handleContextOverflow } from "./lib/context-overflow.js";
5859
import {
5960
AccountManager,
6061
extractAccountEmail,
@@ -660,10 +661,16 @@ export const OpenAIAuthPlugin: Plugin = async ({ client }: PluginInput) => {
660661
headers: Object.fromEntries(response.headers.entries()),
661662
});
662663

663-
if (!response.ok) {
664-
const { response: errorResponse, rateLimit } =
665-
await handleErrorResponse(response);
666-
if (rateLimit) {
664+
if (!response.ok) {
665+
// Check for context overflow (400 "prompt too long") before other error handling
666+
const contextOverflowResult = await handleContextOverflow(response, model);
667+
if (contextOverflowResult.handled) {
668+
return contextOverflowResult.response;
669+
}
670+
671+
const { response: errorResponse, rateLimit } =
672+
await handleErrorResponse(response);
673+
if (rateLimit) {
667674
const { attempt, delayMs } = getRateLimitBackoff(
668675
account.index,
669676
quotaKey,

0 commit comments

Comments
 (0)