Skip to content

Fix Codex approval request syncing#25

Merged
achimala merged 9 commits into
achimala:mainfrom
lelonco:codex/fix-approval-request-sync
Apr 27, 2026
Merged

Fix Codex approval request syncing#25
achimala merged 9 commits into
achimala:mainfrom
lelonco:codex/fix-approval-request-sync

Conversation

@lelonco
Copy link
Copy Markdown
Contributor

@lelonco lelonco commented Mar 26, 2026

Summary

  • fix web thread syncing so pending requests update immediately
  • disable Codex mode controls until thread ownership is available
  • capture incoming app-server approval/user-input requests and broadcast them to the web UI
  • add regression coverage for approval visibility and new request sync behavior
  • make workspace build ordering deterministic

Testing

  • bun run typecheck
  • bun test packages/codex-api/test/json-rpc.test.ts
  • bun test apps/server/test/unified-adapter.test.ts
  • bun run --filter @farfield/web test

Summary by CodeRabbit

  • New Features

    • Added approval policy support for message submissions
    • Introduced pending request tracking and subscription capabilities
    • Enhanced thread state synchronization to include pending requests
  • Bug Fixes

    • Improved error recovery when transitioning from thread registration failures
    • Fixed stream event error clearing for better state management
    • Better handling of pending requests during live-state updates
  • Tests

    • Added comprehensive test coverage for pending request workflows, approval handling, and error scenarios

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 26, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The pull request introduces approval policy support and pending app-server request tracking to the Codex agent adapter. Changes flow through the adapter layer, unified API, web app state management, schema definitions, and include comprehensive tests validating the new pending-request merging behavior and approval policy propagation.

Changes

Cohort / File(s) Summary
Codex Agent Adapter
apps/server/src/agents/adapters/codex-agent.ts, apps/server/src/agents/types.ts
Introduces CodexAppServerRequestEvent interface and onAppServerRequest(...) subscription API; adds per-thread pending-request tracking via pendingAppServerRequestsByThreadId; extends turn-start payloads to include approvalPolicy when provided; merges pending requests into readThread and readLiveState results; removes pending requests after successful submitUserInput completion.
Unified Adapter Integration
apps/server/src/unified/adapter.ts, apps/web/src/lib/api.ts
Exports mapThreadRequest helper function; propagates optional approvalPolicy field from unified commands through to adapter sendMessage invocations.
Schema & Protocol Updates
packages/unified-surface/src/index.ts, packages/codex-protocol/test/protocol.test.ts
Updates commandExecution items' processId to accept `string
Web App State Management
apps/web/src/App.tsx, apps/web/src/main.tsx
Introduces buildRequestSourceState to merge pending requests from live+read sources by de-duplicating on method:id key; updates sync signatures to include requestProgressSignature and liveStateError; adds canEditThreadMode guard to block edits when thread mode is not editable; improves stream event handling by clearing errors and moving state updates into transitions; modifies service worker registration to conditionally unregister in development/localhost environments.
Test Coverage
apps/server/test/codex-agent-live-state.test.ts, apps/server/test/unified-adapter.test.ts, apps/web/test/app.test.tsx, packages/codex-api/test/json-rpc.test.ts
Adds comprehensive test suite for pending request tracking and merging behavior; validates approval policy propagation through unified adapter; tests transient thread registration errors and UI recovery; extends JSON-RPC parsing test coverage.

Sequence Diagram

sequenceDiagram
    actor App as Web App
    participant CA as CodexAgent<br/>Adapter
    participant AC as AppServer<br/>Client
    participant LS as Live/Read<br/>Sources
    
    AC->>CA: onServerRequest (ThreadConversationRequest)
    CA->>CA: Extract threadId,<br/>validate payload
    CA->>CA: Store in<br/>pendingAppServerRequests<br/>ByThreadId
    CA->>App: onAppServerRequest<br/>(event)
    
    App->>CA: readThread(threadId)
    CA->>LS: Fetch thread state
    LS-->>CA: Base thread state
    CA->>CA: Merge pending requests<br/>into thread.requests
    CA-->>App: Merged thread state
    
    App->>CA: submitUserInput(threadId, requestId)
    CA->>CA: Locate & remove pending<br/>request from tracking
    CA-->>App: Success, pending cleared
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Hops through approval gates with glee,
Pending requests now flow so free,
Merged from live and read with care,
Policies travel here and there!
State and schemas dance in sync,
What a thoughtful, clever link! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix Codex approval request syncing' directly and clearly describes the main change in the pull request, which is fixing the synchronization of approval requests from the Codex app-server to the web UI.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/server/src/agents/adapters/codex-agent.ts (1)

