Conversation
|
Caution Review failedThe pull request is closed. Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughJWT decoding now accepts URL-safe base64 with padding; codex-metrics moved to structured SSE streaming; cache metrics now return cloned objects; request transformation threads sessionContext and can inject session prompt_cache_key; session-manager adds prefix-aware forking and smarter session lookup; config loading gains memoized cache with forceReload; assorted tests and small refactors updated. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant Cmd as codex-metrics
participant SSE as SSE Helpers
participant Stream as ResponseStream
Client->>Cmd: createStaticResponse(req)
Cmd->>Cmd: buildAssistantMessage(...)
Cmd->>Cmd: buildResponsePayload(...)
Cmd->>SSE: buildSseEvents(payload)
SSE->>SSE: createSsePayload(events)
SSE-->>Stream: stream SSE chunks
Stream-->>Client: streaming SSE response
sequenceDiagram
autonumber
actor Caller
participant Fetch as fetch-helpers
participant Transformer as request-transformer
participant Session
Caller->>Fetch: prepare request (with optional sessionContext)
Fetch->>Transformer: transformRequestBody(init, body, sessionContext)
Transformer->>Session: consult/inject prompt_cache_key, bridge message, compaction
Transformer-->>Fetch: TransformResult { body, updatedInit, sessionContext }
Fetch-->>Caller: serialized updatedInit (ready to fetch)
sequenceDiagram
autonumber
participant Executor as compaction-executor
participant Orig as originalResponse
participant Clone as clonedResponse
participant Session
Executor->>Orig: resp.clone()
Orig-->>Clone: cloned response body
Executor->>Executor: try parse & compute compaction summary
alt success
Executor->>Session: apply compaction summary
Executor->>Executor: return new Response(updatedPayload, status, headers)
else error
Executor->>Executor: catch -> return original response unchanged
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related issues
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (3)
Comment |
…codex into chore/codex-max-release-review
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
lib/session/session-manager.ts (1)
22-51: Consider renaming function to reflect assistant message inclusion.The function name
extractLatestUserSliceis now misleading since it includes both user and assistant messages in the tail. Consider renaming to something likeextractConversationTailorextractLatestUserAssistantSliceto better reflect the current behavior.test/README.md (1)
35-55: Doc updates look good; consider refreshing the test file listThe per-file headings and timing language are now more maintainable and realistic. As a follow-up (non-blocking), it may be worth updating the “Test Structure” tree to include newer suites like
fetch-helpers.test.tsandrequest-transformer-tools-normalization.test.tsso the overview matches the actual test layout.lib/cache/cache-metrics.ts (1)
234-261: Consider gating recommendations on sufficient trafficThe
getCachePerformanceReport()heuristics will recommend increasing TTL even whentotalRequestsis near zero (hit rate 0% but not yet meaningful). You might make the “increase cache TTL” suggestion conditional on a minimumtotalRequeststhreshold (similar to the low-usage check) to avoid noisy guidance on fresh or rarely used instances.lib/commands/codex-metrics.ts (1)
164-261: Optional: align events more closely with full Responses SSE patternsThe sequence of events (
response.created→ singleresponse.output_text.delta→ item added/done →response.completed) is sufficient for these commands. If downstream consumers ever rely onresponse.output_text.doneevents (as they often do with standard Responses streams), you might consider emitting that as a final text event for closer parity, but it’s not a blocker given the current usage.lib/request/request-transformer.ts (1)
1014-1044: Blocker: cache-key logging insidetransformRequestBodypushes cognitive complexity over limitThe new
isNewSession-aware logging for generated prompt cache keys is functionally sound, but it contributes to the SonarJS cognitive-complexity error ontransformRequestBody(reported as 34 > 30). This will keep CI red unless you either adjust lint rules or refactor.A low-risk way to bring complexity down is to extract the cache-key logging into a dedicated helper that takes the
PromptCacheKeyResultandsessionContext, and then call it fromtransformRequestBody. That keeps behaviour identical while simplifying the main function.For example:
@@ -function ensurePromptCacheKey(body: RequestBody): PromptCacheKeyResult { +function ensurePromptCacheKey(body: RequestBody): PromptCacheKeyResult { … } + +function logPromptCacheKeyHandling( + result: PromptCacheKeyResult, + sessionContext?: SessionContext, +): void { + const isNewSession = Boolean(sessionContext?.isNew); + + if (result.source === "existing") { + return; + } + + if (result.source === "metadata") { + logDebug("Prompt cache key missing; derived from metadata", { + promptCacheKey: result.key, + sourceKey: result.sourceKey, + forkSourceKey: result.forkSourceKey, + forkHintKeys: result.forkHintKeys, + }); + return; + } + + if (result.source === "generated") { + const hasHints = Boolean( + (result.hintKeys && result.hintKeys.length > 0) || + (result.forkHintKeys && result.forkHintKeys.length > 0), + ); + const message = hasHints + ? "Prompt cache key hints detected but unusable; generated fallback cache key" + : "Prompt cache key missing; generated fallback cache key"; + const logPayload = { + promptCacheKey: result.key, + fallbackHash: result.fallbackHash, + hintKeys: result.hintKeys, + unusableKeys: result.unusableKeys, + forkHintKeys: result.forkHintKeys, + forkUnusableKeys: result.forkUnusableKeys, + }; + + if (!hasHints && isNewSession) { + logInfo(message, logPayload); + } else { + logWarn(message, logPayload); + } + } +} @@ - // Ensure prompt_cache_key is set using our robust logic - const cacheKeyResult = ensurePromptCacheKey(body); - const isNewSession = Boolean(sessionContext?.isNew); - if (cacheKeyResult.source === "existing") { - // Host provided a valid cache key, use it as-is - } else if (cacheKeyResult.source === "metadata") { - logDebug("Prompt cache key missing; derived from metadata", { - promptCacheKey: cacheKeyResult.key, - sourceKey: cacheKeyResult.sourceKey, - forkSourceKey: cacheKeyResult.forkSourceKey, - forkHintKeys: cacheKeyResult.forkHintKeys, - }); - } else if (cacheKeyResult.source === "generated") { - const hasHints = Boolean( - (cacheKeyResult.hintKeys && cacheKeyResult.hintKeys.length > 0) || - (cacheKeyResult.forkHintKeys && cacheKeyResult.forkHintKeys.length > 0), - ); - const message = hasHints - ? "Prompt cache key hints detected but unusable; generated fallback cache key" - : "Prompt cache key missing; generated fallback cache key"; - const logPayload = { - promptCacheKey: cacheKeyResult.key, - fallbackHash: cacheKeyResult.fallbackHash, - hintKeys: cacheKeyResult.hintKeys, - unusableKeys: cacheKeyResult.unusableKeys, - forkHintKeys: cacheKeyResult.forkHintKeys, - forkUnusableKeys: cacheKeyResult.forkUnusableKeys, - }; - if (!hasHints && isNewSession) { - logInfo(message, logPayload); - } else { - logWarn(message, logPayload); - } - } + // Ensure prompt_cache_key is set using our robust logic + const cacheKeyResult = ensurePromptCacheKey(body); + logPromptCacheKeyHandling(cacheKeyResult, sessionContext);This should reduce
transformRequestBody’s complexity and clear the SonarJS blocker while preserving your new info-vs-warn behaviour for new sessions.lib/request/fetch-helpers.ts (1)
20-21: Remove unusedInputItemimport to satisfy lint
InputItemis imported from../types.jsbut never used in this module, which triggers the@typescript-eslint/no-unused-varswarning.You can fix it by dropping the unused type:
-import type { InputItem, PluginConfig, RequestBody, SessionContext, UserConfig } from "../types.js"; +import type { PluginConfig, RequestBody, SessionContext, UserConfig } from "../types.js";
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (2)
docs/notes/2025.11.19.18.38.24.mdis excluded by none and included by nonespec/prompt-cache-warning.mdis excluded by none and included by none
📒 Files selected for processing (15)
lib/auth/auth.ts(1 hunks)lib/cache/cache-metrics.ts(1 hunks)lib/cache/cache-warming.ts(1 hunks)lib/commands/codex-metrics.ts(3 hunks)lib/compaction/codex-compaction.ts(2 hunks)lib/compaction/compaction-executor.ts(1 hunks)lib/request/fetch-helpers.ts(2 hunks)lib/request/request-transformer.ts(7 hunks)lib/session/session-manager.ts(2 hunks)test/README.md(6 hunks)test/auth.test.ts(1 hunks)test/config.test.ts(1 hunks)test/fetch-helpers.test.ts(1 hunks)test/request-transformer-tools-normalization.test.ts(1 hunks)test/request-transformer.test.ts(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
test/request-transformer-tools-normalization.test.ts (1)
lib/request/request-transformer.ts (1)
TransformRequestOptions(940-951)
test/auth.test.ts (1)
lib/auth/auth.ts (1)
decodeJWT(107-119)
lib/session/session-manager.ts (3)
lib/utils/input-item-utils.ts (2)
isUserMessage(80-82)isAssistantMessage(89-91)lib/types.ts (1)
InputItem(136-142)lib/utils/clone.ts (1)
cloneInputItems(30-35)
lib/request/request-transformer.ts (4)
lib/types.ts (1)
InputItem(136-142)lib/prompts/codex-opencode-bridge.ts (1)
CODEX_OPENCODE_BRIDGE(11-125)lib/cache/prompt-fingerprinting.ts (1)
generateContentHash(15-17)lib/logger.ts (2)
logInfo(92-94)logWarn(96-98)
lib/compaction/codex-compaction.ts (1)
lib/utils/clone.ts (1)
cloneInputItems(30-35)
lib/compaction/compaction-executor.ts (1)
lib/compaction/codex-compaction.ts (1)
createSummaryMessage(108-118)
test/request-transformer.test.ts (2)
lib/types.ts (2)
RequestBody(147-169)SessionContext(199-205)lib/request/request-transformer.ts (1)
transformRequestBody(958-1125)
test/config.test.ts (1)
lib/request/request-transformer.ts (1)
getReasoningConfig(290-358)
lib/request/fetch-helpers.ts (1)
lib/request/request-transformer.ts (1)
transformRequestBody(958-1125)
🪛 GitHub Actions: CI
lib/request/request-transformer.ts
[warning] 91-91: Function 'normalizeToolsForResponses' has too many lines (114). Maximum allowed is 80 max-lines-per-function
[warning] 106-106: Unexpected block statement surrounding arrow body; move the returned value immediately after the => arrow-body-style
[warning] 137-137: Arrow function has a complexity of 32. Maximum allowed is 20 complexity
[warning] 290-290: Function 'getReasoningConfig' has a complexity of 39. Maximum allowed is 20 complexity
[warning] 461-461: Async function 'filterOpenCodeSystemPrompts' has too many lines (84). Maximum allowed is 80 max-lines-per-function
[warning] 654-654: Assignment to property of function parameter 'sessionContext' no-param-reassign
[warning] 694-694: File has too many lines (851). Maximum allowed is 500 max-lines
[warning] 893-893: Assignment to property of function parameter 'body' no-param-reassign
[warning] 905-905: Assignment to property of function parameter 'body' no-param-reassign
[warning] 919-919: Assignment to property of function parameter 'body' no-param-reassign
[warning] 958-958: Async function 'transformRequestBody' has too many lines (127). Maximum allowed is 80 max-lines-per-function
[warning] 958-958: Async function 'transformRequestBody' has a complexity of 38. Maximum allowed is 20 complexity
[error] 958-958: Refactor this function to reduce its Cognitive Complexity from 34 to the 30 allowed sonarjs/cognitive-complexity
lib/request/fetch-helpers.ts
[warning] 20-20: 'InputItem' is defined but never used. Allowed unused vars must match /^_/u @typescript-eslint/no-unused-vars
[warning] 70-70: Assignment to property of function parameter 'currentAuth' no-param-reassign
[warning] 71-71: Assignment to property of function parameter 'currentAuth' no-param-reassign
[warning] 72-72: Assignment to property of function parameter 'currentAuth' no-param-reassign
[warning] 107-107: Async function 'transformRequestForCodex' has too many lines (91). Maximum allowed is 80 max-lines-per-function
[warning] 107-107: Async function 'transformRequestForCodex' has a complexity of 23. Maximum allowed is 20 complexity
[warning] 247-247: Async function 'handleErrorResponse' has a complexity of 21. Maximum allowed is 20 complexity
🔇 Additional comments (22)
lib/compaction/codex-compaction.ts (2)
3-4: LGTM: Clean consolidation of shared utilities.The imports properly bring in centralized helper functions for cloning and text extraction, improving code maintainability and reducing duplication.
120-130: LGTM: Proper use of shared cloning utility.The refactor correctly delegates to
cloneInputItemsfor deep cloning the tail slice, maintaining immutability while consolidating clone logic in a shared helper.lib/compaction/compaction-executor.ts (4)
30-30: LGTM: Correct response cloning pattern.Cloning the response before consumption enables clean fallback to the original response in the error path.
32-35: LGTM: Proper error handling for response parsing.The try-catch structure safely guards against JSON parsing failures and uses the cloned response, preserving the original for fallback.
46-61: LGTM: Safe metadata merging and session updates.The metadata spread preserves existing fields while adding compaction details. Session updates are properly guarded by checking both manager and context availability.
63-71: Silent error handling provides clean fallback.The reconstruction properly preserves the original response's headers, status, and statusText. The catch block silently returns the original response, which ensures availability but may hide processing errors.
Consider whether error logging would be valuable here for debugging compaction failures, or if the silent fallback is intentional for resilience.
lib/session/session-manager.ts (3)
6-6: Import addition looks correct.The
isAssistantMessageimport is properly added to support the new tail construction logic.
27-34: Last user message search logic is correct.The backward iteration correctly identifies the last user message index and exits early once found.
50-50: Clone usage is appropriate.Using
cloneInputItemsensures the returned tail is a deep copy, preventing unintended mutations of the original array.lib/cache/cache-warming.ts (1)
113-125: Parameterless catch keeps behavior simple and correctDropping the unused error variable is fine here; the function still safely treats any failure as “caches not warm” without changing semantics.
test/request-transformer-tools-normalization.test.ts (1)
6-15: Defaultingoptionsto{}is a good robustness improvementAlways passing a concrete
TransformRequestOptionsobject intotransformRequestBodysimplifies callers and avoids undefined checks downstream, with no change to existing call sites.test/auth.test.ts (1)
79-118: New base64url JWT test appropriately covers the updated decoderThis case correctly constructs a padding-stripped base64url payload and verifies
decodeJWTreturns the original object, matching the new normalization + padding behavior.test/config.test.ts (1)
133-143: Additional reasoning-config tests align with production logicThese cases accurately reflect
getReasoningConfigbehavior forgpt-5.1(defaulting tonone) andgpt-5.1-codex(normalizingnonetolow), strengthening coverage around the new model family.lib/auth/auth.ts (1)
107-118: JWT decoder now correctly supports base64url payloadsNormalizing
-/_and padding to a multiple of 4 beforeBuffer.from(..., "base64")makesdecodeJWTrobust to both standard and URL-safe encodings without changing the null-on-error contract.test/README.md (1)
88-90: Reworded performance guarantee is appropriate for a growing suiteShifting from a strict
< 250msguarantee to “designed to stay quick; actual timings may vary” better reflects reality as coverage expands, without losing the intent to keep tests fast.lib/cache/cache-metrics.ts (1)
99-115: Returning cloned metrics is a solid immutability improvementUsing
cloneMetrics()ingetMetrics()prevents callers from accidentally mutating internal counters while keeping the implementation simple and type-safe for the current primitive-only shape.lib/commands/codex-metrics.ts (1)
126-162: SSE-style static response construction is coherent and self-containedThe refactor cleanly factors out message, payload, and event construction and returns a text/event-stream
Responsethat should be easy for the existing response handler to consume, while keeping metadata (command name, ids, timestamps) explicit.lib/request/request-transformer.ts (2)
599-605: Bridge helper refactor looks correct and keeps behaviour consistentThe new
buildBridgeMessage()helper cleanly centralizes the bridgeInputItemconstruction, and both the cached-decision path and the main injection path now share the same implementation. That reduces duplication and makes future changes to the bridge message safer.No functional issues spotted here.
Also applies to: 632-633, 657-658
953-956: ExportingTransformResultis appropriate and keeps types alignedMaking
TransformResultan exported interface matches howtransformRequestBodyis now consumed (e.g., bytransformRequestForCodex) and keeps type information explicit for bothbodyandcompactionDecision. This is a straightforward, correct change.test/fetch-helpers.test.ts (1)
295-300: Nice regression test for immutability ofcompaction.originalInputThe added expectations that
compaction.originalInputis a distinct clone and remains"hello"after mutatingbody.input[0].contentare exactly what we need to guard against accidental in-place mutations in the compaction flow. This lines up with the newcloneInputItemsusage intransformRequestForCodex.lib/request/fetch-helpers.ts (1)
126-137: Compaction + session context wiring intotransformRequestBodylooks soundCloning
originalInputviacloneInputItems, derivingmanualCommandfrom that clone, and passing both compaction settings andsessionContextthrough totransformRequestBodymatches the transformer’s new signature and keeps original user input insulated from later mutations or history application.The conditional
applyCompactedHistorycall before transformation is also guarded correctly bycompactionEnabled && !manualCommand, so behaviour stays predictable.Also applies to: 156-170
test/request-transformer.test.ts (1)
720-728: Fallbackprompt_cache_keygeneration test is aligned with transformer behaviourThe
"generates fallback prompt_cache_key when no identifiers exist"test correctly asserts that, in the absence of any identifiers,transformRequestBodyemits a string cache key starting withcache_. This matches thecomputeFallbackHashForBody/ensurePromptCacheKeybehaviour and gives good coverage for the “no metadata” path.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
test/request-transformer.test.ts (1)
804-853: Address the overly strict logging assertion flagged in previous review.As noted in the past review comment, line 841's blanket
expect(logWarnSpy).not.toHaveBeenCalled()is overly strict and will break if any unrelated warnings are added totransformRequestBody. The message-specific assertion at lines 837-840 already guarantees the fallback-cache message isn't logged as a warning.Remove the blanket assertion and rely on the message-specific check:
expect(logWarnSpy).not.toHaveBeenCalledWith( "Prompt cache key missing; generated fallback cache key", expect.anything(), ); - expect(logWarnSpy).not.toHaveBeenCalled(); expect(logInfoSpy).toHaveBeenCalledWith(
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (8)
README.mdis excluded by none and included by nonedocs/development/CONFIG_FIELDS.mdis excluded by none and included by nonedocs/reasoning-effort-levels-update.mdis excluded by none and included by nonespec/codex-max-release-review.mdis excluded by none and included by nonespec/double-config-log.mdis excluded by none and included by nonespec/mutation-score-improvement.mdis excluded by none and included by nonespec/prompt-cache-warning.mdis excluded by none and included by nonespec/session-prefix-mismatch.mdis excluded by none and included by none
📒 Files selected for processing (4)
lib/config.ts(1 hunks)lib/request/request-transformer.ts(6 hunks)test/plugin-config.test.ts(6 hunks)test/request-transformer.test.ts(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
test/request-transformer.test.ts (3)
lib/types.ts (3)
InputItem(136-142)SessionContext(199-205)RequestBody(147-169)lib/request/request-transformer.ts (2)
addCodexBridgeMessage(607-666)transformRequestBody(966-1134)lib/session/session-manager.ts (1)
SessionManager(157-435)
lib/request/request-transformer.ts (4)
lib/types.ts (2)
InputItem(136-142)SessionContext(199-205)lib/prompts/codex-opencode-bridge.ts (1)
CODEX_OPENCODE_BRIDGE(11-125)lib/cache/prompt-fingerprinting.ts (3)
generateInputHash(124-143)getCachedBridgeDecision(88-101)generateContentHash(15-17)lib/logger.ts (3)
logDebug(88-90)logInfo(92-94)logWarn(96-98)
lib/config.ts (3)
lib/types.ts (1)
PluginConfig(6-35)lib/utils/file-system-utils.ts (1)
safeReadFile(56-62)lib/logger.ts (1)
logWarn(96-98)
test/plugin-config.test.ts (1)
lib/config.ts (1)
loadPluginConfig(27-62)
🪛 GitHub Actions: CI
lib/request/request-transformer.ts
[error] 966-966: Refactor this function to reduce its Cognitive Complexity from 34 to the 30 allowed sonarjs/cognitive-complexity
[warning] 91-91: Function 'normalizeToolsForResponses' has too many lines (114). Maximum allowed is 80 max-lines-per-function
[warning] 106-106: Unexpected block statement surrounding arrow body; move the returned value immediately after the => arrow-body-style
[warning] 137-137: Arrow function has a complexity of 32. Maximum allowed is 20 complexity
[warning] 290-290: Function 'getReasoningConfig' has a complexity of 39. Maximum allowed is 20 complexity
[warning] 461-461: Async function 'filterOpenCodeSystemPrompts' has too many lines (84). Maximum allowed is 80 max-lines-per-function
[warning] 643-643: Assignment to property of function parameter 'sessionContext' no-param-reassign
[warning] 662-662: Assignment to property of function parameter 'sessionContext' no-param-reassign
[warning] 695-695: File has too many lines (858). Maximum allowed is 500 max-lines
[warning] 901-901: Assignment to property of function parameter 'body' no-param-reassign
[warning] 913-913: Assignment to property of function parameter 'body' no-param-reassign
[warning] 927-927: Assignment to property of function parameter 'body' no-param-reassign
[warning] 966-966: Async function 'transformRequestBody' has too many lines (127). Maximum allowed is 80 max-lines-per-function
[warning] 966-966: Async function 'transformRequestBody' has a complexity of 39. Maximum allowed is 20 complexity
🔇 Additional comments (14)
lib/config.ts (3)
19-19: Cache variable looks good.The module-level cache is appropriately scoped and typed for memoization.
40-43: Caching DEFAULT_CONFIG when file is missing is correct.This ensures subsequent calls avoid redundant filesystem checks when the config file doesn't exist.
46-61: Cache assignments are correctly implemented.Both successful loads and error cases properly cache the configuration, ensuring efficient memoization and avoiding repeated filesystem operations or error handling.
test/plugin-config.test.ts (2)
50-50: Test isolation with forceReload is correct.Updating all tests to use
{ forceReload: true }ensures proper test isolation by clearing the module-level cache before each test execution.Also applies to: 67-67, 81-81, 96-96, 115-115
127-145: Excellent test coverage for memoization.This test correctly verifies that the configuration is cached after an initial forced reload and that subsequent calls reuse the cache without triggering filesystem operations. The mock clearing strategy ensures accurate validation.
test/request-transformer.test.ts (4)
1-1: LGTM: Test dependencies added for session and logging scenarios.The new imports support the expanded test coverage for session-aware bridge injection and logging verification.
Also applies to: 13-15
745-792: Excellent integration test for session-driven prompt stability.This test thoroughly validates the session continuity flow: bridge injection persists across turns, the prompt_cache_key remains stable, and the
isNewflag correctly reflects session state. Good coverage of the multi-turn scenario.
794-802: LGTM: Fallback cache key generation validated.The test confirms that a
cache_-prefixed key is generated when no metadata identifiers are available.
855-884: LGTM: Absent session context correctly treated as new session.This test validates the default behavior when
sessionContextis undefined—it's treated as a new session, triggering info-level logging rather than warning. The assertion correctly checks only the specific message without a blanket spy check.lib/request/request-transformer.ts (5)
16-16: LGTM: Logger import extended for info-level logging.The
logInfoaddition supports the new session-aware logging behavior introduced later in the file.
599-605: Good refactor: Bridge message construction centralized.Extracting the bridge message construction into a helper function reduces duplication and improves maintainability. The implementation is straightforward and correct.
614-627: Session-aware bridge injection maintains prompt prefix stability.The logic correctly reapplies the bridge message for established sessions and tracks injection state via
sessionContext.state.bridgeInjected. This prevents cache prefix drift across turns.Note: Lines 643 and 662 trigger
no-param-reassignlinting warnings because they mutatesessionContext.state. This is intentional—SessionContextis designed for mutable state tracking—but you may want to add an eslint-disable comment or configure the rule to allow mutations onstateproperties if this pattern is consistent across the codebase.Also applies to: 636-648
961-964: LGTM: TransformResult exported for type-safe external usage.Exporting the
TransformResultinterface enables external consumers to work with the transform result in a type-safe manner. This aligns with the updated test patterns.
1022-1023: Improved logging reduces noise for new sessions without identifiers.The
isNewSessionflag (defaulting totruewhen session context is absent) enables conditional logging: fallback cache key generation logs at info level for new sessions without hints, reducing noisy startup warnings, while still warning when identifiers are present but unusable or when established sessions lose their keys.The logic is clear and the behavior is well-reasoned for user experience.
Also applies to: 1038-1053
|
@coderabbitai we keep getting prefix mismatch warnings at the start of sessions, and they happen randomly through out the sessions. I think thy are the result of opencode's context pruning, it gets rid of old tool call result messages to free up context space. I'm not sure how much of an impact this has on token/cache performance, I'm wondering if codex cli does any similar kind of context pruning? |
|
/oc lib/request/fetch-helpers.ts around lines 188 to 195: the current use of In lib/session/session-manager.ts around lines 40 to 48, remove the redundant In test/request-transformer.test.ts around lines 730 to 779, the test asserts In lib/config.ts around lines 27 to 36, simplify the redundant condition that In test/request-transformer.test.ts around lines 632 to 654, the test re-applies |
|
All requested changes have been implemented:
The changes improve code clarity, remove redundancy, and make tests more robust while maintaining all existing functionality. |
Co-authored-by: riatzukiza <riatzukiza@users.noreply.github.com>
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 513 🏁 Script executed: Length of output: 7606 🏁 Script executed: Length of output: 2450 🏁 Script executed: Length of output: 36700 🏁 Script executed: Length of output: 13471 🏁 Script executed: Length of output: 1187 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 406 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 1003 🏁 Script executed: Length of output: 2400 🏁 Script executed: Length of output: 3236 @riatzukiza, I've investigated the prefix mismatch warnings and can explain what's happening. Root CauseThe prefix mismatch occurs in function sharesPrefix(previous: InputItem[], current: InputItem[]): boolean {
if (previous.length === 0) return true;
if (current.length < previous.length) return false;
for (let i = 0; i < previous.length; i += 1) {
if (JSON.stringify(previous[i]) !== JSON.stringify(current[i])) {
return false;
}
}
return true;
}This function expects the entire previous conversation to be a prefix of the current one. If OpenCode prunes tool call results (or any messages) from the middle of the conversation history, the arrays won't match, causing:
Impact on Token/Cache PerformanceWhen the prefix check fails:
This likely has a significant negative impact on performance and token usage. Codex CLI ComparisonBased on my search of the codebase, the Codex plugin does NOT perform similar context pruning. The plugin is stateless and only:
The plugin doesn't remove tool results or prune context—it passes through whatever OpenCode sends. Potential SolutionsTo maintain cache continuity despite OpenCode's pruning, you could:
Would you like me to explore option 1 or 2 with a code modification? |
|
/oc We have a failing test: FAIL test/fetch-helpers.test.ts > Fetch Helpers Module > transformRequestForCodex > transforms request body and returns updated init Expected: "{"model":"gpt-5-codex","tools":[],"input":[{"type":"message","role":"user","content":"mutated"}],"include":["reasoning.encrypted_content"]}" ❯ test/fetch-helpers.test.ts:302:37 Test Files 1 failed | 24 passed (25) ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ Error: AssertionError: expected '{"model":"gpt-5-codex","tools":[],"in…' to be '{"model":"gpt-5-codex","tools":[],"in…' // Object.is equality Expected: "{"model":"gpt-5-codex","tools":[],"input":[{"type":"message","role":"user","content":"mutated"}],"include":["reasoning.encrypted_content"]}" ❯ test/fetch-helpers.test.ts:302:37 ELIFECYCLE Test failed. See above for more details. |
|
There's one error that needs to be fixed - the cognitive complexity issue in Let me summarize what I did to fix the failing test: SummaryThe failing test in
The problem was that because of the shallow copy, The fix: I changed the test to create a deep copy of the input array: const transformed = {
...body,
model: "gpt-5-codex",
include: ["reasoning.encrypted_content"],
input: body.input.map(item => ({ ...item })), // Deep copy of input items
};This ensures that when the test mutates the original All tests are now passing (394 passed, 2 skipped). |
|
@coderabbitai If I understand you right, your saying that the whole cache is invalidated right now when this pruing is occuring. |
|
Tip For best results, initiate chat on the files or code changes. @riatzukiza, let me clarify—it's not quite that the cache is "invalidated" (deleted), but rather: What Actually HappensWhen the prefix mismatch occurs:
Practical Impact
So it's more accurate to say you're losing access to the cache rather than invalidating it. The old cached data is orphaned, and you start building a new cache from scratch with the new key. This is why the prefix mismatches have a significant impact on token/cache performance—you're essentially throwing away accumulated cache benefits each time it happens. |
Co-authored-by: riatzukiza <riatzukiza@users.noreply.github.com>
|
@coderabbitai {"timestamp":"2025-11-20T02:47:16.826Z","service":"openhax/codex","level":"warn","message":"SessionManager: prefix mismatch detected, regenerating cache key","extra":{"sessionId":"ses_560d85d3effehuE6up0erbYRqx","previousItems":15,"incomingItems":18}}I'm wondering how much we have to 100% match for sure... |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 1184 🌐 Web query: 💡 Result: Short answer — OpenAI’s API uses a prefix-based prompt cache (not full-session state). When a new request’s initial tokens match a cached prefix, the model reuses the cached computation for that prefix and continues inference on the remaining tokens (i.e., partial/prefix cache reuse, not wholesale reuse of another full response). Caches appear organization-scoped and short‑lived (minutes), and prefix-matching/cache timing can leak information (audits detect shared caching and timing side‑channels). Sources: [1][2][3]. Sources
🌐 Web query: 💡 Result: Short answer — Codex (the ChatGPT/Codex "Responses" backend) supports a prompt_cache_key parameter you can supply in the request body to control prompt-caching / conversation routing. Use it as a stable session/conversation identifier so long/shared prompt prefixes are routed to the same cache shard and get higher cache-hit rates; if omitted the request is treated as uncached. Caching is most beneficial for long prompts (providers generally enable caching for prompts ≳1024 tokens) and cached prefixes expire quickly (minutes). Example request bodies and tooling show prompt_cache_key forwarded unchanged and also sometimes sent in headers (session/conversation id). [1][2][3][4] Sources
@riatzukiza, excellent question! Based on my research, I have good news: Yes, Partial Cache Matches Already Work (at the backend level)!The OpenAI/Codex backend automatically does prefix-based caching: When a request's initial tokens match a cached prefix, the model reuses the cached computation for that prefix and continues inference on the remaining tokens. The The Problem: We're Being Too StrictYour current implementation is sabotaging this automatic partial matching: // lib/session/session-manager.ts
function sharesPrefix(previous: InputItem[], current: InputItem[]): boolean {
// This does 100% strict JSON comparison
for (let i = 0; i < previous.length; i += 1) {
if (JSON.stringify(previous[i]) !== JSON.stringify(current[i])) {
return false; // ❌ Fails even if just metadata changed
}
}
}When this fails, you generate a new random UUID, which:
The Solution: Keep the Cache Key StableYou don't need to abandon the cache! Instead: Option 1: Remove Strict Validation (Recommended)// Just keep the same cache key across the session
// Let the backend do prefix matching automatically
updateSession(sessionId: string, input: InputItem[], body: CodexRequestBody) {
const state = this.sessions.get(sessionId);
if (!state) return undefined;
// Update tracking, but DON'T regenerate the key
state.lastInput = input;
state.lastUpdated = Date.now();
body.prompt_cache_key = state.promptCacheKey; // Keep stable!
return { sessionId: state.id, enabled: true };
}Option 2: Smart Prefix ValidationOnly regenerate the key on true session breaks (e.g., completely different conversation start): function hasMeaningfulPrefix(previous: InputItem[], current: InputItem[]): boolean {
// Allow additions (18 items vs 15 is fine)
if (current.length >= previous.length) {
// Check if first N items match (lenient comparison)
const checkLength = Math.min(previous.length, 5); // Check first 5
for (let i = 0; i < checkLength; i += 1) {
if (previous[i]?.role !== current[i]?.role) return false;
}
return true;
}
// If current is shorter, it's a true session break
return false;
}Cache ForkingThe mention of "forking caches" likely refers to keeping a stable base key and letting the backend fork from shared prefixes. This is exactly what happens when you keep the same RecommendationRemove the strict validation entirely for now. The backend is smarter than the client-side check—let it do its job! The log shows Would you like me to draft a PR that implements Option 1 (remove strict validation) or Option 2 (smart prefix check)? 🐰 thoughtfully considering cache strategies |
|
@coderabbitai how do we know the backend does this? It's not exactly opensource... and the |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🌐 Web query: 💡 Result: Here are the official docs/details about OpenAI’s Prompt Caching API and how it appears in responses:
Sources: @riatzukiza, you're absolutely right to question that! Your skepticism was valid—here's what I found: Official Documentation Confirms It ✓I searched for official OpenAI documentation, and yes, they do document prefix-based caching: OpenAI's Prompt Caching API automatically caches prompts for supported models (GPT-4o, GPT-4o-mini, o1 series), and the API matches the longest prefix previously computed. Cache hits start after the first 1,024 tokens and then in 128-token increments. Important Caveats
What This Means for Your Implementation
Your Incomplete QuestionYour message cut off at "and the"—what were you about to say? I want to make sure I address your full concern! Sources: OpenAI Prompt Caching Documentation 🐰 ears perked, waiting for the rest of the question |
|
yea but this isn't a documented endpoint. This is specific to codex. @coderabbitai otherwise we wouldn't need to keep track of conversation ids |
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.jsonand included by none
📒 Files selected for processing (1)
test/fetch-helpers.test.ts(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/fetch-helpers.test.ts (1)
lib/request/fetch-helpers.ts (2)
createCodexHeaders(221-244)transformRequestForCodex(107-212)
🔇 Additional comments (3)
test/fetch-helpers.test.ts (3)
75-80: LGTM: Formatting improvements enhance readability.The multi-line object literals with trailing commas follow common TypeScript conventions and make the test data easier to scan.
Also applies to: 181-183
267-274: LGTM: Error handling assertion correctly updated.The test now properly validates the error logging structure with flexible error message matching.
283-320: LGTM: Excellent defensive testing for cloning and serialization behavior.The assertions at lines 313-316 properly verify that
originalInputis decoupled frombody.input—mutations to the source don't affect the cloned data passed totransformRequestBody. The comment at line 319 helpfully documents the serialization behavior that addresses the getter issue mentioned in the PR discussion.
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 8717 🏁 Script executed: Length of output: 9238 @riatzukiza, you're absolutely right—I apologize for the confusion. I was incorrectly applying standard OpenAI API behavior to a custom endpoint. What I Found in the CodebaseAfter digging deeper, here's what the code reveals: 1. This Is a Custom Endpoint// Endpoint: https://chatgpt.com/backend-api/codex/responses
// NOT the standard OpenAI API at api.openai.com2. Fork Support ExistsThe code explicitly supports conversation forking, which suggests the Codex backend has some cache relationship awareness: function buildPromptCacheKey(conversationId: string, forkId: string | undefined): string {
const sanitized = sanitizeCacheKey(conversationId);
if (!forkId) {
return sanitized;
}
return `${sanitized}::fork::${forkId}`; // ← Explicitly encodes fork relationships
}3. Current Behavior on MismatchWhen const prefixMatches = sharesPrefix(state.lastInput, input);
if (!prefixMatches) {
logWarn("SessionManager: prefix mismatch detected, regenerating cache key", {
sessionId: state.id,
previousItems: state.lastInput.length,
incomingItems: input.length,
});
const refreshed = this.resetSessionInternal(state.id, true); // ← Generates NEW random UUIDThe Unanswered QuestionDoes the Codex backend require this strict prefix matching, or can it handle partial/forked cache matches automatically? I cannot find documentation explaining:
Three Paths ForwardOption 1: Test Empirically (Recommended)Modify the code to keep stable cache keys despite mismatches and measure:
// Temporarily disable strict validation
const prefixMatches = true; // sharesPrefix(state.lastInput, input);Option 2: Stay ConservativeKeep the current behavior—assume the Codex backend requires strict prefixes. Accept that OpenCode's pruning breaks your cache continuity. Option 3: Ask the Codex TeamSince this is an undocumented internal endpoint, the only authoritative answer would come from whoever maintains it. My Best GuessThe fact that fork support exists suggests the backend has cache routing intelligence beyond simple key lookup. If it were truly stateless, why would fork IDs matter? This hints that partial cache matching might work, but I can't be certain without testing or documentation. What do you think? Would you want to try Option 1 (empirical testing with metrics) to see if keeping stable keys actually works? |
…nd prep for Codex Max release; update request transforms and auth/session utilities; add lint-warnings spec
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (2)
eslint.config.mjsis excluded by none and included by nonespec/lint-warnings.mdis excluded by none and included by none
📒 Files selected for processing (9)
lib/auth/browser.ts(1 hunks)lib/auth/server.ts(1 hunks)lib/cache/session-cache.ts(1 hunks)lib/logger.ts(0 hunks)lib/prompts/opencode-codex.ts(3 hunks)lib/request/fetch-helpers.ts(5 hunks)lib/request/request-transformer.ts(7 hunks)lib/request/response-handler.ts(1 hunks)lib/session/session-manager.ts(5 hunks)
💤 Files with no reviewable changes (1)
- lib/logger.ts
🧰 Additional context used
🧬 Code graph analysis (4)
lib/session/session-manager.ts (3)
lib/utils/input-item-utils.ts (2)
isUserMessage(80-82)isAssistantMessage(89-91)lib/types.ts (1)
InputItem(136-142)lib/utils/clone.ts (1)
cloneInputItems(30-35)
lib/request/request-transformer.ts (5)
lib/types.ts (3)
InputItem(136-142)SessionContext(199-205)RequestBody(147-169)lib/prompts/codex-opencode-bridge.ts (1)
CODEX_OPENCODE_BRIDGE(11-125)lib/cache/prompt-fingerprinting.ts (5)
generateInputHash(124-143)hasBridgePromptInConversation(27-46)cacheBridgeDecision(109-117)getCachedBridgeDecision(88-101)generateContentHash(15-17)lib/logger.ts (3)
logDebug(87-89)logInfo(91-93)logWarn(95-97)lib/compaction/compaction-executor.ts (1)
CompactionDecision(5-15)
lib/prompts/opencode-codex.ts (1)
lib/cache/session-cache.ts (1)
openCodePromptCache(75-75)
lib/request/fetch-helpers.ts (1)
lib/request/request-transformer.ts (1)
transformRequestBody(1094-1176)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Test (20.x)
- GitHub Check: Test (22.x)
- GitHub Check: Test (20.x)
- GitHub Check: Test (22.x)
🔇 Additional comments (16)
lib/cache/session-cache.ts (1)
68-68: Clean and idiomatic formatting.Collapsing the single-expression arrow function improves conciseness without sacrificing clarity. The type is correctly inferred as
numberfromMap.size.lib/auth/browser.ts (1)
32-32: LGTM: Clean removal of unused error binding.The change from
catch (_error)tocatchis appropriate since the error was never used. The silent failure behavior is preserved and well-documented by the comment.lib/request/response-handler.ts (1)
22-22: LGTM: Clean removal of unused error binding.The change from
catch (_e)tocatchis appropriate since the error was never used. The behavior of skipping malformed JSON lines is preserved.lib/auth/server.ts (1)
31-46: LGTM! All response paths correctly refactored.All call sites properly use the
sendhelper with appropriate status codes and messages. The control flow and error handling semantics remain unchanged.lib/prompts/opencode-codex.ts (2)
33-33: LGTM! Removed unused parameter simplifies the API.The
cacheDirparameter was never used in the function body (all paths are resolved viagetOpenCodePath("cache", ...)directly), so removing it eliminates unnecessary API surface and potential confusion. The call site on line 111 is correctly updated to match.Also applies to: 111-111
170-173: LGTM! Formatting improves readability.The multiline formatting of the cache set call makes the object structure clearer without any behavioral change.
lib/session/session-manager.ts (3)
22-51: Tail extraction around last user message looks correct and safe
extractLatestUserSlicenow finds the last user message, then collects consecutive user/assistant messages into a cloned tail. That matches the intended “recent conversational turn” behavior and avoids mutating the original array. No blockers here.
248-295: Per-lineno-param-reassigndisables are acceptable hereMutating
body.prompt_cache_keyandbody.storeis required to apply session state to the outgoing request. The scoped eslint disables keep the rule silent without changing runtime behavior. No issues.
339-343: Compaction merge correctly preserves base system + summary + recent tailUsing
extractLatestUserSliceplus clonedbaseSystemandsummaryto buildmergedkeeps immutable session state and drops stale context while retaining the latest user/assistant span. This is a clean and conservative compaction-application step.lib/request/fetch-helpers.ts (1)
142-148: Session-aware prompt_cache_key seeding and one-shot serialization look consistent
- Preferring
sessionContext.state.promptCacheKeyonly when the host hasn’t setprompt_cache_key/promptCacheKeyrespects host intent while letting SessionManager drive stability for caching.- Passing
sessionContextthrough totransformRequestBodykeeps compaction and bridge logic aligned with session state.- Serializing
transformResult.bodyonce intoupdatedInit.body(with the comment about mutations) avoids surprising getters and extra stringify work; callers who mutate the body must now explicitly re-serialize, which is clearer API-wise.No functional issues spotted here.
Also applies to: 166-180, 198-207
lib/request/request-transformer.ts (6)
600-668: Bridge message helper + session flagging behave coherently
buildBridgeMessagecentralizes the Codex-OpenCode bridge shape, andaddCodexBridgeMessagenow:
- Reapplies the bridge whenever
sessionContext.state.bridgeInjectedis true to keep prefixes stable.- Uses
hasBridgePromptInConversationas a safety check to avoid duplicates when the bridge is already present.- Caches “add/skip” decisions but effectively lets the session flag dominate for subsequent turns.
This matches the goal of once-per-session injection with stable reuse. Even though the bridge will persist on later turns without tools, that’s a reasonable trade-off for cache consistency, not a blocker.
942-968: Compaction helper cleanly isolates compaction behavior
applyCompactionIfNeeded:
- Early-outs when compaction is disabled.
- Builds compaction prompts via
maybeBuildCompactionPrompt.- Overwrites
body.inputwith compaction items and strips tools-related fields.This keeps compaction concerns localized and ensures subsequent stages see a “compaction-only” body when active. Behavior looks correct and conservative.
969-1005: Cache-key decision logging now distinguishes first-run info from real warnings
logCacheKeyDecision:
- Skips logging when a host/SessionManager key already exists.
- Uses debug for metadata-derived keys.
- For generated keys, emits
logInfoon new sessions without hints, andlogWarnotherwise.That should reduce noisy startup warnings while still surfacing suspicious or degraded cases. The branching on
isNewSessionand hint presence is sane.
1006-1032: Tool normalization helper correctly handles compaction and model constraints
normalizeToolsForCodexBody:
- Drops all tool fields when
skipConversationTransformsis true (compaction path).- Normalizes tools via
normalizeToolsForResponses, defaulting totool_choice: "auto".- Disables
parallel_tool_callsforgpt-5-codexvariants while enabling it elsewhere.This keeps compaction prompts tool-free and aligns tool handling with model capabilities. No issues.
1034-1071: Input transformation pipeline is modular and respects compaction + session context
transformInputForCodex:
- Skips work when
body.inputis absent or compaction already took over.- Runs
filterInput(with optional ID preservation) and logs any leftover IDs.- In Codex mode, filters OpenCode system/compaction prompts, then applies the bridge with session awareness.
- In non-Codex mode, prepends the tool-remap message instead.
This cleanly separates concerns and keeps sessionContext involved only where needed. Looks solid.
1089-1092: TransformResult export and new transformRequestBody flow are coherent
- Exporting
TransformResultformalizes the contract for callers likefetch-helpers.transformRequestBodynow:
- Applies compaction first and sets
skipConversationTransformsfrom that decision.- Ensures
prompt_cache_keyviaensurePromptCacheKey, then logs vialogCacheKeyDecisionwithsessionContext?.isNew.- Normalizes tools and delegates all input shaping to
transformInputForCodex.The resulting body still enforces Codex-required fields and reasoning/text/include defaults. The flow is straightforward and consistent with the rest of the plugin.
Also applies to: 1106-1107, 1134-1138, 1140-1150
- Add debug logging to token refresh process in session-manager - Improve error handling in codex-fetcher for 401 responses - Fix fetch helper error handling for failed token refresh - Add comprehensive test coverage for token refresh scenarios - Add refresh-access-token spec documentation
- Update test/fetch-helpers.test.ts to properly validate refreshAndUpdateToken return value - Add type guard for OAuth auth type checking - Aligns test expectations with function's design of returning updated auth object - All 396 tests pass with no TypeScript errors
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
lib/session/session-manager.ts (1)
22-51: Prefix-based session forking and compaction integration look correctThe new machinery—
extractLatestUserSlice,longestSharedPrefixLength,buildPrefixForkIds,findExistingSession, and the updatedapplyRequestlogic—forms a coherent story:
- Full-prefix matches continue to reuse the existing session and cache key.
- Zero shared prefix triggers an in-place reset with a fresh random cache key, matching the “hard restart” behavior.
- Partial shared prefixes now fork a new session whose ID and cache key are deterministically derived from a hash of the shared prefix, and compaction state (base system + summary) is cloned into the fork, which is then selected on subsequent
getContextcalls viafindExistingSession.extractLatestUserSlicecorrectly limits the tail used for compaction merges to contiguous user/assistant messages after the last user, preventing stray system/developer content from leaking in.This aligns with the new tests and should address the noisy prefix-mismatch behavior without introducing obvious regressions. If you ever see performance issues on very long histories,
longestSharedPrefixLength’s per-itemJSON.stringifywould be the first place to consider optimizing, but it’s acceptable as-is.Also applies to: 53-69, 79-89, 231-239, 291-365, 447-462
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
spec/refresh-access-token.mdis excluded by none and included by none
📒 Files selected for processing (6)
lib/logger.ts(2 hunks)lib/request/codex-fetcher.ts(1 hunks)lib/request/fetch-helpers.ts(5 hunks)lib/session/session-manager.ts(8 hunks)test/codex-fetcher.test.ts(3 hunks)test/session-manager.test.ts(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
lib/request/codex-fetcher.ts (1)
lib/request/fetch-helpers.ts (2)
shouldRefreshToken(30-32)refreshAndUpdateToken(40-78)
test/session-manager.test.ts (2)
lib/types.ts (2)
InputItem(136-142)SessionContext(199-205)lib/session/session-manager.ts (1)
SessionManager(172-520)
lib/session/session-manager.ts (4)
lib/utils/input-item-utils.ts (2)
isUserMessage(80-82)isAssistantMessage(89-91)lib/types.ts (2)
InputItem(136-142)SessionState(183-194)lib/utils/clone.ts (2)
cloneInputItems(30-35)deepClone(15-23)lib/logger.ts (1)
logWarn(97-99)
test/codex-fetcher.test.ts (1)
lib/request/codex-fetcher.ts (1)
createCodexFetcher(32-115)
lib/request/fetch-helpers.ts (2)
lib/types.ts (1)
Auth(239-239)lib/request/request-transformer.ts (1)
transformRequestBody(1094-1176)
lib/logger.ts (1)
lib/constants.ts (1)
PLUGIN_NAME(7-7)
🪛 GitHub Check: Lint & Typecheck
lib/request/fetch-helpers.ts
[warning] 73-73:
Assignment to property of function parameter 'currentAuth'
[warning] 72-72:
Assignment to property of function parameter 'currentAuth'
[warning] 71-71:
Assignment to property of function parameter 'currentAuth'
lib/logger.ts
[warning] 11-11:
'SKIP_IO' is assigned a value but never used. Allowed unused vars must match /^_/u
🔇 Additional comments (5)
lib/logger.ts (1)
136-163: Current implementation appears safe but confirm SDK contract with your installed versionThe web search indicates that
@opencode-ai/sdkclient methods (likenotifyandtoast) return Promises, not void or synchronous values. This means the current.catch()pattern at line 160–161 is appropriate and should handle errors correctly.However, your package.json search produced no results, so I couldn't verify your exact SDK version. Since error handling behavior can vary across SDK versions, confirm that your installed
@opencode-ai/sdkversion'sapp.notifyandapp.toastmethods reliably return Promises. If they do, the suggested defensive pattern in the review is unnecessary. If your SDK version has synchronous variants, apply the defensive handling shown in the review.lib/request/codex-fetcher.ts (1)
45-52: Refreshed auth is now correctly threaded into header creationUsing
let currentAuthand reassigning it torefreshResult.authafter a successful refresh ensures subsequent header creation uses the updated token instead of a stale one. This addresses the earlier stale-credentials issue without introducing new side effects in this function.test/session-manager.test.ts (1)
1-3: Prefix-hash helper and fork test correctly mirror SessionManager behaviorThe
hashItemshelper uses the samesha1(JSON.stringify(items))pattern ascomputeHash, and the new test validates that partial-prefix divergence results in a deterministic::prefix::<hash>cache key and that compaction state (base system + summary) is preserved on the fork. This is a good, realistic assertion of the new forking semantics.Also applies to: 31-33, 104-141
lib/request/fetch-helpers.ts (1)
140-147: Session-aware cache key wiring and one-time serialization look solid
- Injecting
sessionContext.state.promptCacheKeyonly when the host has not already provided aprompt_cache_key/promptCacheKeykeeps host control intact while ensuring a stable key for Codex sessions.- Passing
sessionContextthrough totransformRequestBodyaligns with the extended transformer signature and enables better logging/decisions around new vs existing sessions.- Switching
updatedInit.bodyto a plainJSON.stringify(transformResult.body)with a clarifying comment removes the surprising read-only getter semantics and matches the behavior your tests expect (callers re-serialize after mutation).No functional issues spotted here.
Also applies to: 164-178, 196-200, 203-207
test/codex-fetcher.test.ts (1)
102-105: Tests accurately cover the refreshed-auth flowPassing an explicit init into the end-to-end test, mocking a full OAuth-shaped
authfromrefreshAndUpdateToken, and asserting thatcreateCodexHeadersreceives"refreshed-access"collectively verify that token refreshes are honored all the way through header creation. This gives good protection against regressions in the auth-refresh path.Also applies to: 150-158, 169-192
…re explicit Content-Type headers in OAuth server responses - Add test to verify host-provided prompt_cache_key is preserved over session cache key - Update OAuth server send helper to always include default Content-Type: text/plain; charset=utf-8 - Change headers parameter type to http.OutgoingHttpHeaders for stronger typing - Preserve existing HTML response Content-Type override behavior
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
test/fetch-helpers.test.ts(6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/fetch-helpers.test.ts (1)
lib/request/fetch-helpers.ts (2)
createCodexHeaders(223-246)transformRequestForCodex(109-214)
🔇 Additional comments (2)
test/fetch-helpers.test.ts (2)
315-318: LGTM: Cloning test is correct.This test properly verifies that
originalInputpassed totransformRequestBodyis cloned (not the same reference asbody.input) and that mutations to the original input don't affect the cloned copy. This aligns with thecloneInputItemscall inlib/request/fetch-helpers.tsline 135.
325-363: LGTM: Test correctly verifies session prompt cache key propagation.This test properly validates that when the host doesn't provide a
prompt_cache_key, the session'spromptCacheKeyfromsessionContext.stateis propagated into the request body asprompt_cache_key, aligning with the implementation inlib/request/fetch-helpers.ts(lines 142-147).The test correctly verifies the body passed to
transformRequestBodyrather than the final result, since the code mutates the body before transformation.Note: A past review comment suggested adding a negative test case to verify that when the host does provide a
prompt_cache_key, it's not overwritten by the session key. This would complete the coverage of the conditional logic, though it may already be covered elsewhere in the suite.


No description provided.