From edde928736cf92ef44009f22df2301d2c26c214c Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Sun, 12 Jul 2026 20:47:01 -0700 Subject: [PATCH] Capture Codex worker token usage Record per-turn app-server token notifications so Live architecture benchmarks can compare context and cache costs.\n\nAI-assisted: OpenAI Codex. --- skill/scripts/live/codex-app-server-client.mjs | 5 +++++ tests/live-codex-app-server-client.test.mjs | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/skill/scripts/live/codex-app-server-client.mjs b/skill/scripts/live/codex-app-server-client.mjs index de16395be..a75f7ca70 100644 --- a/skill/scripts/live/codex-app-server-client.mjs +++ b/skill/scripts/live/codex-app-server-client.mjs @@ -384,6 +384,7 @@ export class CodexAppServerClient { let turnId = null; let started = null; let completed = null; + let tokenUsage = null; const agentMessages = []; const agentMessageCallbacks = []; let firstAgentMessageAt = null; @@ -407,6 +408,9 @@ export class CodexAppServerClient { } if (notificationTurnId !== turnId) return; if (notification.method === 'turn/started') started = notification; + if (notification.method === 'thread/tokenUsage/updated') { + tokenUsage = notification.params?.tokenUsage || tokenUsage; + } if (notification.method === 'item/completed' && notification.params?.item?.type === 'agentMessage' && typeof notification.params.item.text === 'string') { @@ -456,6 +460,7 @@ export class CodexAppServerClient { startResponse: result, started, completed, + tokenUsage, status: completed?.params?.turn?.status || result.turn?.status || null, agentMessages, message: agentMessages.at(-1) || null, diff --git a/tests/live-codex-app-server-client.test.mjs b/tests/live-codex-app-server-client.test.mjs index d9729674a..d0547ddc5 100644 --- a/tests/live-codex-app-server-client.test.mjs +++ b/tests/live-codex-app-server-client.test.mjs @@ -294,6 +294,16 @@ describe('dedicated Codex worker threads', () => { method: 'item/completed', params: { ...common, item: { type: 'agentMessage', text: 'final answer' } }, }); + process.send({ + method: 'thread/tokenUsage/updated', + params: { + ...common, + tokenUsage: { + last: { inputTokens: 100, cachedInputTokens: 60, outputTokens: 20, reasoningOutputTokens: 4, totalTokens: 124 }, + total: { inputTokens: 100, cachedInputTokens: 60, outputTokens: 20, reasoningOutputTokens: 4, totalTokens: 124 }, + }, + }, + }); process.send({ method: 'turn/completed', params: { threadId: 'worker', turn: { id: 'turn-1', status: 'completed' } }, @@ -321,6 +331,7 @@ describe('dedicated Codex worker threads', () => { assert.deepEqual(deliveredMessages, ['first fragment', 'final answer']); assert.equal(result.message, 'final answer'); assert.equal(result.firstAgentMessageMs >= 0, true); + assert.equal(result.tokenUsage.last.inputTokens, 100); assert.equal(result.started.method, 'turn/started'); assert.equal(result.durationMs > 0, true); await client.close();