Skip to content

fix: dedupe codex app-server reconciled assistant text - #427

Merged
dimavedenyapin merged 1 commit into
mainfrom
fix/codex-appserver-assistant-reprint
Jul 22, 2026
Merged

fix: dedupe codex app-server reconciled assistant text#427
dimavedenyapin merged 1 commit into
mainfrom
fix/codex-appserver-assistant-reprint

Conversation

@dimavedenyapin

@dimavedenyapin dimavedenyapin commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fixes the remaining Codex app-server transcript re-print paths that PR fix: stop Codex app-server transcript repeating older messages after idle #419 did not cover.
  • Live assistant deltas now register their normalized text in the per-turn assistant text dedupe set.
  • Completed-turn reconciliation now maps reasoning items back to the same reasoning-<itemId> id and REASONING type as live reasoning deltas.
  • When turn/completed reconciliation re-lists content already shown live, the adapter suppresses it instead of appending previous messages after the final UI message.

Root cause

PR #419 made thread item reconciliation idempotent for id-less thread items by giving those items stable synthetic keys. That covered reconcile-vs-reconcile re-buffering of the same thread item across idle transitions.

The remaining regression is a different class: live-stream-vs-reconcile identity mismatch. During a turn, app-server streams content live. When turn/completed fires, reconcileCompletedTurn() re-lists the whole thread and can surface the same logical content through a different item shape/id/type. The frontend dedupes by part id/update semantics, so different part ids/types get appended.

Two concrete paths are covered:

  • Assistant text: live item/agentMessage/delta emits agent-<streamId>, then reconcile can return the same assistant text as agent-<persistedId>. The text guard missed this because live deltas did not register their text by turn.
  • Reasoning: live item/reasoning/textDelta emits reasoning-<id> as REASONING, then reconcile previously fell through convertThreadItem() to the generic tool branch and emitted tool-<id> as TOOL.

Evidence

  • Added regression tests for both reproduced paths:
    • does not reprint assistant text when reconcile returns a different completed item id
    • does not reprint reasoning as a tool when completed turn reconcile lists it
  • Mutation check for assistant path: temporarily removed markAssistantTextForTurn(...); the test failed by emitting duplicate agent-reconciled-assistant-1 with the already-rendered assistant text.
  • Mutation check for reasoning path: temporarily removed the reasoning branch; the test failed by emitting duplicate tool-reasoning-1 as a completed tool.
  • Investigation subtask independently reached ready_for_review and reproduced the same two root-cause paths.

Test plan

  • source ~/.nvm/nvm.sh && nvm use 22.22.0 >/dev/null && pnpm exec cross-env ELECTRON_RUN_AS_NODE=1 electron ./node_modules/vitest/vitest.mjs run --project main src/main/adapters/codex-app-server-adapter.test.ts -t "does not reprint"
  • source ~/.nvm/nvm.sh && nvm use 22.22.0 >/dev/null && pnpm exec cross-env ELECTRON_RUN_AS_NODE=1 electron ./node_modules/vitest/vitest.mjs run --project main src/main/adapters/codex-app-server-adapter.test.ts
  • source ~/.nvm/nvm.sh && nvm use 22.22.0 >/dev/null && pnpm test:main -- src/main/adapters/codex-app-server-adapter.test.ts (script selected full main project: 51 files, 918 tests passed)
  • source ~/.nvm/nvm.sh && nvm use 22.22.0 >/dev/null && pnpm typecheck
  • source ~/.nvm/nvm.sh && nvm use 22.22.0 >/dev/null && pnpm lint (0 errors, existing warnings only)

@dimavedenyapin
dimavedenyapin force-pushed the fix/codex-appserver-assistant-reprint branch from bdbf1dc to a843b1d Compare July 22, 2026 04:54
@dimavedenyapin
dimavedenyapin merged commit a3d098f into main Jul 22, 2026
1 check passed
dimavedenyapin added a commit that referenced this pull request Jul 27, 2026
…nflicts)

Brings the transcript stack up to date with main (10 commits: PostHog
analytics #424/#425/#426, codex fixes #423/#427, releases) and resolves the
conflicts for the #421 → main merge.

Key resolutions:
- agent-manager.ts resumeAdapterSession / replayMissedTranscriptPartsBeforeIdle:
  keep our event-sourced model (resume never replays history to the renderer;
  clients render the durable projection) AND keep main's #427 assistantTextKeys
  codex reconciled-text dedup. In the idle-replay path both dedup layers now
  run — main's assistantTextKeys AND our projection-content dedup — which are
  complementary against the codex agent-msg_/agent-item id-scheme mismatch.
- Renderer + mobile agent-store: took our projection-cache rewrite and re-applied
  main's PostHog telemetry (agent_session_status_changed, agent_output_batch_received,
  agent_approval_requested, agent_created/updated/deleted, agent_session_stopped).
- use-agent-session.ts: kept the "Starting…"/pendingSend send flow AND main's
  agent_message_sent analytics in both send branches.
- agent-manager.test.ts: kept BOTH codex-dedup regression tests (projection-content
  and assistantTextKeys) as separate cases, since both mechanisms now exist.

Verified: typecheck clean, build ✓, full suite 1579 passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant