Repository navigation
Fix post-refactor permission and feed rendering regressions - #2
Merged
Merged
Conversation
…l unconditionally The optimistic-user row is appended directly to `runtime.entries` by addOptimisticCodexUserEntry on submit. The reconciliation path in the Codex rollout ingest was supposed to drop it when the committed user entry arrived, but the check was gated behind `appended.length === 0` — "only look at the current.entries tail if we haven't appended anything in this burst yet." That's wrong when Codex emits the `<environment_context>` synthetic user message BEFORE the real user message. The env_context is a non-matching user entry that gets appended first; when the real user message lands next, `appended.length > 0` takes the if-branch, finds env_context (not optimistic) on the appended tail, and never touches `current.entries`. The optimistic row survives and the user sees their prompt rendered twice — the top-of-feed dup in the 2026-04-23 Codex pane screenshot. Fix: run both checks independently. They target different data (appended[] is this burst's accumulator, current.entries is the pre-burst state), so there's no logical conflict. The current.entries tail is always a candidate for removal when we see a matching committed user message, regardless of what else the burst already appended. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…response_items
Three improvements to the Codex rollout → feed-entry mapper, all
in workspace/codex/rollout.ts.
Env_context filter. Codex injects a synthetic user message on every
conversation that wraps cwd / shell / timezone / date metadata
for the model (`<environment_context>…</environment_context>`). It
landed in the feed as a real user bubble, which is what the
top-of-feed dup in the 2026-04-23 Codex pane screenshot came
from (three stacked user rows: optimistic + env_context + real).
Drop at the mapper so the shim never enters runtime.entries;
downstream surfaces (visibleDecisions, debug panels, copy-
assistant picker) don't have to know about it. Match permissively
on the outer tag so variations across Codex versions still
catch, and require the message content to be just one env_context
block before filtering — a real user prompt that quotes the tag
still renders.
Refusal preservation. The message-content filter previously only
accepted blocks with a `.text` field. `CodexContentItem` also
allows `{ type: 'refusal'; refusal: string }` (per
packages/codex-headless/src/transcript/TranscriptTypes.ts:108);
those items have their payload under `.refusal`, not `.text`, so
refusals silently vanished from the committed feed even though the
live semantic renderer painted them. Map to a text block prefixed
"(refused) " so the row reads cleanly without needing its own row
component.
New response_item mappers. SemanticLiveBlockRow has explicit live
UI for web_search_call, image_generation_call, local_shell_call,
and tool_search_call (plus the tool_search_output result variant).
The rollout mapper used to `return []` for all of them, so those
blocks rendered once live and disappeared on commit / reload.
Synthesize Claude-shaped tool_use / tool_result entries so the
existing CodexToolRow / CodexToolResultRow dispatcher paints a
committed row. The headlines don't match the specialized live
UI exactly (web_search emoji, image-gen chip, shell prefix) —
consider a dedicated CodexSpecialToolRow in a follow-up — but
the block no longer disappears on commit and the data round-
trips through disk for reload parity.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ds in pending-tools Two changes to the live-turn rendering + lifecycle, sharing the same theme (live vs committed ownership). Live-block suppression against the committed tool_use index. SemanticStreamingTurn used to render every block in `semantic.currentTurn.blocks` unconditionally, even ones whose `toolUseId` / `callId` already existed as a committed tool_use entry in the main feed above. The 2026-04-23 debug bundle showed seven exec_command rows painted at the bottom of a Codex pane while their committed counterparts (call_8jIH…, call_b5Dv…, …) had already rendered inline — because the live turn stays mounted for the full multi-minute agent turn and keeps painting the block list long after individual tool rounds commit. buildSemanticRenderUnits now takes an optional committed ToolUseIndex and skips any block whose tool correlation id is already there. The check looks at both Claude's `toolUseId` and Codex's `callId` (see workspace/codex/entries.ts::codexToolUseEntry for why the committed Codex index is keyed by call_id). Text and thinking and citation blocks carry no tool id and pass through unchanged — the assistant-text-in-live-vs-committed dup is a separate class, handled by a future committed-tail guard. StreamingTurn.tsx pulls the committed index from ToolUseIndexContext — already provided by Feed at a higher scope, so no new prop is needed. The filter runs purely at render time: the live turn's state shape is unchanged, and when the turn's own `currentTurn` clears the filter goes with it. Pending-tools covers Codex kinds. hasPendingSemanticTools only returned true for Claude's `tool_use` / `server_tool_use` / `mcp_tool_use` kinds. Codex blocks are `function_call` / `custom_tool_call`, which the check trivially returned false for. If turn_completed ever fires on a Codex turn with in-flight function calls, the turn seals while results are still pending — late results land in a closed turn. Doesn't bite today because CodexResponsesAdapter isn't emitting turn_completed at all (the separate watchdog fix in packages/codex-headless), but the check should be right independently. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ith output Two independent render-polish fixes in the feed. WorkIndicator on empty feed. The early-return branch at `visible.length === 0 && !hasSemanticStreaming` used to render only the "waiting for Codex…" / "waiting for Claude Code…" placeholder, swallowing the WorkIndicator during `submitting` / `requesting` / early `thinking`. A fresh submit on a new session looked like a silent stall until the first delta landed. Reuse the same positioning the non-empty branch uses — WorkIndicator at the natural composer-adjacent bottom, wrapped in a column flex so the placeholder text centers above it. Semantic collapse skips blocks with real output. classify- SemanticToolActivity marked Read / Grep / Bash / list tools collapsible unconditionally, so finished blocks with meaningful `resultContent` or a `resultIsError` got folded into "worked: 3 reads, 1 search" — the content the user cares about disappeared. Gate the classifier so collapse only fires on blocks without resolved output; running churn and stub-success results still collapse, but a tool that actually produced something renders as its own SemanticLiveBlockRow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…g cursor post-append Two small correctness fixes on workspace-level bookkeeping. Older-history pagination trusts the chunk's own hasMore signal. The old rule OR'd in `prepend.length === 0`, i.e. "re-enable loading when nothing renderable came back." That legitimately loops forever when the loader returns a final chunk whose entries are all non-renderable (Codex metadata: turn_context, session_meta, event_msg variants the mapper drops). Those chunks carry `hasMore: false`; honoring that ends the pagination even when prepend is empty. If a chunk with `hasMore: true` produces zero renderable entries we still fall through with hasOlderHistory=true and the user can request the next one. Feed-debug persistence advances the cursor after the IPC append resolves, not before it fires. The old ordering advanced the `persistedFeedDebugIdRef` optimistically; a transient failure (disk full, IPC timeout, main-process mid-restart) would mark the entries as persisted and the next effect pass would skip them, permanently dropping that window of debug logs. Moving the assignment into `.then()` leaves the cursor at its previous value on failure so the next runtime update retries. The main-side appender is idempotent on (sessionId, id) because entries are written append-order with monotonic ids, so duplicate writes from a retry just no-op at the file layer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Points packages/codex-headless at Juliusolsson05/codex-headless branch fix/proxy-flow-watchdog (commit aa0e187). The only change is CodexResponsesAdapter.ts: add a watchdog that forcibly releases a silent activeFlowId after 60s, with a 10s poll interval. Fixes the underlying cause of the frozen-live-turn scenario the earlier Feed.tsx fix in this branch only half-addressed: the A3 renderer filter hides duplicate live blocks after commit, but the turn itself used to stay mounted indefinitely because the flow slot never cleared. With the watchdog, a dropped upstream now surfaces as a sealed turn within ~70s instead of freezing the session's semantic view for the rest of the run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Jul 6, 2026
Juliusolsson05
added a commit
that referenced
this pull request
Jul 20, 2026
… imports Three bugs, all found by actually running it. The symptom was an empty modal with no error anywhere. 1. PER-MOUNT ELEMENT. React.StrictMode runs effects mount -> cleanup -> mount. An extension whose disposer defers its unmount — queueMicrotask, requestAnimationFrame, a transition — tears down root #1 AFTER root #2 has rendered into the same node, and root1.unmount() empties the element root2 lives in. The view mounts "successfully" and shows nothing. The timer extension defers exactly this way, which is how it surfaced. Each mount now gets its own child element, discarded on cleanup, so a late disposer operates on a detached node it cannot reach anything through. That makes the host immune to extension cleanup timing rather than requiring every extension to get it right. 2. NO SILENT EARLY RETURN. `if (!element) return` left status at whatever it started as — and when the extension already activated at startup that is 'ready', producing a visible empty view with nothing logged. Any exit from the mount path now sets a status. 3. CACHE-BUSTED IMPORT URL. The ES module registry caches by URL and caches FAILURES: once import(url) rejects, every later import of that URL returns the same rejection for the life of the realm, even after the file on disk is replaced. So an extension that failed once could never be fixed by reinstalling — the user updates it, sees the identical error, and has no signal that their fix landed. Reloading the window was the only escape and nothing said so. The key is version + the installer's sha256, which changes exactly when the bytes change. The query never reaches the filesystem: the scheme handler builds its path from url.pathname, which excludes the search string. typecheck / 1187 tests / build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Sep 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Dependency
Verification