678-705: ⚠️ Potential issue | 🟠 Major

Use the merged pending-request state when routing replies.

Line 683 still checks only the raw readThread(false) payload. If a request exists only in pendingAppServerRequestsByThreadId during the sync gap, this app-server branch is skipped and the code falls through to IPC ownership checks. That recreates the race this change is trying to close.

Suggested fix
-    const parsedRoutingThread = parseThreadConversationState(threadForRouting.thread);
+    const parsedRoutingThread = this.mergePendingAppServerRequests(
+      parseThreadConversationState(threadForRouting.thread),
+    );
     const routingPendingRequest = findPendingRequestWithId(
       parsedRoutingThread,
       input.requestId,
     );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/agents/adapters/codex-agent.ts` around lines 678 - 705, The
routing check currently inspects the raw readThread(false) result
(threadForRouting → parseThreadConversationState → findPendingRequestWithId)
which can miss requests stored in pendingAppServerRequestsByThreadId; modify the
flow in the codex-agent method so that after parsing threadForRouting you merge
in pending app-server requests (use the existing
mergePendingAppServerRequests(parsedRoutingThread) or equivalent) and then call
findPendingRequestWithId against that merged state; ensure subsequent actions
(submitUserInput, removePendingAppServerRequest, refreshing the thread, updating
streamSnapshotByThreadId/streamSnapshotOriginByThreadId, and setThreadTitle) run
only when the merged-state pending request is found.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/server/src/agents/adapters/codex-agent.ts`:
- Around line 153-162: The current code rebuilds the request object and only
parses id/method/params which drops other top-level fields (e.g., completed) and
hides schema mismatches; change the parse to run
ThreadConversationRequestSchema.safeParse(message.value) (i.e., pass the full
incoming message.value instead of reconstructing it), and if
parsedRequest.success is false, fail hard or log the zod error details before
returning so schema mismatches are visible and completed requests aren’t
re-cached as pending; update any downstream use of parsedRequest to reference
parsedRequest.data accordingly.

In `@apps/web/src/App.tsx`:
- Around line 870-886: The sync signature function requestProgressSignature
currently only uses request.id, method, and completed, so mutations to the
request body (e.g., reason, questions, availableDecisions) are ignored; update
requestProgressSignature (and the sibling signature used in the other block) to
include serialized, deterministic representations of the request body fields you
care about (at least request.reason, request.questions, and
request.availableDecisions) — e.g., append JSON.stringify of those fields (or
join their values) alongside id/method/completed, ensuring null/undefined are
normalized to a stable string so the signature changes whenever those body
fields change and setLiveState()/setReadThreadState() will detect updates.
- Around line 670-710: mergePendingRequests currently only tracks keys for
non-completed items from the primary, so completed requests present in either
source can be reintroduced from the fallback; fix this by computing a set of
completed keys from both primary and secondary (collect buildPendingRequestKey
for requests where completed === true) and then only include pending requests
whose key is not in that completedKeys set; update mergePendingRequests (used by
buildRequestSourceState) to skip any secondary request whose key is listed as
completed in either source so read-thread fallbacks cannot reopen resolved
requests, and ensure buildRequestSourceState continues to only merge when both
liveConversationState and readConversationState are present.

In `@packages/codex-api/src/app-server-transport.ts`:
- Around line 17-20: Change the AppServerTransport interface so
setIncomingMessageHandler is required (remove the optional ?), then update the
call in AppServerClient.setIncomingMessageHandler to invoke
transport.setIncomingMessageHandler(...) without optional chaining so missing
implementations fail at compile time; update any transport implementations and
test mocks to implement setIncomingMessageHandler(handler: (message:
JsonRpcIncomingMessage) => void | null): void accordingly.

---

Outside diff comments:
In `@apps/server/src/agents/adapters/codex-agent.ts`:
- Around line 678-705: The routing check currently inspects the raw
readThread(false) result (threadForRouting → parseThreadConversationState →
findPendingRequestWithId) which can miss requests stored in
pendingAppServerRequestsByThreadId; modify the flow in the codex-agent method so
that after parsing threadForRouting you merge in pending app-server requests
(use the existing mergePendingAppServerRequests(parsedRoutingThread) or
equivalent) and then call findPendingRequestWithId against that merged state;
ensure subsequent actions (submitUserInput, removePendingAppServerRequest,
refreshing the thread, updating
streamSnapshotByThreadId/streamSnapshotOriginByThreadId, and setThreadTitle) run
only when the merged-state pending request is found.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f5e02bfc-487e-4f03-bfcb-7a2cb44dc393

📥 Commits

Reviewing files that changed from the base of the PR and between a4b664c and 9eecc06.

📒 Files selected for processing (10)
  • apps/server/src/agents/adapters/codex-agent.ts
  • apps/server/src/index.ts
  • apps/server/src/unified/adapter.ts
  • apps/web/src/App.tsx
  • apps/web/test/app.test.tsx
  • package.json
  • packages/codex-api/src/app-server-client.ts
  • packages/codex-api/src/app-server-transport.ts
  • packages/codex-api/src/json-rpc.ts
  • packages/codex-api/test/json-rpc.test.ts

Comment thread apps/server/src/agents/adapters/codex-agent.ts Outdated
Comment thread apps/web/src/App.tsx
Comment thread apps/web/test/app.test.tsx Outdated
Comment thread packages/codex-api/src/app-server-transport.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/codex-api/src/app-server-transport.ts`:
- Around line 104-106: Wrap the incomingMessageHandler invocation in a try/catch
to prevent handler-thrown exceptions from breaking the transport loop: replace
the direct call to incomingMessageHandler?.(message) with a guarded call inside
try { this.incomingMessageHandler?.(message); } catch (err) {
this.handleTransportError?.(err, message); return; } so any exception is
surfaced via a transport-level error handler (implement handleTransportError if
missing) and the loop returns safely.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c2c775d6-c235-4856-a6ed-14248dbcbe20

📥 Commits

Reviewing files that changed from the base of the PR and between 9eecc06 and 2600f12.

📒 Files selected for processing (6)
  • apps/server/src/agents/adapters/codex-agent.ts
  • apps/web/src/App.tsx
  • apps/web/test/app.test.tsx
  • packages/codex-api/src/app-server-client.ts
  • packages/codex-api/src/app-server-transport.ts
  • packages/codex-api/test/app-server-client.test.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/App.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/codex-api/src/app-server-client.ts
  • apps/server/src/agents/adapters/codex-agent.ts

Comment thread packages/codex-api/src/app-server-transport.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/App.tsx (1)

917-925: ⚠️ Potential issue | 🟠 Major

Include liveStateError in the live-state sync signature.

buildRequestSourceState() and the chat warning banner now depend on liveState.liveStateError, but setLiveState() still dedupes without it. If the reducer fails or recovers without changing conversationState, the app will keep the previous liveState, so request merging can stay in the wrong mode and the warning banner will not toggle.

🔧 Minimal fix
 function buildLiveStateSyncSignature(
   state: LiveStateResponse | null | undefined,
 ): string {
   if (!state) {
     return "";
   }

   const conversationState = state.conversationState;
   return [
     state.threadId,
     state.ownerClientId ?? "",
+    JSON.stringify(state.liveStateError ?? null),
     String(getConversationStateUpdatedAt(conversationState)),
     String(conversationState?.turns.length ?? -1),
     modeSelectionSignatureFromConversationState(conversationState),
     conversationProgressSignature(conversationState),
     requestProgressSignature(conversationState),
   ].join("|");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/App.tsx` around lines 917 - 925, The live-state dedupe signature
