Uh oh!
There was an error while loading. Please reload this page.
feat(mobile): update tool summaries and chat transitions - #223
Merged
Conversation
Adapted for Pylon. Adopted: the work-log presentation and command-label logic move into client-runtime, which is genuine convergence - web's MessagesTimeline drops its local commandProgramName and the shell tokenizer behind it in favour of the shared module. Also adopted are the thread feed's disclosure enter/exit transitions and the FloatingWorkingControl extraction, which C-21 builds on. Not adopted: upstream's composer toolbar restructure. Pylon's mobile composer is a deliberate fork carrying QuickQuestionTrigger, ContextWindowIndicator, the follow-up queue pill and ComposerToolbarScroller, and upstream's new View nesting cannot hold them without losing behaviour. Pylon's ThreadComposer is kept whole and now exports its layout transition constants for the detail screen. The two upstream-only couplings that came with it - a fifth argument to useKeyboardChatComposerInset and a queueCount composer prop - are dropped with it, since both exist only to drive upstream's composer animation. Restored after the cherry-pick dropped them through non-conflicting hunks: the session-interaction surfaces on ThreadDetailScreen. Taking upstream's version silently removed the above- and below-editor SessionPresentationSurface blocks, PendingSessionInteractionCard, the third pending-interaction condition, sessionInputBlocked, the below-widget bottom-inset gate, and 23 composer props. thread-work-log keeps Pylon's amber-warning / rose-error split rather than upstream's collapse of both into destructive, and does not adopt its xmark failure glyph - that would move mobile opposite to the circle-alert decision web already made. Upstream's live shimmer and grouped-detail icon gating are taken.
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Adversarial review of the port turned up two Pylon features the merge dropped and four defects inherited from upstream's own implementation. Pylon regressions: - `showWorkingControl` gated on upstream's two pending kinds only, so the floating pill kept counting "Working for Xs" while a Pylon session interaction waited on the user, and kept 52px of coverage in the feed inset. The third condition was restored in the JSX but not in the gate. - Upstream removed the trailing status column and moved failure onto an xmark glyph; the port took the removal and declined the replacement, so failed tool calls rendered identically to successful ones for sighted users. Failed rows now paint destructive, matching web's reserve of red for failures and amber for warnings. - The terminal-response notice was regrouped into a work toggle that announces "show 1 tool call". Only the info-toned outcome was affected, so one feature rendered as two different row species. Upstream defects, fixed with regression tests that fail without them: - At the active tail the finished activity won over the still-running one, naming a command that had already ended and leaving shimmer off. - All-non-tool groups of two or more collapsed to "Received N updates", discarding the only text a runtime warning carries. Web documents the opposite rule and follows it. - The shared `workLogEntryIsToolLike` dropped web's missing-response guard, so an error-toned notice summarised as "Used 1 tool". Also completes the composer coupling: `COMPOSER_TRANSITION_DURATION_MS` was exported with no consumer while the adopted patch gated its animation on `animationDuration > 0`, leaving both dead. The constant now drives the composer's own transition and is passed to `useKeyboardChatComposerInset` so the feed inset glides with the card on iOS.
Upstream grew all three work-log elements together; the port took its metrics for the live row and the group toggle but kept Pylon's denser settled row. A tool call therefore shrank from text-sm/24px to text-xs/18px the instant it completed, and the row wrapper carries a layout transition, so the shrink animated. The live row now tracks the settled row exactly. Also corrects the pre-measurement comment, which still described the rows as text-sm, and drops a leftover alias left behind by the deleted visibleWorkLogActivities call.
The vendored @legendapp/list patch hard-gates doMaintainScrollAtEnd on contentSizeAnimationActiveEpoch and clears pendingMaintainScrollAtEnd on the way out, so a queued end-follow is dropped rather than deferred. The branch two lines below, for pendingNativeMVCPAdjust, does the opposite and carries the request forward. Nothing re-triggers after the animation completes, and eligibility re-arms on every content-size bump while an animation is running, so a user who scrolls up mid-stream and then taps scroll-to-end can have the feed stop following the agent for the rest of the burst. Both the epoch machinery and the itemLayoutAnimation prop that arms it are new here, so the exposure is new too. Carry the request forward instead. The consumers at react-native.js:1840 and :2141 re-check isWithinMaintainScrollAtEndThreshold, so a deferred follow cannot fire once the user has scrolled away. Applied to both the CJS and ESM builds; lockfile hashes recomputed.
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 freeto 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.
First of the mobile batch. Adopted from upstream
pingdotgg/t3code#8793(f15680bd3).What this buys
Partial convergence. Two modules move into
packages/client-runtime/src/work-log/, but only one of them actuallyconverges anything, and an earlier draft of this description overstated it.
command-labelgenuinely converges: web'sMessagesTimelinedeletes its owncommandProgramNameand the shell tokenizer behind it (tokenizeShellCommand,COMMAND_WRAPPER_FLAGS,isWindowsDrivePath, and friends) and imports theshared one. Two copies become one, verified byte-identical to what was deleted.
presentationdoes not. Mobile imports it; web still owns verbatim copies ofsummarizeToolGroup,toolGroupAction,normalizeCompactToolLabel,omitSupersededLifecycleMarkersand the rest inMessagesTimeline.logic.ts.Nothing was deleted on the web side, so that module is currently a third copy.
Upstream has the same gap. Collapsing it is follow-up work, not this PR.
It also brings the thread feed's disclosure enter/exit transitions and extracts
FloatingWorkingControl, which C-21 (#8862, Expo glass) needs — that PRmodifies the file this one creates.
What Pylon does not take
Upstream's composer toolbar restructure. Pylon's mobile composer is a
deliberate fork carrying
QuickQuestionTrigger,ContextWindowIndicator, thefollow-up queue pill and
ComposerToolbarScroller; upstream's newViewnestingcannot hold them without losing behaviour. Pylon's
ThreadComposeris kept wholeand now exports its layout-transition constants for the detail screen. The two
upstream-only couplings that arrived with it — a fifth argument to
useKeyboardChatComposerInsetand aqueueCountcomposer prop — go with it,since both exist only to drive upstream's composer animation.
Its failure glyph.
thread-work-logkeeps Pylon's amber-warning / rose-errorsplit rather than upstream's collapse of both into destructive, and declines the
xmarkswap — taking it would move mobile in the opposite direction from thecircle-alertdecision web just made. Upstream's live shimmer and grouped-detailicon gating are taken.
Restored after the cherry-pick dropped them silently
Taking upstream's
ThreadDetailScreenremoved a set of Pylon surfaces throughnon-conflicting hunks, so no conflict marker and no type error flagged them.
Found by diffing symbol counts against
origin/pylon:SessionPresentationSurfaceblocks (above- and below-editor widgets)PendingSessionInteractionCardand the thirdactivePendingInteractionbranchsessionInputBlockedon the composerhasBelowEditorWidgets ? 0 : composerBottomInsetgate, without which thecomposer double-pads when below-editor widgets are present
ThreadComposercall site had been replacedby upstream's much shorter one
This is the same failure mode that bit #221, so I audited for it deliberately
rather than trusting the conflict list.
Verified:
@t3tools/mobile,@t3tools/weband@t3tools/client-runtimetypecheck clean;
vp lintclean; 390 tests passing acrossapps/mobile/src/libandapps/mobile/src/features/threads, plus web'sMessagesTimeline(43) unaffected.Review fixes (second commit)
Adversarial review found two Pylon features the merge dropped and four defects
inherited from upstream. All are fixed in
c4e286be0; see that commit messagefor the full accounting. The short version:
interaction, because the new gate only knew upstream's two pending kinds;
removal of Pylon's status column but declined its replacement glyph;
row named a command that had already ended and shimmer stayed off;
only text a runtime warning carries (web documents the opposite rule);
workLogEntryIsToolLikedropped web's missing-response guard.The two upstream behaviour fixes ship with regression tests that were confirmed
to fail without them. One pre-existing test assertion here was tautological —
JSON.stringify(entry)contains the message viaworkEntry.labelno matterwhat the row displays — and is replaced with the precise check it had before.
It also completes a half-taken coupling:
COMPOSER_TRANSITION_DURATION_MSwasexported with no consumer while the adopted patch gated its animation on
animationDuration > 0, so both the export and the patch parameter were dead.The constant now drives the composer's own transition and is passed through to
useKeyboardChatComposerInset.Vendored patch fix
doMaintainScrollAtEndin the adopted@legendapp/listpatch hard-gates oncontentSizeAnimationActiveEpochand clearspendingMaintainScrollAtEndon theway out, dropping a queued end-follow instead of deferring it — where the
pendingNativeMVCPAdjustbranch two lines below carries the request forward.Nothing re-triggers afterwards, and eligibility re-arms on every content-size
bump while an animation runs, so scrolling up mid-stream and then tapping
scroll-to-end could stop the feed following the agent for the rest of the burst.
Both the epoch machinery and the
itemLayoutAnimationprop that arms it are newhere, so the exposure is new. Changed to defer; the consumers at
react-native.js:1840and:2141re-checkisWithinMaintainScrollAtEndThreshold,so a deferred follow cannot fire once the user has scrolled away. Applied to both
CJS and ESM builds with lockfile hashes recomputed.
Mechanism confirmed by reading the applied bundle; not reproduced, since that
needs a live streaming turn.
Simulator pass
Run on an iPhone 17 Pro against an isolated backend seeded read-only from
~/.pylon-codeviaVACUUM INTO. Verified: the work-log disclosure expandswith correct
Ran N command(s)grouping andexpandedstate; Pylon's composertoolbar is intact and still horizontally scrollable; the feed's bottom inset
clears the composer with no clipping and no phantom gap in both collapsed and
expanded states; and the working-timer branch renders with content correctly
cleared, including the timer/scroll-to-end separation animation. The working
branch needs a live turn, so it was exercised by temporarily forcing
showWorkingControlon and then reverting — no such change is in the diff.Not covered: Android, and a real provider turn.
Reviewed and integrated with Claude Opus 5 in Claude Code.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.