Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6f4d0be
Refactor metrics response helpers and fix JWT decoding
riatzukiza Nov 20, 2025
e5a5a84
Tighten session tail slice to user/assistant only
riatzukiza Nov 20, 2025
bd8bdfe
Improve compaction resilience and cache metrics safety
riatzukiza Nov 20, 2025
034219d
Merge branch 'staging' into chore/codex-max-release-review
riatzukiza Nov 20, 2025
a820aa3
Strengthen tests for cache keys and gpt-5.1 cases
riatzukiza Nov 20, 2025
848778f
Merge branch 'chore/codex-max-release-review' of github.com:open-hax/…
riatzukiza Nov 20, 2025
80995c0
Soften first-session cache warnings and sync transformed body
riatzukiza Nov 20, 2025
63d01e9
Preseed session prompt cache keys
riatzukiza Nov 20, 2025
df60966
Memoize config loading and keep bridge prompts stable
riatzukiza Nov 20, 2025
954f559
Code cleanup: removed redundancy, improved tests
opencode-agent[bot] Nov 20, 2025
ed5ca22
Fixed test shallow copy issue with deep copy.
opencode-agent[bot] Nov 20, 2025
f3ce77e
chore(codex-max): memoize config loading, stabilize bridge prompts, a…
riatzukiza Nov 20, 2025
f43878f
stabilize oauth server tests by completing mocks
riatzukiza Nov 20, 2025
2a9b588
fix: improve token refresh error handling and add debug logging
riatzukiza Nov 20, 2025
6ef3e00
Fix test to assert on returned auth object instead of in-place mutation
riatzukiza Nov 20, 2025
bb5c250
test: add negative test for host-provided prompt_cache_key; fix: ensu…
riatzukiza Nov 20, 2025
1d6697b
fix: clone auth refresh and tighten console logging
riatzukiza Nov 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -450,18 +450,18 @@ When using [`config/full-opencode.json`](./config/full-opencode.json), you get t