built in the function that returns the joined array (used by setLiveState) omits
liveState.liveStateError, causing reducers or recoveries that only change
liveStateError to be ignored; update that signature builder (the function
returning state.threadId, state.ownerClientId...,
modeSelectionSignatureFromConversationState, conversationProgressSignature,
requestProgressSignature) to include the current liveState.liveStateError (e.g.,
stringify it or default to empty) so setLiveState will treat changes to
liveStateError as a different signature and trigger updates; ensure any callers
like buildRequestSourceState and setLiveState rely on the new signature shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/App.tsx`:
- Around line 1596-1599: The computed canEditThreadMode currently allows brief
editability because activeThreadAgentId can fall back to selectedAgentId; update
the guard to also require hasResolvedSelectedThreadProvider so controls stay
disabled until the thread's provider is resolved (i.e., include a boolean check
for hasResolvedSelectedThreadProvider in the canEditThreadMode expression).
Additionally, in the toolbar handlers that call applyModeDraft (referenced as
applyModeDraft and the local mode/model/effort update handlers), early-return if
!hasResolvedSelectedThreadProvider to avoid mutating local state and invoking
applyModeDraft before resolution.

---

Outside diff comments:
In `@apps/web/src/App.tsx`:
- Around line 917-925: The live-state dedupe signature built in the function
that returns the joined array (used by setLiveState) omits
liveState.liveStateError, causing reducers or recoveries that only change
liveStateError to be ignored; update that signature builder (the function
returning state.threadId, state.ownerClientId...,
modeSelectionSignatureFromConversationState, conversationProgressSignature,
requestProgressSignature) to include the current liveState.liveStateError (e.g.,
stringify it or default to empty) so setLiveState will treat changes to
liveStateError as a different signature and trigger updates; ensure any callers
like buildRequestSourceState and setLiveState rely on the new signature shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 834f6e0e-7ee8-4e53-8739-2afc8c9891ab

📥 Commits

Reviewing files that changed from the base of the PR and between 2600f12 and 0303635.

📒 Files selected for processing (4)
  • apps/server/src/agents/adapters/codex-agent.ts
  • apps/web/src/App.tsx
  • packages/codex-api/src/app-server-client.ts
  • packages/codex-api/src/app-server-transport.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/server/src/agents/adapters/codex-agent.ts

Comment thread apps/web/src/App.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/main.tsx`:
- Around line 20-24: The service-worker cleanup call using
navigator.serviceWorker.getRegistrations().then(...registration.unregister())
currently ignores failures; update the logic to handle errors by awaiting the
unregister operations inside an async function or promise chain and adding a
.catch that logs the error (e.g., include the registration or error details) so
failures from navigator.serviceWorker.getRegistrations and
registration.unregister() are not dropped; locate the call to
navigator.serviceWorker.getRegistrations and modify the promise chain around
Promise.all(registrations.map(registration => registration.unregister())) to
catch and log any thrown errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0418347f-4010-4d84-8edd-e92793dbdff6

📥 Commits

Reviewing files that changed from the base of the PR and between 0303635 and 3249b8c.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • apps/server/src/agents/adapters/codex-agent.ts
  • apps/server/src/agents/types.ts
  • apps/server/src/unified/adapter.ts
  • apps/server/test/codex-agent-live-state.test.ts
  • apps/server/test/unified-adapter.test.ts
  • apps/web/src/lib/api.ts
  • apps/web/src/main.tsx
  • packages/codex-protocol/src/thread.ts
  • packages/codex-protocol/test/protocol.test.ts
  • packages/unified-surface/src/index.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/codex-protocol/test/protocol.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/server/src/unified/adapter.ts

Comment thread apps/web/src/main.tsx Outdated
@achimala
Copy link
Copy Markdown
Owner

Codex here, acting on behalf of @achimala.

I pushed a fix for the live-state parse failure we hit while testing this PR. The issue was that PR #25 makes Farfield parse full thread-stream-state-changed live snapshots more aggressively, and current Codex snapshots can include dynamicToolCall turn items. Farfield had that item type in the generated app-server schemas, but not in the hand-written protocol/unified schemas, so the server rejected otherwise valid snapshots.

What changed:

  • Added strict Zod support for dynamicToolCall in @farfield/protocol, matching the vendored Codex app-server shape.
  • Added the corresponding unified-surface item schema and server mapping.
  • Added a compact web renderer for dynamic tool calls so the item type is handled end-to-end.
  • Added protocol and unified-adapter regression tests.

Verified locally:

  • bun test packages/codex-protocol/test/protocol.test.ts
  • bun test apps/server/test/unified-adapter.test.ts
  • bun run --filter @farfield/server test
  • bun run --filter @farfield/web test
  • bun run typecheck
  • bun run test
  • bun run verify:no-cheats

I also verified the exact failing local thread (019dcbf3-33f7-7b41-82e3-10c099954705) through the running Farfield API and in the in-app browser. The backend now returns the thread/live state successfully and the browser renders the thread again.

Note: I tried to start the broader rebase cleanup for #25, but the branch has substantial conflicts against current main in the approval-sync/live-state files, so I stopped that rebase and kept this focused fix pushed separately. The PR still needs that rebase/conflict pass before merge.

@achimala
Copy link
Copy Markdown
Owner

Codex here, acting on behalf of @achimala.

