Skip to content

feat(frontend): Show startup states before an agent streams - #6073

Merged
bekossy merged 9 commits into
release/v0.112.2from
feat/agent-startup-states
Aug 19, 2026
Merged

feat(frontend): Show startup states before an agent streams#6073
bekossy merged 9 commits into
release/v0.112.2from
feat/agent-startup-states

Conversation

@ashrafchowdury

Copy link
Copy Markdown
Contributor

Closes #6047

Context

Starting a new agent session takes around 15 seconds while its sandbox boots, worst with Claude Code. For that entire wait the chat rendered @ant-design/x's Bubble loading: three animated dots and no words. The session looked stalled, and the only way to know it wasn't was to keep waiting.

A note on the issue text, since a reviewer checking against it will notice. The issue says the chat "shows elapsed time". It does not. There is no timer anywhere in AgentChatSlice; the current state is the wordless dots. Requirement 4 ("do not show the elapsed-time treatment") is honored here in the sense that no number is ever rendered.

Changes

The empty assistant turn now says what is happening, and advances as the wait goes on.

At Label
0s Working
2s Starting the agent
8s Preparing instructions and tools
14s Almost ready

The line clears the instant real content arrives, and equally on an error or a Stop. A warm session skips the ladder entirely and keeps today's dots, gated on nest.isAlive from the session-liveness poll the tab dots already run.

These labels are timed guesses, not observed state, and that is deliberate. Nothing client-side can distinguish a cold sandbox boot from ordinary model latency: both occupy the same window between the stream's start chunk and its first content chunk. Reporting real per-phase progress would need the runner to emit startup events, which is a cross-stack change this PR does not make. The copy is written to fit that constraint rather than to overstate what we know.

Two properties are load-bearing and worth understanding before editing the ladder:

"Working" sits at 0ms with no grace window because it is the one line true of every turn, warm or cold. That is what makes an immediate label safe. A turn this code misjudges as cold and which answers in 300ms still only ever said something correct. A first label claiming a startup step would not survive that case, so a test pins both the 0ms mark and the word.

The copy avoids our own vocabulary. "Sandbox" and "harness" name our infrastructure, not anything a reader has a model of, and "workspace" is already taken in this product for the org-level container. A test fails if any label reintroduces those terms.

The label is derived on every render off a per-turn clock atom, never stored, so it cannot lag the clock. The effect exists only to force a render at each boundary, and it sleeps exactly that long instead of polling. Once the last phase lands it stops scheduling, so a turn that then runs for minutes costs no timers.

Tests

  • 27 unit tests across startupPhases.test.ts, turnClock.test.ts, and TurnActivity.test.tsx. All pass.
  • tsc --noEmit on web/oss: 0 errors. eslint clean on every touched file.
  • The five cases the issue asks for (cold, warm, failure, cancellation, and the transition into streaming) are asserted as pure functions rather than through a render loop. This repo has no @testing-library/react, so component tests use renderToStaticMarkup. That constraint is why StartupActivity takes its label as a prop and owns no timer.
  • Two review passes ran over the diff. The first caught a real bug: starting the clock bailed out when an entry existed, so a resumed turn could inherit the previous turn's start time and open on the final phase. It now starts only on the submitted edge and always replaces.

What to QA

  • Create a new agent session, send hi, and watch the line advance. It should reach "Starting the agent" at about 2s and vanish the moment the answer begins.
  • Send a second message in the same session. You should see the plain dots only, no labels.
  • Send on a cold session and hit Stop mid-boot. The label must disappear immediately, with no "Working" left behind.
  • Force a failure (disconnect the model provider). The error bubble replaces the label, again with nothing stale left over.
  • Check dark mode and prefers-reduced-motion. With reduced motion the shimmer stops and the label renders as plain text; it must not come out invisible.
  • Regression: confirm a settled turn, a turn that returns no output, and a turn parked on an approval all render exactly as they did before.

Notes for the reviewer

The timings are calibrated to a design prototype's clock, not to measurement, so they want a human eye on a real cold start. The 2s beat is the one most likely to feel early: the sandbox is genuinely still booting then, so "Starting the agent" arrives slightly ahead of the truth.

The issue words the first two states as "Sandbox starting" and "Sandbox started". We moved off that wording on purpose, because "sandbox" means nothing to a non-technical user.

A cold agent session spends ~15s booting its sandbox while the chat showed a
wordless three-dot bubble, so the run read as stalled (#6047). The empty turn now
advances through a startup story — "Getting things ready" to "Almost ready" — and
clears the moment content, an error, or a Stop arrives. A warm session keeps the
existing dots, gated on the liveness poll already being fetched.

The phases are TIMED GUESSES, not observed state: sandbox boot and ordinary model
latency share the one window between the stream's `start` chunk and its first
content chunk, so nothing client-side can tell them apart and no backend signal is
involved. Copy deliberately avoids naming our infrastructure ("sandbox",
"harness") since that means nothing to the reader; a test enforces it.

The label is derived per render off a per-turn clock atom, with timeouts scheduled
to each phase boundary rather than a poll, so it cannot go stale and costs no
timers once the last phase lands.
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Aug 17, 2026
@vercel

vercel Bot commented Aug 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview Aug 19, 2026 10:06am

Request Review

@dosubot dosubot Bot added enhancement New feature or request frontend tests ux labels Aug 17, 2026
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b2fa562-2646-4e59-9001-1571e65d0108

📥 Commits

Reviewing files that changed from the base of the PR and between d40cc1e and 6f2e4e1.

📒 Files selected for processing (2)
  • web/oss/src/components/AgentChatSlice/components/TurnActivity.tsx
  • web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added informative startup-phase messages while agents prepare cold-start responses.
    • Startup labels update automatically as processing progresses with subtle animated styling.
    • Preserved the existing loading indicator for warm starts and other pending states.
  • Accessibility
    • Added polite status announcements and motion-safe visual effects for startup activity.
    • Clarified waiting-for-input messaging.
  • Reliability
    • Ensured startup messaging remains accurate as phases change or sessions finish.

Walkthrough

The change adds timed startup labels for cold agent turns. Session clocks follow turn lifecycle states. Pending turns render accessible startup activity with shimmer styling. Warm turns retain the existing loading behavior.

Changes

Cold Startup Activity

Layer / File(s) Summary
Startup timing and turn clock
web/oss/src/components/AgentChatSlice/assets/startupPhases.ts, web/oss/src/components/AgentChatSlice/state/turnClock.ts, related tests
Defines the four-phase startup ladder and elapsed-time helpers. Adds per-session Jotai start clocks with restart and cleanup behavior.
Session clock lifecycle
web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts, web/oss/src/components/AgentChatSlice/hooks/useStartupPhase.ts
Starts clocks for cold turns, clears them for warm or terminal states, and schedules phase updates using wall-clock timing.
Pending-turn startup rendering
web/oss/src/components/AgentChatSlice/components/AgentMessage.tsx, web/oss/src/components/AgentChatSlice/components/TurnActivity.tsx, web/oss/tailwind.config.ts, related tests
Renders startup labels for pending turns. Adds accessible status announcements, shimmer styling, animated dots, updated waiting text, and validation coverage.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: 🔵 Low · up to 6f2e4

The PR improves startup feedback, but warm sessions may briefly show misleading startup labels and users who prefer reduced motion may still see animated dots. The change is mergeable with explicit owner awareness or follow-up on these bounded issues.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant useAgentChatSession
  participant startTurnClockAtom
  participant useStartupPhase
  participant AgentMessage
  participant StartupActivity
  User->>useAgentChatSession: submit agent turn
  useAgentChatSession->>startTurnClockAtom: record cold-turn start time
  AgentMessage->>useStartupPhase: read session startup phase
  useStartupPhase->>useStartupPhase: schedule next phase boundary
  useStartupPhase-->>AgentMessage: return current phase label
  AgentMessage->>StartupActivity: render startup label
  StartupActivity-->>User: announce and display phase
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR adds startup labels and covers warm, failure, cancellation, and streaming paths, but it does not show confirmed “Sandbox started” readiness as required by #6047. Implement readiness-driven “Sandbox starting” and “Sandbox started” states, or update #6047 to explicitly accept timed interim labels.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: showing startup states before agent streaming.
Description check ✅ Passed The description explains the startup labels, warm-session behavior, cleanup paths, tests, and known approximation.
Out of Scope Changes check ✅ Passed All changed files support startup feedback, timing, rendering, accessibility, state, styling, or tests for the linked issue; no unrelated code is evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agent-startup-states

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 0fb16222-9fdd-43c7-b1d4-c216ea9b4896

📥 Commits

Reviewing files that changed from the base of the PR and between 0af145e and 731bd2b.

📒 Files selected for processing (10)
  • web/oss/src/components/AgentChatSlice/assets/startupPhases.test.ts
  • web/oss/src/components/AgentChatSlice/assets/startupPhases.ts
  • web/oss/src/components/AgentChatSlice/components/AgentMessage.tsx
  • web/oss/src/components/AgentChatSlice/components/TurnActivity.test.tsx
  • web/oss/src/components/AgentChatSlice/components/TurnActivity.tsx
  • web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts
  • web/oss/src/components/AgentChatSlice/hooks/useStartupPhase.ts
  • web/oss/src/components/AgentChatSlice/state/turnClock.test.ts
  • web/oss/src/components/AgentChatSlice/state/turnClock.ts
  • web/oss/tailwind.config.ts

Included review availability: Your plan includes up to 8 reviews per rolling hour; 7 remain after this review.

Comment thread web/oss/src/components/AgentChatSlice/assets/startupPhases.ts Outdated
Comment thread web/oss/src/components/AgentChatSlice/components/TurnActivity.tsx Outdated
Comment thread web/oss/src/components/AgentChatSlice/hooks/useAgentChatSession.ts Outdated
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-pr-6073.up.railway.app/w
Project agenta-oss-clone-spike
Image tag pr-6073-6fb9a34
Status Deployed
Railway logs Open logs
Workflow logs View workflow run
Updated at 2026-08-19T10:17:13.646Z

A review pass measured this code at 1.82 and 1.09 comment-to-code against a
folder norm of 0.28-0.42, and web/CLAUDE.md sets a hard rule against multi-line
prose comments. Kept the two genuinely surprising constraints (the labels are
timed guesses with no backend signal, and why "Working" at 0ms is safe) and cut
the rest to one line each.

Also dropped three tests that could not fail independently: two loops brute-
forcing monotonicity over a five-line scan, and a check that a Record keyed by
session id is keyed by session id. Removed two assertions matching literal
Tailwind class names, which would have failed on a cosmetic rename while still
passing if the tailwind config entry were deleted.
Incidental eslint --fix output picked up while working elsewhere in this file.
No behaviour change.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
web/oss/src/components/AgentChatSlice/components/TurnActivity.test.tsx (1)

2-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Shorten the test rationale comment.

This five-line block exceeds the repository rule for in-code comments. Keep only the non-obvious renderToStaticMarkup constraint in one short line; the test intent is clear from the assertions.

As per coding guidelines: “Keep in-code comments to at most one short line; use longer comments only for genuinely surprising constraints such as bugs, races, or ordering requirements.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 57c3f9b6-b6c0-4eee-b7aa-de864fff0d4d

📥 Commits

Reviewing files that changed from the base of the PR and between 731bd2b and 373e6df.

📒 Files selected for processing (7)
  • web/oss/src/components/AgentChatSlice/assets/startupPhases.test.ts
  • web/oss/src/components/AgentChatSlice/assets/startupPhases.ts
  • web/oss/src/components/AgentChatSlice/components/TurnActivity.test.tsx
  • web/oss/src/components/AgentChatSlice/components/TurnActivity.tsx
  • web/oss/src/components/AgentChatSlice/hooks/useStartupPhase.ts
  • web/oss/src/components/AgentChatSlice/state/turnClock.test.ts
  • web/oss/src/components/AgentChatSlice/state/turnClock.ts
💤 Files with no reviewable changes (2)
  • web/oss/src/components/AgentChatSlice/assets/startupPhases.test.ts
  • web/oss/src/components/AgentChatSlice/state/turnClock.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • web/oss/src/components/AgentChatSlice/components/TurnActivity.tsx
  • web/oss/src/components/AgentChatSlice/hooks/useStartupPhase.ts
  • web/oss/src/components/AgentChatSlice/state/turnClock.ts
  • web/oss/src/components/AgentChatSlice/assets/startupPhases.ts

Included review availability: Your plan includes up to 8 reviews per rolling hour; 6 remain after this review.

@ashrafchowdury
ashrafchowdury changed the base branch from main to release/v0.112.2 August 17, 2026 09:32
…g comments

CodeRabbit review on #6073.

The three pulsing dots animated unconditionally while the label beside them was
already motion-safe, so a reduced-motion reader still got movement. SessionRunSpinner
sets the convention here; the dots now follow it.

Also cut the narrative comments the earlier trim missed: the PendingTurn docblock,
the test-suite rationale, and the two tailwind ones. Kept the client-side
timed-guess limitation and the one-line note on why "Working" sits at 0ms, which
a test pins.
@ashrafchowdury
ashrafchowdury changed the base branch from release/v0.112.2 to main August 17, 2026 09:35
@ashrafchowdury
ashrafchowdury changed the base branch from main to release/v0.112.2 August 17, 2026 11:39
@ashrafchowdury ashrafchowdury changed the title [6047] feat(frontend): Show startup states before an agent streams feat(frontend): Show startup states before an agent streams Aug 17, 2026

Copy link
Copy Markdown
Member

Runner startup-event investigation

The runner can report useful startup state natively. I recommend prioritizing a focused follow-up, but keeping the first version coarse.

What the architecture already knows

The shared sandbox-agent path has authoritative boundaries for:

  • environment acquisition begins;
  • a parked sandbox reconnect succeeds, or a new sandbox is created;
  • remote assets and durable mounts finish;
  • the harness session is ready and the turn begins.

These boundaries live in services/runner/src/environment/sandbox-lifecycle.ts and services/runner/src/engines/sandbox_agent/environment.ts. They apply across the supported harnesses because they sit above harness-specific execution.

The stream protocol also already supports transient custom data events. The Python Vercel adapter converts a runner data event into a data-<name> stream part and preserves transient: true. The frontend can consume a data-agent-status part without presenting it as assistant content.

Gap and effort

The current emit callback reaches runTurn only after acquireEnvironment completes. Startup therefore cannot emit live events yet. Threading the same callback through the cold acquisition path is a direct extension of the existing stream, not a separate polling channel.

One safeguard is required: buildPersistingEmitter currently persists all otherwise-unhandled events, including transient data. Startup status must be excluded from durable session records, or the transcript will accumulate presentation-only events.

A useful partial implementation looks low to medium effort:

  1. Define a small stable status payload, for example {phase: "environment_starting" | "environment_ready" | "agent_starting"}.
  2. Pass the existing emitter into environment acquisition and emit only at confirmed boundaries.
  3. Project it through the existing transient data-event path.
  4. Skip transient status events in session persistence.
  5. Let the frontend show the latest status until the first real content, error, cancellation, or completion.

A richer breakdown such as mounts, instructions, tools, and provider readiness is medium effort. Some phases overlap or differ by local versus Daytona execution, so we should add only boundaries that the runner can prove.

Recommendation

Prioritize the coarse follow-up after this PR. The architecture supports it cleanly, and partial information is already valuable. Do not reproduce the current four timed labels as server events. Start with two or three observed states, measure their durations, then add finer phases only where the runner owns a reliable boundary.

Copy link
Copy Markdown
Member

Product review: when the startup messages appear

The detailed labels do not appear on every message by design.

At submission, useAgentChatSession snapshots sessionLivenessAtomFamily(sessionId).nest.isAlive:

  • isAlive: true: the turn keeps the existing three-dot loader.
  • isAlive: false: the turn shows the timed ladder until real content, an error, Stop, or another terminal state.

After a successful first turn, onFinish invalidates the liveness query. The backend keeps a resumable session marked alive, so later turns should normally show only the dots. This is better than showing the four labels on every message.

There are two accuracy limits:

  1. This signal describes whether the runner process is alive. It does not describe whether the sandbox is warm, cold, gone, rebuilding after a configuration change, or already preparing instructions and tools.
  2. The query defaults a missing or unresolved stream to isAlive: false. A send before liveness resolves can show the ladder for a session that is actually warm. The liveness query is also a cached poll, so the decision can be stale.

A revision or configuration change does not reliably trigger these labels. If the runner can reconcile the existing live environment, the client sees isAlive: true and shows dots. If the process is no longer alive, it shows the ladder, but that still does not prove why the restart happened.

Product recommendation

I would not reject this PR for showing the labels on every message, because it does not intentionally do that. I would treat it as an interim improvement with inaccurate phase copy. Before merge, either:

  • reduce the timed copy to claims that remain true without backend evidence, such as “Working” and “This is taking a little longer”; or
  • explicitly accept the current copy as a short-lived approximation and create the runner-event follow-up.

The eventual product behavior should be event-driven: show startup messages only when the runner confirms a startup or restore boundary, and use the plain activity indicator for ordinary model latency.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Aug 17, 2026

@bekossy bekossy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the work here. The per-turn clock and cleanup behavior look good, but I think we should address one functional issue before merging: unresolved liveness is still treated as isAlive: false, so a warm session can show the cold-start ladder if the user sends before the liveness query resolves.

Could we keep the existing dots while liveness is loading, and only show the startup ladder once we know the session is not alive?

Separately, the timed labels are fine as an interim UX if we explicitly accept them as approximations, but they are not confirmed backend startup states yet

@dosubot dosubot Bot removed the lgtm This PR has been approved by a maintainer label Aug 18, 2026
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Aug 19, 2026
feat(agent): show observed startup states
@bekossy
bekossy merged commit 316cbfb into release/v0.112.2 Aug 19, 2026
34 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request frontend lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files. tests ux

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(feat) Show sandbox startup states before an agent streams

3 participants