The cloud session mirror reads the page's turns, not a page dump (#1225) - #1671
Merged
Conversation
claude.ai renders no <article> elements, so the mirror never found a message and fell to its plan B: the whole conversation region as one 8000-character tail — the run's prompt, the navigation labels and the exchange somewhere at the end, with no boundaries. The page does mark its turns: one `transcript-row` per turn in a `role="feed"`, each naming its position (`data-index`) and kind (`data-perf-row`: human / assistant / marker). The content script now reads those: one entry per user or session turn under the page's own position, markers skipped, text head-capped per turn (only the prompt turn is ever that long), streaming replies re-sent in place. The page-dump fallback is gone — a page with no turn rows mirrors nothing and says so, rather than mirroring whatever is on screen. The same row is now the "opening message" the #1568 decoy exclusion scopes by, which on a live page it never found. The dashboard renders by role: the user's turns as one "you ›" line (the opening turn is the whole prompt), the session's in full, the newest kept in view. Extension 0.10.0, daemon lockstep bumped; check.mjs fixtures move to the real row shape and cover the mirror. Addresses #1225 (item 1; item 2, the front cut, goes with it).
Contributor
Author
|
🤖 automated Dogfooded end to end on a live Opus gemstack web run (
|
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.
🤖 automated
Addresses #1225 — item 1 ("it dumps the whole page, not the conversation"); item 2 (the 8000-char cap cutting from the front) goes with it, since the cap is now per turn.
What was wrong
The content script looked for
<article>elements to split the conversation. claude.ai renders none (articles 0in every hello note), so the mirror always fell to its plan B: the whole conversation region as one 8000-character tail. What the dashboard showed was the run's own prompt, the sidebar labels, and the actual exchange somewhere at the end, with no turn boundaries.What the page actually marks
Inspected on a real session (
session_01HgamNnpzgVyp8TUoWt7fRM): the transcript is adiv[role="feed"]("Chat messages") ofdiv[data-testid="transcript-row"], one per turn, each carryingdata-index(position),data-perf-row(human/assistant/marker) anddata-perf-row-streaming. Row 0 is the run prompt (11.5 k chars); the exchange is rows 2–5. The list is virtual — a long session keeps only its tail rendered.What changes
Extension
0.9.0 → 0.10.0(packages/chrome-extension, daemon lockstep bumped)transcript()reads the rows:seq = data-index,human → user,assistant → agent, markers skipped; per-turn text cleaned (icon glyphs, blank lines) and head-capped at 8000 — only the prompt turn is ever that long. Streaming replies are re-sent in place (the store already keys by position).no transcript rows found. A layout change is a named state, not 8 k of sidebar that reads like output.rows 8 (human,marker,assistant), turns 4), so a new kind shows up by name.findPendingChoicescoped "the opening message" bydeepQueryAll('article')[0]—undefinedon every live page, so the Bridge false-positive: the system-prompt template renders as a real question when the session asked nothing #1568 decoy exclusion by position never ran there andisTemplatecarried it alone. The opening message is now thedata-index="0"row.check.mjs: fixtures move to the real row shape; three new cases — one entry per turn with roles mapped / markers skipped / prompt head-capped; positions from the page when only the tail is rendered; no rows → nothing mirrored and the panel says so. Break-checked (positions from DOM order fails).Dashboard (
CloudAgentNotice.tsx)you ›line (first line, whole turn on hover — the opening turn is the entire prompt), the session's in full; the box scrolls to the newest turn whenever it changes or grows.scrubMirrorTextstays (per-message action labels still ride in row text). One new test, break-checked.No change to
/_bridge/eventsor the store — theseq/role/textcontract already fit.SPEC.md changes
packages/chrome-extension/content.SPEC.md— the page half of the bridge.TL;DR, two lines replaced:
"Mirroring the transcript" rewritten (rows, kinds, positions from the page, streaming replace, nothing-when-no-rows, what the self-report carries; the rationale records why the dump was dropped). The "Mirroring a page with no message blocks" section is removed. In "Telling a real question from the protocol's own examples", the position rule now names the opening turn as position zero and says what happens when a virtual list has scrolled it away. The panel's "how many message blocks" → "how many turn rows".
packages/chrome-extension/SPEC.md— one phrase: the transcript is mirrored "turn by turn, as it is written".packages/chrome-extension/check.SPEC.md— the #1568 bullet speaks of the opening turn; a new "Mirroring the transcript" group lists the three cases above.packages/framework/dashboard/components/CloudAgentNotice.SPEC.md— TL;DR and glossary say "turn by turn"; the mirror section gains:+Each turn is its own block, in transcript order. The user's turns show as a single line — "you ›" followed by the turn's first line, the whole turn available on hover — because the opening turn is the run's entire prompt and what the session did is the point of the mirror, not what it was told. The session's turns are shown whole. The box keeps its newest turn in view, scrolling to the end whenever the last turn changes or grows, so a reply being written is watched rather than scrolled to.FEATURES-SPEC.md— the mirror had no entry; added under Remote execution and sharing:+- A cloud session's conversation mirrored into the run view, turn by turn, as it is writtenVerified
node check.mjs: 20/20.pnpm build/typecheck/test: 1581 daemon + 804 dashboard, green.