I pushed one more small UI recovery fix after local testing. The direct thread route can briefly ask for a Codex thread before Farfield has registered the provider owner for that thread, which produces Thread <id> is not registered. A later selected-thread refresh can successfully load the same thread, but the old error banner stayed visible.

What changed:

  • Clear the top error banner after the selected thread successfully loads.
  • Added a regression test where the first direct-route read returns threadNotFound, the next refresh loads the thread, and the registration error disappears.

Verified locally:

  • bun run --filter @farfield/web test -- -t "clears a transient direct-route registration error"
  • bun run --filter @farfield/web test
  • bun run typecheck
  • bun run verify:no-cheats

I also reloaded the same local thread in the in-app browser and confirmed the thread renders with no error bar.

@achimala achimala force-pushed the codex/fix-approval-request-sync branch from c6cfbd4 to 11934d5 Compare April 27, 2026 00:01
@achimala
Copy link
Copy Markdown
Owner

achimala commented Apr 27, 2026

Codex here, acting on behalf of @achimala.

I rebased this PR onto current main, resolved the conflicts, and pushed the updated branch.

What changed in the cleanup:

  • Kept current main typed app-server request/notification listener architecture and adapted the pending approval/user-input request cache onto onServerRequest.
  • Preserved bun.lock and removed the earlier package-script churn from the final branch.
  • Kept the dynamic tool-call schema/rendering fix and the direct-route error recovery fix from local testing.
  • Reworked the stale server regression coverage so it now tests the rebased app-server pending-request behavior directly.
  • Addressed and resolved the remaining open review threads for the transport hook, mode-control gating, and service-worker cleanup.

Repo-rule check on the final diff is clean: no as any, no new unknown, and no new type-shape probing outside Zod.

Verified locally:

  • bun run prepare:workspace-dist
  • bun run typecheck
  • bun run --filter @farfield/server test -- test/codex-agent-live-state.test.ts
  • bun run test
  • bun run verify:no-cheats
  • git diff --check
  • final diff rule scan for forbidden TS patterns

