From c13ca33be70236bea35145ec59f38d6c70552375 Mon Sep 17 00:00:00 2001 From: Sterling Date: Sat, 21 Mar 2026 20:22:17 -0400 Subject: [PATCH 1/4] chore: open PR for P120 codex-proxy build From 6a54d8b1cb5e3721931c25916df4e6fa01f13b68 Mon Sep 17 00:00:00 2001 From: Sterling Date: Sun, 22 Mar 2026 09:53:46 -0400 Subject: [PATCH 2/4] Fix emitDelta return guard and onTokenUsageUpdated slot leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug 1: add return after triggerCleanup in emitDelta when accumulatedSize exceeds maxSize — without it, subsequent deltas still accumulate content after the error cleanup is triggered. Bug 2: call triggerCleanup after sendNonStreamingResponse in onTokenUsageUpdated grace period path — slot was never released, in-flight entry was never removed, archive never ran. Co-Authored-By: Claude Sonnet 4.6 --- src/client/app-server.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/client/app-server.ts b/src/client/app-server.ts index c4b8664..afb0201 100644 --- a/src/client/app-server.ts +++ b/src/client/app-server.ts @@ -608,6 +608,7 @@ export class AppServerClient { message: 'Response too large', errorType: 'server_error', }); + return; } } } @@ -661,8 +662,9 @@ export class AppServerClient { if (!inflight.stream && inflight.gracePeriodTimer) { clearTimeout(inflight.gracePeriodTimer); inflight.gracePeriodTimer = null; - // Send the non-streaming response now + // Send the non-streaming response now, then release the slot and archive this.sendNonStreamingResponse(inflight); + this.triggerCleanup(inflight, { type: 'success' }); } } From d20289500f98562f65e90ce5275faed31868381e Mon Sep 17 00:00:00 2001 From: Sterling Date: Sun, 22 Mar 2026 10:05:25 -0400 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20M1-11=20=E2=80=94=20delta=20turnId?= =?UTF-8?q?=20not=20used=20to=20resolve=20buffer=20(app-server.ts)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In onAgentMessageDelta, destructured turnId was never applied to inflight.turnId. Spec requires: delta notifications themselves carry turnId and must set inflight.turnId if not already set, triggering buffer replay. Without this, the race window where deltas arrive before both turn/start response and turn/started notification leaves the buffer unresolved until turn/started eventually arrives. Fix: if !inflight.turnId && turnId, set inflight.turnId = turnId and call flushDeltaBuffer before falling through to emit current delta. Co-Authored-By: Claude Sonnet 4.6 --- src/client/app-server.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/client/app-server.ts b/src/client/app-server.ts index afb0201..b195871 100644 --- a/src/client/app-server.ts +++ b/src/client/app-server.ts @@ -493,7 +493,17 @@ export class AppServerClient { } if (inflight.cleanupDone) return; - // If turnId not yet set, buffer the delta + // If turnId not yet set, try to resolve from delta params before buffering + if (!inflight.turnId) { + if (turnId) { + // Delta carries turnId — resolve it now and flush buffer + inflight.turnId = turnId; + this.flushDeltaBuffer(inflight); + // Fall through to emit current delta normally (turnId is now set) + } + } + + // If turnId still not set after attempting resolution, buffer the delta if (!inflight.turnId) { // Check buffer limits inflight.deltaBufferSize += delta.length; From ce94433b0916540fabd4c932cb59d3b83b77378d Mon Sep 17 00:00:00 2001 From: Sterling Date: Sun, 22 Mar 2026 22:10:11 -0400 Subject: [PATCH 4/4] Fix TINFOIL rejections: env-var injection guard + localhost CORS (PROJECT-SPEC.md) --- scripts/codex-auth-check.sh | 12 ++++++------ src/server/routes.ts | 10 ++++++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/scripts/codex-auth-check.sh b/scripts/codex-auth-check.sh index a219e62..5bc9f27 100755 --- a/scripts/codex-auth-check.sh +++ b/scripts/codex-auth-check.sh @@ -20,10 +20,10 @@ fi # ─── Extract access token ───────────────────────────────────────────────────── -ACCESS_TOKEN=$(python3 -c " -import json, sys +ACCESS_TOKEN=$(AUTH_FILE="${AUTH_FILE}" python3 -c " +import json, sys, os try: - data = json.load(open('${AUTH_FILE}')) + data = json.load(open(os.environ['AUTH_FILE'])) token = data.get('accessToken') or data.get('access_token') or data.get('token') if not token: print('', end='') @@ -43,10 +43,10 @@ fi # ─── Decode JWT exp claim ───────────────────────────────────────────────────── -EXP=$(python3 -c " -import base64, json, sys +EXP=$(ACCESS_TOKEN="${ACCESS_TOKEN}" python3 -c " +import base64, json, sys, os -token = '${ACCESS_TOKEN}' +token = os.environ['ACCESS_TOKEN'] parts = token.split('.') if len(parts) != 3: print(-1) diff --git a/src/server/routes.ts b/src/server/routes.ts index 0221f78..d0a04b5 100644 --- a/src/server/routes.ts +++ b/src/server/routes.ts @@ -138,8 +138,14 @@ export function buildRouter(client: AppServerClient): Router { router.use(limiter); // ─── CORS ────────────────────────────────────────────────────────────────── - router.use((_req: Request, res: Response, next: NextFunction) => { - res.setHeader('Access-Control-Allow-Origin', '*'); + // Restrict to localhost origins only — this proxy is local-only + router.use((req: Request, res: Response, next: NextFunction) => { + const origin = req.headers.origin; + if (origin && /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(origin)) { + res.setHeader('Access-Control-Allow-Origin', origin); + } else { + res.setHeader('Access-Control-Allow-Origin', 'http://localhost'); + } res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type'); next();