fix(codex,db): resolve 6 issues — Codex 502, store default, migration guards

Fixes:
- fix(codex): rename getWreqWebsocket() → getCodexWebSocketTransport()
  Fixes the ReferenceError causing 502 on all Codex requests (#1652, #1653)

- fix(codex): default store to false instead of true
  Codex OAuth backend rejects store=true with 'Store must be set to false' (#1635)

- fix(db): add post-migration startup guards for combos.sort_order (#1657)
  and batches/files tables (#1648) — handles heuristic seeding edge case

- fix(db): renumber duplicate migration 032_create_reasoning_cache → 033

Closes #1635, #1648, #1652, #1653, #1657
Also closed as user-config: #1649 (Claude 429), #1659 (thought_signature)
This commit is contained in:
diegosouzapw
2026-04-27 07:39:12 -03:00
parent 6dd883e5f4
commit 6747e22757
4 changed files with 130 additions and 8 deletions

View File

@@ -4,6 +4,32 @@
---
## [3.7.2] — 2026-04-27
### ✨ New Features
- **feat(authz):** introduce centralized proxy-based authz pipeline and lifecycle policy (#1632)
- **feat(logs):** configure call log pipeline artifacts (#1650)
- **feat(network):** add guarded remote image fetch utility
- **feat(codex):** enable native Codex websocket responses on beta-gated models (#1658)
### 🐛 Bug Fixes
- **fix(chatgpt-web):** bound tls-client native deadlocks so requests never hang forever (#1664)
- **fix(codex):** default gpt-5.5 to HTTP transport instead of WebSocket (#1660)
- **fix(codex):** [urgent] fix gpt-5.5 websocket transport and model labels (#1656)
- **fix(grokweb):** update Request and Response Specifications (#1655)
- **fix(blackbox-web):** set isPremium flag to true to enable premium model access (#1661)
- **fix(core):** avoid OpenAI stream options for Anthropic-compatible providers (#1654)
- **fix(electron):** resolve MCP server start failure on Windows (#1662)
- **fix(electron):** make Windows smoke test non-blocking (continue-on-error), pre-create userData dir for Windows + stream logs in CI, and add --no-sandbox and sandbox env for CI smoke tests
- **fix(codex):** fix `getWreqWebsocket` ReferenceError causing 502 on all Codex requests (#1652, #1653)
- **fix(codex):** default `store` to `false` — Codex OAuth backend rejects `store=true` (#1635)
- **fix(db):** add post-migration guards for missing `batches` table and `combos.sort_order` column on DB upgrades (#1648, #1657)
- **fix(db):** renumber duplicate migration `032` to prevent collision
---
## [3.7.1] — 2026-04-26
### ✨ New Features

View File

@@ -526,7 +526,7 @@ export function isCodexResponsesWebSocketRequired(_model: string, credentials: u
credentials && typeof credentials === "object"
? (credentials as { providerSpecificData?: Record<string, unknown> }).providerSpecificData
: null;
return !!(providerSpecificData?.codexTransport === "websocket" && getWreqWebsocket());
return !!(providerSpecificData?.codexTransport === "websocket" && getCodexWebSocketTransport());
}
function toStatusCode(value: unknown): number | null {
@@ -1004,20 +1004,21 @@ export class CodexExecutor extends BaseExecutor {
}
}
// Store: The Codex API defaults store to false when not specified.
// Proxy clients (e.g. OpenClaw) rely on response chaining via previous_response_id,
// which requires store=true so that response items are persisted.
// If the client explicitly sets store, respect it. Otherwise default to true.
// Store: The Codex OAuth backend rejects store=true with
// "Store must be set to false". Default to false unless the provider
// explicitly opts in (e.g. API-key accounts that support persistence).
// Ref: sub2api openai_codex_transform.go line 75-80
const explicitStoreSetting =
credentials?.providerSpecificData &&
typeof credentials.providerSpecificData === "object" &&
!Array.isArray(credentials.providerSpecificData)
? credentials.providerSpecificData.openaiStoreEnabled
: undefined;
if (explicitStoreSetting === false) {
body.store = false;
} else if (body.store === undefined) {
if (explicitStoreSetting === true) {
body.store = true;
} else {
// backend rejects store=true ("Store must be set to false"), so default to false.
body.store = false;
}
// Codex Responses only supports function tools with non-empty names.

View File

@@ -1250,6 +1250,101 @@ export function getDbInstance(): SqliteDatabase {
);
}
runMigrations(db, { isNewDb });
// ── Post-migration safety guards ──────────────────────────────────────────
// The heuristic seeding above can mark migration versions as "applied" based
// on column detection, causing the migration runner to skip newer migrations
// whose tables/columns don't have heuristic detectors yet.
// These guards ensure critical schema elements exist regardless of migration
// state, fixing upgrade failures reported in #1648 and #1657.
// Guard: combos.sort_order (migration 020)
if (hasTable(db, "combos") && !hasColumn(db, "combos", "sort_order")) {
try {
db.exec(`
ALTER TABLE combos ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0;
WITH ordered_combos AS (
SELECT id, ROW_NUMBER() OVER (
ORDER BY created_at ASC, updated_at ASC, name COLLATE NOCASE ASC
) AS next_sort_order
FROM combos
)
UPDATE combos SET sort_order = (
SELECT next_sort_order FROM ordered_combos
WHERE ordered_combos.id = combos.id
);
`);
console.log("[DB] Post-migration guard: added missing combos.sort_order column (#1657)");
} catch (e: unknown) {
const msg = e instanceof Error ? e.message : String(e);
if (!msg.includes("duplicate column")) {
console.warn("[DB] Post-migration guard: combos.sort_order failed:", msg);
}
}
}
// Guard: batches table (migration 028)
if (!hasTable(db, "batches")) {
try {
db.exec(`
CREATE TABLE IF NOT EXISTS files (
id TEXT PRIMARY KEY,
bytes INTEGER NOT NULL,
created_at INTEGER NOT NULL,
filename TEXT NOT NULL,
purpose TEXT NOT NULL,
content BLOB,
mime_type TEXT,
api_key_id TEXT,
deleted_at INTEGER,
status TEXT DEFAULT 'validating',
expires_at INTEGER
);
CREATE INDEX IF NOT EXISTS idx_files_api_key ON files(api_key_id);
CREATE TABLE IF NOT EXISTS batches (
id TEXT PRIMARY KEY,
endpoint TEXT NOT NULL,
completion_window TEXT NOT NULL,
status TEXT NOT NULL,
input_file_id TEXT NOT NULL,
output_file_id TEXT,
error_file_id TEXT,
created_at INTEGER NOT NULL,
in_progress_at INTEGER,
expires_at INTEGER,
finalizing_at INTEGER,
completed_at INTEGER,
failed_at INTEGER,
expired_at INTEGER,
cancelling_at INTEGER,
cancelled_at INTEGER,
request_counts_total INTEGER DEFAULT 0,
request_counts_completed INTEGER DEFAULT 0,
request_counts_failed INTEGER DEFAULT 0,
metadata TEXT,
api_key_id TEXT,
errors TEXT,
model TEXT,
usage TEXT,
output_expires_after_seconds INTEGER,
output_expires_after_anchor TEXT,
FOREIGN KEY(input_file_id) REFERENCES files(id),
FOREIGN KEY(output_file_id) REFERENCES files(id),
FOREIGN KEY(error_file_id) REFERENCES files(id)
);
CREATE INDEX IF NOT EXISTS idx_batches_api_key ON batches(api_key_id);
CREATE INDEX IF NOT EXISTS idx_batches_status ON batches(status);
`);
console.log("[DB] Post-migration guard: created missing batches/files tables (#1648)");
} catch (e: unknown) {
const msg = e instanceof Error ? e.message : String(e);
if (!msg.includes("already exists")) {
console.warn("[DB] Post-migration guard: batches/files creation failed:", msg);
}
}
}
offloadLegacyCallLogDetails(db);
// Auto-migrate from db.json if exists