| CLI Model ID | TUI Display Name | Reasoning Effort | Best For |
|--------------|------------------|-----------------|----------|
| `gpt-5.1-codex-max` | GPT 5.1 Codex Max (OAuth) | Medium (Extra High optional) | Default flagship tier with optional `xhigh` reasoning for long, complex runs |
| `gpt-5.1-codex-max` | GPT 5.1 Codex Max (OAuth) | Low/Medium/High/**Extra High** | Default flagship tier with `xhigh` reasoning for complex, multi-step problems |
| `gpt-5.1-codex-low` | GPT 5.1 Codex Low (OAuth) | Low | Fast code generation on the newest Codex tier |
| `gpt-5.1-codex-medium` | GPT 5.1 Codex Medium (OAuth) | Medium | Balanced code + tooling workflows |
| `gpt-5.1-codex-high` | GPT 5.1 Codex High (OAuth) | High | Multi-step coding tasks with deep tool use |
| `gpt-5.1-codex-mini-medium` | GPT 5.1 Codex Mini Medium (OAuth) | Medium | Budget-friendly Codex runs (200k/100k tokens) |
| `gpt-5.1-codex-mini-high` | GPT 5.1 Codex Mini High (OAuth) | High | Cheaper Codex tier with maximum reasoning |
| `gpt-5.1-none` | GPT 5.1 None (OAuth) | None | Latency-sensitive chat/tasks using the new "no reasoning" mode |
| `gpt-5.1-none` | GPT 5.1 None (OAuth) | **None** | Latency-sensitive chat/tasks using the "no reasoning" mode |
| `gpt-5.1-low` | GPT 5.1 Low (OAuth) | Low | Fast general-purpose chat with light reasoning |
| `gpt-5.1-medium` | GPT 5.1 Medium (OAuth) | Medium | Default adaptive reasoning for everyday work |
| `gpt-5.1-high` | GPT 5.1 High (OAuth) | High | Deep analysis when reliability matters most |

> **Extra High reasoning:** `reasoningEffort: "xhigh"` is exclusive to `gpt-5.1-codex-max`. Other models automatically map that option to `high` so their API calls remain valid.
> **Extra High reasoning:** `reasoningEffort: "xhigh"` provides maximum computational effort for complex, multi-step problems and is exclusive to `gpt-5.1-codex-max`. Other models automatically map that option to `high` so their API calls remain valid.

#### Legacy GPT-5 lineup (still supported)

Expand Down Expand Up @@ -540,12 +540,14 @@ If you want to customize settings yourself, you can configure options at provide

| Setting | GPT-5 / GPT-5.1 Values | GPT-5-Codex / Codex Mini Values | Plugin Default |
|---------|-------------|-------------------|----------------|
| `reasoningEffort` | `none`, `minimal`, `low`, `medium`, `high` | `low`, `medium`, `high`, `xhigh`* | `medium` |
| `reasoningEffort` | `none`, `minimal`, `low`, `medium`, `high` | `low`, `medium`, `high`, `xhigh` | `medium` |
| `reasoningSummary` | `auto`, `detailed` | `auto`, `detailed` | `auto` |
| `textVerbosity` | `low`, `medium`, `high` | `medium` only | `medium` |
| `include` | Array of strings | Array of strings | `["reasoning.encrypted_content"]` |

> **Note**: `minimal` effort is auto-normalized to `low` for gpt-5-codex (not supported by the API). `none` is only supported on GPT-5.1 general models; when used with legacy gpt-5 it is normalized to `minimal`. `xhigh` is exclusive to `gpt-5.1-codex-max`—other Codex presets automatically map it to `high`.
>
> † **Extra High reasoning**: `reasoningEffort: "xhigh"` provides maximum computational effort for complex, multi-step problems and is only available on `gpt-5.1-codex-max`.

#### Plugin-Level Settings

Expand Down
4 changes: 2 additions & 2 deletions docs/development/CONFIG_FIELDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,8 @@ const parsedModel: ModelsDev.Model = {

**Why this matters:**
- Config keys mirror the Codex CLI's 5.1 presets, making it obvious which tier you're targeting.
- `reasoningEffort: "none"` is only valid for GPT-5.1 general models—the plugin automatically downgrades unsupported values for Codex/Codex Mini.
- `reasoningEffort: "xhigh"` is exclusive to `gpt-5.1-codex-max`; other models automatically clamp it to `high`.
- `reasoningEffort: "none"` (No Reasoning) disables reasoning entirely for latency-sensitive tasks and is only valid for GPT-5.1 general models—the plugin automatically downgrades unsupported values for Codex/Codex Mini.
- `reasoningEffort: "xhigh"` (Extra High) provides maximum computational effort for complex, multi-step problems and is exclusive to `gpt-5.1-codex-max`; other models automatically clamp it to `high`.
- Legacy GPT-5, GPT-5-Codex, and Codex Mini presets automatically clamp unsupported values (`none` → `minimal`/`low`, `minimal` → `low` for Codex).
- Mixing GPT-5.1 and GPT-5 presets inside the same config is fine—just keep config keys unique and let the plugin normalize them.

Expand Down
86 changes: 86 additions & 0 deletions docs/notes/2025.11.19.18.38.24.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
In lib/request/request-transformer.ts around lines 952 to 968, the
TransformResult interface is currently unexported which prevents consumers from
using the return type of transformRequestBody; change the declaration to export
interface TransformResult so it’s exported from the module, and update any local
references or imports elsewhere if needed to use the exported type (no other
logic changes).


In lib/request/request-transformer.ts around lines 621 to 633, the code
duplicates the bridge message object creation (the developer role message with
CODEX_OPENCODE_BRIDGE and input merging) which is repeated later at lines
~658-667; extract a small helper (e.g., buildBridgeMessage(input):
Array<Message> or getBridgeMessage(input): Message[]) that returns the array
with the bridge message followed by the existing input and replace both
duplicated branches with a call to that helper, keeping existing types and
imports and ensuring generateContentHash("add") checks still control whether to
return the helper result or the original input.


In lib/compaction/compaction-executor.ts around lines 24 to 66, wrap the
response.text() + JSON.parse(...) and subsequent payload manipulation in a
try/catch so non‑JSON or unexpected response shapes do not crash compaction; on
any parse or processing error, log or ignore the error and return the original
response object untouched. Ensure the catch block returns the original Response
(preserving status, statusText, headers, and body) so callers receive the
unmodified response when parsing fails.


In lib/compaction/codex-compaction.ts around lines 168 to 170, the cloneRange
function duplicates logic already implemented in lib/utils/clone.ts as
cloneInputItems; replace the local implementation by importing cloneInputItems
from 'lib/utils/clone' and call it where cloneRange is used (or rename uses to
cloneInputItems), remove the duplicate function, and ensure the import is added
and TypeScript types align with InputItem[].


In lib/compaction/codex-compaction.ts around lines 131 to 144, the
extractTextFromItem function duplicates logic already in
lib/utils/input-item-utils.ts; replace this local implementation by importing
and calling the centralized utility (ensuring the import path is correct), and
if needed adapt or wrap the utility call so behavior remains identical (handle
null/undefined input and array/object type checks the same way as the previous
local function). Remove the duplicated function, run type checks/TS compile and
unit tests to confirm no behavioral regressions.


lib/cache/cache-metrics.ts lines 34-53 (also apply similar changes at 59-79,
103-105, 167-185): the metrics object and API are tightened to prevent
accidental writes to the aggregate bucket but getMetrics currently performs only
a shallow clone so callers can still mutate nested CacheMetrics; update the
types to use keyof Omit<CacheMetricsCollection, "overall"> for per-key
operations (hits/misses/evictions) and ensure every place that updates rates
also recomputes and updates the "overall" hitRate consistently, and either
return a deep-cloned/read-only snapshot from getMetrics or clearly document the
return as read-only to prevent external mutation.

In lib/cache/cache-warming.ts around lines 113 to 126, the catch block declares
an unused named parameter (_error) causing lint/typecheck warnings; remove the
unused binding by changing the catch to a bare catch (i.e., catch { ... }) so
the error is still ignored and the function behavior remains identical while
satisfying the linter.

In lib/compaction/codex-compaction.ts around lines 131 to 144, the
extractTextFromItem function duplicates logic already in
lib/utils/input-item-utils.ts; replace this local implementation by importing
and calling the centralized utility (ensuring the import path is correct), and
if needed adapt or wrap the utility call so behavior remains identical (handle
null/undefined input and array/object type checks the same way as the previous
local function). Remove the duplicated function, run type checks/TS compile and
unit tests to confirm no behavioral regressions.

In lib/compaction/codex-compaction.ts around lines 168 to 170, the cloneRange
function duplicates logic already implemented in lib/utils/clone.ts as
cloneInputItems; replace the local implementation by importing cloneInputItems
from 'lib/utils/clone' and call it where cloneRange is used (or rename uses to
cloneInputItems), remove the duplicate function, and ensure the import is added
and TypeScript types align with InputItem[].

In lib/compaction/compaction-executor.ts around lines 24 to 66, wrap the
response.text() + JSON.parse(...) and subsequent payload manipulation in a
try/catch so non‑JSON or unexpected response shapes do not crash compaction; on
any parse or processing error, log or ignore the error and return the original
response object untouched. Ensure the catch block returns the original Response
(preserving status, statusText, headers, and body) so callers receive the
unmodified response when parsing fails.

84 changes: 84 additions & 0 deletions docs/reasoning-effort-levels-update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Reasoning Effort Levels Documentation Update

## Summary

Update documentation to clearly explain all available reasoning effort levels for the new Codex Max model, including `none`, `low`, `medium`, `high`, and `xhigh`.

## Current State

Based on codebase analysis:

### Already Implemented ✅
- `xhigh` reasoning effort is supported in code (`lib/types.ts:53`, `lib/request/request-transformer.ts:327`)
- Tests cover `xhigh` handling (`test/request-transformer.test.ts:141-162`)
- README.md mentions `xhigh` for Codex Max (`README.md:453,464,543,548`)
- Configuration files include proper reasoning levels
- AGENTS.md documents `xhigh` exclusivity to Codex Max

### Documentation Gaps Identified
1. README.md could be clearer about the complete range of reasoning levels
2. Need to ensure all reasoning levels (`none`, `low`, `medium`, `high`, `xhigh`) are clearly documented
3. Configuration examples should show the full spectrum

## Files to Update

### Primary Documentation
- `README.md` - Main user-facing documentation
- `docs/development/CONFIG_FIELDS.md` - Developer configuration reference

### Configuration Examples (Already Up-to-Date)
- `config/full-opencode.json` - Complete configuration with all reasoning levels
- `config/minimal-opencode.json` - Minimal configuration

## Definition of Done

- [x] All reasoning effort levels (`none`, `low`, `medium`, `high`, `xhigh`) are clearly documented
- [x] `xhigh` exclusivity to `gpt-5.1-codex-max` is clearly explained
- [x] Automatic downgrade behavior for unsupported models is documented
- [x] Configuration examples show the complete range of reasoning levels
- [x] Documentation is consistent across all files

## Implementation Notes

### Reasoning Effort Levels by Model Type

| Model Type | Supported Levels | Notes |
|------------|----------------|-------|
| `gpt-5.1-codex-max` | `low`, `medium`, `high`, `xhigh` | `xhigh` is exclusive to this model |
| `gpt-5.1-codex` | `low`, `medium`, `high` | `xhigh` auto-downgrades to `high` |
| `gpt-5.1-codex-mini` | `low`, `medium`, `high` | `xhigh` auto-downgrades to `high` |
| `gpt-5.1` (general) | `none`, `low`, `medium`, `high` | `none` only supported on general models |
| `gpt-5-codex` | `low`, `medium`, `high` | `minimal` auto-normalizes to `low` |
| `gpt-5` (legacy) | `minimal`, `low`, `medium`, `high` | `none` auto-normalizes to `minimal` |

### Automatic Normalization Rules

1. **`xhigh` handling**: Only allowed on `gpt-5.1-codex-max`, others downgrade to `high`
2. **`none` handling**: Only supported on GPT-5.1 general models, legacy gpt-5 normalizes to `minimal`
3. **`minimal` handling**: Normalizes to `low` for Codex models (not supported by API)

## Changes Made

### README.md Updates
- Enhanced reasoning effort documentation table
- Added clearer explanation of `xhigh` exclusivity
- Updated model variant descriptions to include reasoning level ranges
- Improved configuration examples section

### CONFIG_FIELDS.md Updates
- Added `xhigh` to the reasoning effort documentation
- Clarified which models support which levels
- Documented automatic normalization behavior

## Testing Verification

All reasoning effort levels are already tested in:
- `test/request-transformer.test.ts:141-162` - `xhigh` handling tests
- `test/request-transformer.test.ts:125-153` - Basic reasoning config tests
- Integration tests cover full configuration flow

## Impact

- **Users**: Clearer understanding of available reasoning levels and model capabilities
- **Developers**: Better documentation for configuration options
- **Support**: Reduced confusion about reasoning effort limitations per model
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export default [
"sonarjs/cognitive-complexity": ["error", 30],

// Function and file size limits (line counts ignore blank lines and comments)
"max-lines-per-function": ["warn", { max: 80, skipBlankLines: true, skipComments: true }],
"max-lines-per-function": ["warn", { max: 120, skipBlankLines: true, skipComments: true }],
"max-lines": ["warn", { max: 500, skipBlankLines: true, skipComments: true }],

// Rely on TypeScript for undefined/global checks
Expand Down
4 changes: 3 additions & 1 deletion lib/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ export function decodeJWT(token: string): JWTPayload | null {
const parts = token.split(".");
if (parts.length !== 3) return null;
const payload = parts[1];
const decoded = Buffer.from(payload, "base64").toString("utf-8");
const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
const decoded = Buffer.from(padded, "base64").toString("utf-8");
return JSON.parse(decoded) as JWTPayload;
} catch {
return null;
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function openBrowserUrl(url: string): void {
stdio: "ignore",
shell: process.platform === "win32",
});
} catch (_error) {
} catch {
// Silently fail - user can manually open the URL from instructions
}
}
25 changes: 14 additions & 11 deletions lib/auth/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,34 @@ const successHtml = fs.readFileSync(path.join(__dirname, "..", "oauth-success.ht
*/
export function startLocalOAuthServer({ state }: { state: string }): Promise<OAuthServerInfo> {
const server = http.createServer((req, res) => {
const send = (status: number, message: string, headers?: http.OutgoingHttpHeaders) => {
const finalHeaders = {
"Content-Type": "text/plain; charset=utf-8",
...headers,
};
res.writeHead(status, finalHeaders);
res.end(message);
};

try {
const url = new URL(req.url || "", "http://localhost");
if (url.pathname !== "/auth/callback") {
res.statusCode = 404;
res.end("Not found");
send(404, "Not found");
return;
}
if (url.searchParams.get("state") !== state) {
res.statusCode = 400;
res.end("State mismatch");
send(400, "State mismatch");
return;
}
const code = url.searchParams.get("code");
if (!code) {
res.statusCode = 400;
res.end("Missing authorization code");
send(400, "Missing authorization code");
return;
}
res.statusCode = 200;
res.setHeader("Content-Type", "text/html; charset=utf-8");
res.end(successHtml);
send(200, successHtml, { "Content-Type": "text/html; charset=utf-8" });
(server as http.Server & { _lastCode?: string })._lastCode = code;
} catch {
res.statusCode = 500;
res.end("Internal error");
send(500, "Internal error");
}
});

Expand Down
12 changes: 11 additions & 1 deletion lib/cache/cache-metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,22 @@ class CacheMetricsCollector {
metrics.hitRate = metrics.totalRequests > 0 ? (metrics.hits / metrics.totalRequests) * 100 : 0;
}

private cloneMetrics(): CacheMetricsCollection {
const cloneMetric = (metric: CacheMetrics): CacheMetrics => ({ ...metric });
return {
codexInstructions: cloneMetric(this.metrics.codexInstructions),
opencodePrompt: cloneMetric(this.metrics.opencodePrompt),
bridgeDecisions: cloneMetric(this.metrics.bridgeDecisions),
overall: cloneMetric(this.metrics.overall),
};
}

/**
* Get current metrics
* @returns Complete metrics collection
*/
getMetrics(): CacheMetricsCollection {
return { ...this.metrics };
return this.cloneMetrics();
}

/**
Expand Down
2 changes: 1 addition & 1 deletion lib/cache/cache-warming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export async function areCachesWarm(): Promise<boolean> {

// If both caches have valid entries, they are warm
return !!(codexEntry && opencodeEntry);
} catch (_error) {
} catch {
// Any error suggests caches are not warm
return false;
}
Expand Down
4 changes: 1 addition & 3 deletions lib/cache/session-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@ export function createSessionCache<T>(ttlMs = 15 * 60 * 1000): SessionCache<T> {
}
};

const getSize = (): number => {
return cache.size;
};
const getSize = (): number => cache.size;

return { get, set, clear, clean, getSize };
}
Expand Down
Loading
Loading