Skip to content

feat(session-ui): show when a turn finished in the response footer - #62345

Open
lsequeiraa wants to merge 3 commits into
anomalyco:devfrom
lsequeiraa:turn-finish-time
Open

lsequeiraa wants to merge 3 commits into
anomalyco:devfrom
lsequeiraa:turn-finish-time

Conversation

@lsequeiraa

@lsequeiraa lsequeiraa commented Nov 6, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #62344

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

The response footer shows agent, model and duration on hover, but not when the turn finished. This adds the finish time to the end of that line: a bare time for today (14:32), Yesterday, 23:10, then Nov 4, 9:15, with the year once it differs.

The time is the completion time of the same step the duration measures to, so prompt time + duration = the time shown. session-timeline-row.tsx passes it down as turnCompletedAt; outside the timeline the footer falls back to its own message's completion time, as the duration does.

The user message menu already had the Today / Yesterday / date formatting, so I moved it into message/message-time.ts and use it in both places, with a bareToday option for the footer. Both labels read the day from a shared clock (useToday() in relative-time.ts), so they say Yesterday after midnight in a session left open overnight; before this, the menu kept saying Today. The clock ticks every 30 seconds and when the page is shown or focused again, since timers stop while the machine sleeps. A tick only redraws labels when the date changes, so its cost doesn't grow with the number of messages. Formatting comes from Intl, so there are no new strings to translate. The time doesn't shrink; the model name truncates first in a narrow footer.

I didn't add a feature flag, since this only adds a read-only label to an existing footer and there is no client-side flag path for session-ui today. Happy to gate it if you'd rather.

How did you verify your code works?

  • bun run test in packages/session-ui, including a new message-time.test.ts that passes the current day in (just after midnight, yesterday, earlier this year, last year, and bareToday).
  • bun typecheck in packages/session-ui and packages/app, and bun run typecheck:tsc in packages/session-ui.
  • Component tests session-message-projection.spec.ts and session-timeline-mobile.spec.ts pass. I added a datetime check to the response-speed test; it fails if the footer shows the wrong message's time. A new story test moves Playwright's clock past midnight and expects both labels to say Yesterday; it fails if either label reads the clock only once. Another pauses the clock, moves the date on and fires visibilitychange and focus; it fails if either listener is missing.
  • Live: the dev app against an isolated serve, with an imported session whose turns finished two days ago, last night and today; also at 390px wide and with the footer squeezed to check truncation.

Visual

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="72" viewBox="0 0 240 72" fill="none" font-family="ui-sans-serif,system-ui,sans-serif" font-size="11">
  <style>.t{animation:t 4s ease-in-out infinite}@keyframes t{0%,20%{fill-opacity:0}40%,85%{fill-opacity:.55}100%{fill-opacity:0}}@media (prefers-reduced-motion:reduce){.t{animation:none}}</style>
  <rect x="20" y="17" width="168" height="6" rx="3" fill="currentColor" fill-opacity=".3"/>
  <g stroke="currentColor" stroke-opacity=".5"><rect x="20.5" y="43.5" width="7" height="8" rx="1.5"/><path d="M23.5 41.5h5a1.5 1.5 0 0 1 1.5 1.5v6"/></g>
  <text x="38" y="51" fill="currentColor" fill-opacity=".55">Build · Opus 5.5 · 6m 28s<tspan class="t"> · 14:32</tspan></text>
</svg>

Screenshots / recordings

Before:
app-before-crop
After:
app-after-crop

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #53192, #35348

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The change looks sound. The finish time comes from the same last completed step as the duration, so prompt time + duration equals the time shown. It only renders where the copy footer already does, and the user message menu's formatting moved over unchanged. I ran bun test src/message and tsc -b in packages/session-ui, and both pass. I couldn't launch a browser here, so I didn't run the component specs or try the footer live.

Public API: unchanged. The only new prop is the internal turnCompletedAt on SessionAssistantContent/AssistantTextContent, and message-time.ts is a new internal helper.

One concern is worth fixing (see the inline comment): a time shown without a date doesn't update after midnight, so a session left open overnight shows yesterday's replies as if they finished today.

Related: #53192, #35348

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

My earlier midnight concern is resolved. The footer time and the menu's day name now come from useToday(), a memo over the existing shared 30-second useNow() clock. That clock runs one interval for every label on screen, so each footer adds no timer of its own, and labels redraw once at local midnight. The unit tests now pass a fixed day instead of faking the system clock, and the new story runs in the describe block that sets the timezone to UTC, so its assertions don't depend on the machine's timezone. I ran bun test src/message and tsc -b in packages/session-ui, and both pass. The component specs didn't run here because no browser could launch.

Public API: unchanged. The new useToday() and the extra today argument to messageTime are internal to session-ui.

No further concerns.

Related: #53192, #35348

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The midnight concern stays fixed, and the labels now also catch up after the machine sleeps. The current day is one shared signal, set in the same tick as the shared clock, so labels only redraw when the date changes. The clock also ticks on visibilitychange and focus; those listeners are added for the first label on screen and removed after the last one, alongside the interval. I ran bun test src/message and tsc -b in packages/session-ui, and both pass. The component specs couldn't run here because no browser could launch.

Public API: unchanged.

No further concerns.

This branch has not been deployed

No deployments
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.

[FEATURE]: Show when a turn finished in the response footer

1 participant