The PR is mergeable now. CodeRabbit is currently re-reviewing the pushed head.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/server/src/agents/adapters/codex-agent.ts`:
- Around line 304-307: The handler uses request.params.threadId but some request
types (applyPatchApproval, execCommandApproval) extract the thread id as
params.conversationId; update handleServerRequest and any similar
refresh/broadcast logic (also the other occurrence around the block at lines
2353-2370) to use the already-extracted thread id (conversationId) when present:
read the thread id from the extracted value (e.g., the variable set by
capturePendingAppServerRequest or the helper that pulled params.conversationId)
and pass that into refresh/broadcast/update calls instead of
request.params.threadId so all app-server request types route to the correct
thread.
- Around line 546-549: mergePendingAppServerRequests currently only appends
cached pending entries so stale requests that were resolved upstream never get
evicted; update the logic (called from applyPendingCollaborationMode around
parseThreadConversationState in readThread/readLiveState flows) to compare
cached pending request IDs against the authoritative request IDs from
parseThreadConversationState and remove any cached entries not present upstream
before merging (i.e., perform an eviction pass or replace cached entries for
missing IDs), ensuring mergePendingAppServerRequests returns a clean
conversation state without reintroducing resolved/stale requests.

In `@apps/web/src/App.tsx`:
- Around line 2575-2579: The current branch in the shouldLoadStreamEvents check
calls setError("") which clears the global error banner; change this to only
clear the transient thread-registration error by comparing the existing error
state (or an error type/flag) before clearing so unrelated errors
(catalog/settings) are not dismissed. Locate the block using
shouldLoadStreamEvents, selectedThreadIdRef.current and threadId, and replace
the unconditional setError("") with logic that checks the current error is the
thread-load/transient error (or a dedicated threadLoadError state) and only then
clear it; apply the same scoped fix in the other similar block around lines
referencing the same symbols (the 2594–2608 region).
- Around line 757-810: mergePendingRequests currently prefers the primary (live)
request when keys collide; change it so the secondary (readThread) copy wins on
key collisions. Update mergePendingRequests (and its use in
buildRequestSourceState) to: compute completedKeys from both sources as now,
start merged from secondary.filter(request => request.completed !== true &&
!completedKeys.has(buildPendingRequestKey(request))), track seenKeys from that
merged set, then iterate primary and only add requests whose key is not in
seenKeys and not completed. This ensures buildPendingRequestKey collisions
prefer the read-thread copy.

In `@packages/unified-surface/src/index.ts`:
- Line 1026: Replace the loose string schema for the approvalPolicy field with a
Zod enum of the canonical values so validation fails fast; specifically change
the approvalPolicy schema (the property named approvalPolicy in the relevant Zod
object) from z.string().optional() to a Zod enum with values
"untrusted","on-failure","on-request","never" (and mark it optional if needed),
and make the corresponding change at the other location where approvalPolicy is
defined earlier in the file (the other approvalPolicy schema around the earlier
definition at ~line 988) so both schema boundaries use z.enum([...]).optional()
for consistent, strict validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 84bec153-89a0-40ae-9411-8e9058d439f1

📥 Commits

Reviewing files that changed from the base of the PR and between 3249b8c and 11934d5.

📒 Files selected for processing (12)
  • apps/server/src/agents/adapters/codex-agent.ts
  • apps/server/src/agents/types.ts
  • apps/server/src/unified/adapter.ts
  • apps/server/test/codex-agent-live-state.test.ts
  • apps/server/test/unified-adapter.test.ts
  • apps/web/src/App.tsx
  • apps/web/src/lib/api.ts
  • apps/web/src/main.tsx
  • apps/web/test/app.test.tsx
  • packages/codex-api/test/json-rpc.test.ts
  • packages/codex-protocol/test/protocol.test.ts
  • packages/unified-surface/src/index.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/codex-protocol/test/protocol.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/codex-api/test/json-rpc.test.ts
  • apps/server/test/unified-adapter.test.ts
  • apps/web/test/app.test.tsx
  • apps/web/src/main.tsx
  • apps/server/test/codex-agent-live-state.test.ts

Comment thread apps/server/src/agents/adapters/codex-agent.ts
Comment thread apps/server/src/agents/adapters/codex-agent.ts
Comment thread apps/web/src/App.tsx
Comment thread apps/web/src/App.tsx
Comment thread packages/unified-surface/src/index.ts Outdated
@achimala
Copy link
Copy Markdown
Owner

achimala commented Apr 27, 2026

Codex here, acting on behalf of @achimala.

I pushed one more follow-up commit, 972f33c, after live-testing the approval flow locally.

Fixes in this follow-up:

  • submitUserInput now writes the approval response directly by request id, clears the local pending request cache, and schedules the refresh asynchronously instead of blocking the approval response on a full thread refresh.
  • The Codex protocol schema now accepts the newer custom_tool_call / custom_tool_call_output stream items and empty in-progress userMessage.content arrays.
  • The unified adapter maps those custom tool call items into the existing dynamic tool-call display shape.

Verification: bun run prepare:workspace-dist, bun run typecheck, focused protocol/server tests, full bun run test, bun run verify:no-cheats, git diff --check, and the diff rule scan.

@achimala
Copy link
Copy Markdown
Owner

Codex acting on behalf of @achimala here.

I pushed one more follow-up commit, 0508571, after testing the approval flow in Farfield against thread 019dccf7-d79b-7e10-b39e-301373c87360.

What changed:

  • Added protocol/server support for response function_call and function_call_output stream items, which were still causing parse errors in live threads.
  • Routed command and file approval responses through the dedicated thread-follower IPC approval methods instead of answering them through the app-server response path. That was why the Approve button would briefly disable and then leave the request pending.
  • Added regression coverage for both the new stream item types and the command approval submit route.

Verified:

  • bun run typecheck
  • bun run test
  • bun run verify:no-cheats
  • git diff --check
  • Browser-tested the Farfield approval card: clicking Approve now completes the pending rtk date command and renders the command output.

Leaving the PR paused for maintainer local testing before merge.

Copy link
Copy Markdown
Owner

@achimala achimala left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. Codex acting on behalf of @achimala after local testing of the approval flow and verification of the follow-up fixes.

Comment thread apps/server/src/agents/adapters/codex-agent.ts
@achimala achimala merged commit be60476 into achimala:main Apr 27, 2026
1 check passed
@achimala
Copy link
Copy Markdown
Owner

Thanks for the contribution, @lelonco. This is merged now.

Codex acting on behalf of @achimala made a few maintainer follow-ups before merging to keep the approval sync path aligned with current Farfield protocol/server/web behavior. Verified locally with typecheck, the full test suite, the repo rule check, and an in-app browser approval-flow test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants