Skip to content

improvement(chat): restore stable inline tool activity headers and action summaries - #8225

Open
waleedlatif1 wants to merge 1 commit into
stagingfrom
improvement/inline-tool-activity
Open

waleedlatif1 wants to merge 1 commit into
stagingfrom
improvement/inline-tool-activity

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Restores the inline tool activity feel from before v1.0.0 on top of the new activity grouping, built on one rule: one live indicator per active lane, and the Thinking row only when nothing is live and nothing waits on the user.

  • Open tool groups keep a present-tense shimmering header through the gaps between calls (the "Searching → Searched → Searching" flip from v1.0.0 is gone); a group whose latest call failed or stopped reads as finished and hands off to Thinking
  • One owner (lane-activity) decides each lane's single live call (newest running call across the lane's tool groups, search runs, and nested lanes); only that run shimmers, so parallel calls, interleaved searches, and approval cards never show two indicators
  • Running search calls shimmer their own query label; finished searches show static results and hand off to Thinking
  • A lane waiting on an approval, terminal handoff, or browser takeover shows no indicator; other lanes doing real work keep theirs
  • Header icon is back (brand icons included, stable through completion); no more "+N": finished groups without a model-written title summarize the 2–3 most representative actions from a catalog covering the current tool surface incl. CLI commands; finished activity groups keep their own headers
  • Flat expanded layout: rows line up with their header's icon and text columns, indentation only marks nested lanes, one spacing scale (6px within a group, 12px between blocks)
  • Inline links in answers: favicon centered on the text line, tooltip names the page (retrieved source title → preview title → site name, never the raw URL), subtle underline on hover instead of a filled background
  • Pairs with simstudioai/mothership#516, which restores per-call intent descriptions (activityDescription) from the worker; Sim already renders them

Type of Change

  • Improvement

Testing

  • An exhaustive rendered state matrix (8,753 states through the real MessageContent, collapsed and expanded: streaming/reload × position × tool group/search/subagent/nested × every call status × parallel/sequential × model title/description) shows exactly the expected live indicator, tense, header text, and icon in every state
  • Focused tests for every rule and review finding, each verified to fail without its change
  • Type-check, lint, check:audits (49), block registry, and docs manifest pass; 3,264 tests across home, mothership tools, and the landing hero pass

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Sep 24, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated
docs Skipped Skipped Sep 24, 2026 5:54am UTC

Request Review

@greptile-apps

greptile-apps Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the previously reported parallel-search indicator issue is resolved and no actionable new regression was established.

Summary

This PR restores stable inline tool activity presentation while centralizing live-indicator ownership at the lane level.

  • Selects at most one live call per lane across tool groups, searches, and nested agents.
  • Keeps open groups in the present tense between calls and hands finished searches or failed calls back to the Thinking row.
  • Adds representative completed-action summaries, stable icons, and a flatter activity layout.
  • Improves answer links with retrieved-source titles, favicon alignment, and understated hover styling.
  • Adds focused activity, layout, title, citation, and external-link coverage.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Turn[Streaming assistant turn] --> Owner[getTurnLiveIndicators]
  Owner --> Main[Main-agent lane]
  Owner --> Sub[Subagent lanes]
  Main --> PickMain[Newest running call or trailing successful tool]
  Sub --> PickSub[Newest running call, trailing call, or narration]
  PickMain --> Group[Tool-group header]
  PickMain --> Search[Single search-query row]
  PickSub --> Agent[Subagent header or nested lane]
  Owner --> Wait{Any lane indicator or user interaction?}
  Wait -->|Yes| Hide[Hide turn-level Thinking row]
  Wait -->|No| Show[Show turn-level Thinking row]
Loading

Reviews (2) · Last reviewed commit: "improvement(chat): restore stable inline..."

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 20 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

…tion summaries

Mothership v1.0.0 reintroduced the "Searching -> Searched -> Searching"
flicker that staging had fixed: between two calls of an open main-agent
activity, the header dropped to the past tense, lost its shimmer, and the
turn-level Thinking row appeared until the next call started. It also removed
the header icon and summarized headers as "<latest title> + N".

Stable in-progress header
- An open main-agent activity group (the streaming turn's tail, with nothing
  after its last call) stays in the present tense through the gaps between
  its calls, so the header no longer flips tense. Succeeded calls arrive
  with past-tense titles, so the open header maps them back through the exact
  inverse of the completed-verb rewrite; one helper (getToolInProgressTitle)
  serves main lanes, subagent lanes, and the landing hero.

One live indicator per lane
- A new pure module (lane-activity) owns the rule and states it in TSDoc.
  Each active lane has one live call: the newest running call anywhere in
  the lane (across all its runs, including runs split by a search or an
  interaction card, and its nested lanes). With none running and the lane
  open, the latest call of its trailing run owns the gap, and only if it
  succeeded. A finished search shows static results, so the gap after it
  belongs to Thinking.
- Only the run holding that call shimmers: its group header, or its one
  search row. Tool rows never shimmer by themselves. A parent lane defers
  to a visible nested lane holding the live call only while that lane is
  still working; an ended nested lane hands its last call back to the
  parent's header.
- Tense follows liveness: a header reads in progress only while it is live
  or its call still runs, so a group whose latest call failed or stopped
  reads as finished instead of keeping a stale present tense.
- The turn computes this once; every lane view receives its share, and the
  Thinking row reads the same result. The main lane spans all main-agent
  segments. Thinking shows only when no lane is live and no lane waits on
  the user. A lane waiting on an approval, terminal handoff, or browser
  takeover shows no indicator, while other lanes doing real work keep theirs.
  When the stream finishes, nothing shimmers.
- Rendering and liveness share one run split, and failed subagent lanes no
  longer count as working. Only the tail group is open, so a later pending
  interaction no longer re-opens earlier finished groups.

Header icons
- Group headers and single inline rows render the icon of the call they
  describe again, including integration brand icons, and keep it through
  completion. Main and subagent headers share one title rule: "Working…"
  while a sim_cli or run_code call generates its arguments, a live wait
  countdown, and streaming file-edit titles.

Summaries without "+N"
- Running headers show only the active call. Main and subagent lanes share
  one completed-label rule: the model's completedTitle only when every call
  succeeded, and otherwise a fallback naming up to three distinct successful
  actions ("Navigated, read pages, clicked elements"), picked by first
  occurrence in transcript order. Failed calls are
  left out, and the " · N stopped/skipped" outcome suffix stays.
- A rebuilt action catalog covers the current tool surface: every visible
  catalog tool and operation, plus sim_cli commands. Each command's verb comes
  from its own display title, and the object comes from its resource path, so
  commands on one resource share a word ("Listed, read tables"). `mkdir`
  commands read as creating folders and `tables upsert` as writing table rows.
  Tools outside the catalog use their own completed title.

Separate finished activity groups
- groupByActivity still splits main-agent calls by activity id, but no longer
  merges finished groups into one header, so the completedGroupCount plumbing
  is removed, along with the unused getAgentIcon fallback parameter.

Flat expanded layout
- Indentation now only marks nested work. Expanded tool rows in main and
  subagent groups line up with their header: the icon in the shared 14px
  icon column, the text one gap-2 to its right. Nested subagent lanes,
  narration, lane errors, and the permission card's rows sit on that same
  text column through one shared primitive instead of pl-6 or pl-[22px].
- One spacing scale: gap-1.5 between rows inside a group (tool rows, search
  queries), gap-3 between blocks in the main lane (tool groups, search runs,
  lanes, interaction cards, prose).

Inline links in answers
- A link's favicon is centered on the text's own middle with align-middle,
  like inline citation chips, instead of a baseline pixel nudge.
- The link tooltip names the page instead of showing its URL. It uses the
  turn's retrieved source for that exact URL, then the link preview's title,
  then the site name, with the site name muted beneath. One helper dedupes a
  turn's sources by URL everywhere they are listed or looked up.
- Prose links show a thin muted underline on hover instead of a filled
  background, and keep the keyboard focus outline.

Verb rewrites
- The past-tense and present-tense verb tables are Maps, because titles
  include model-written text, and a description starting with "constructor"
  must never resolve to an inherited object property.
@waleedlatif1
waleedlatif1 force-pushed the improvement/inline-tool-activity branch from c6eb7e8 to a6114e8 Compare September 24, 2026 05:54
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 36 files

Confidence score: 3/5

  • lane-activity.ts can suppress the turn wait when a browser takeover is executing but the browser agent is unavailable, leaving no takeover card and potentially leaving the user without visible progress; pass browser-agent availability into the lane decision.
  • tool-call-title.ts truncates streamed prepare_file_edit labels when title values contain escaped quotes, reducing live status clarity; parse escaped JSON characters while extracting the title.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/lane-activity.ts">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/lane-activity.ts:100">
P2: This unconditionally suppresses the turn wait for an executing browser takeover, even when the browser agent is unavailable and no takeover card can render. Pass the browser-agent availability into the lane decision and only treat the takeover as a user wait when the corresponding card will be shown.</violation>
</file>

<file name="apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-title.ts">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-title.ts:60">
P2: Escaped quotes in a streamed `prepare_file_edit` title are parsed as the end of the value, truncating the live label. Match escaped JSON characters while extracting the title so labels remain correct during streaming.</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Fix all with cubic | Re-trigger cubic

includeBrowserTakeover = false
): boolean {
return (
(includeBrowserTakeover && getActiveBrowserTakeover(items) !== null) ||

@cubic-dev-ai cubic-dev-ai Bot Sep 24, 2026

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.

P2: This unconditionally suppresses the turn wait for an executing browser takeover, even when the browser agent is unavailable and no takeover card can render. Pass the browser-agent availability into the lane decision and only treat the takeover as a user wait when the corresponding card will be shown.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/lane-activity.ts, line 100:

<comment>This unconditionally suppresses the turn wait for an executing browser takeover, even when the browser agent is unavailable and no takeover card can render. Pass the browser-agent availability into the lane decision and only treat the takeover as a user wait when the corresponding card will be shown.</comment>

<file context>
@@ -0,0 +1,252 @@
+  includeBrowserTakeover = false
+): boolean {
+  return (
+    (includeBrowserTakeover && getActiveBrowserTakeover(items) !== null) ||
+    items.some((item) =>
+      item.type === 'tool'
</file context>
Fix with cubic

*/
function getLiveFileEditTitle(toolName?: string, streamingArgs?: string): string | null {
if (toolName !== PrepareFileEdit.id || !streamingArgs) return null
const titleMatch = streamingArgs.match(/"title"\s*:\s*"([^"]+)"/)

@cubic-dev-ai cubic-dev-ai Bot Sep 24, 2026

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.

P2: Escaped quotes in a streamed prepare_file_edit title are parsed as the end of the value, truncating the live label. Match escaped JSON characters while extracting the title so labels remain correct during streaming.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-title.ts, line 60:

<comment>Escaped quotes in a streamed `prepare_file_edit` title are parsed as the end of the value, truncating the live label. Match escaped JSON characters while extracting the title so labels remain correct during streaming.</comment>

<file context>
@@ -0,0 +1,111 @@
+ */
+function getLiveFileEditTitle(toolName?: string, streamingArgs?: string): string | null {
+  if (toolName !== PrepareFileEdit.id || !streamingArgs) return null
+  const titleMatch = streamingArgs.match(/"title"\s*:\s*"([^"]+)"/)
+  if (!titleMatch?.[1]) return null
+  const operation = streamingArgs.match(/"operation"\s*:\s*"(\w+)"/)?.[1] ?? ''
</file context>
Suggested change
const titleMatch = streamingArgs.match(/"title"\s*:\s*"([^"]+)"/)
const titleMatch = streamingArgs.match(/"title"\s*:\s*"((?:\\.|[^"\\])*)"/)
Fix with cubic

This branch was previously deployed

1 inactive deployment
Preview a6114e84 Deployed Sep 24, 2026 by vercel[bot]
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.

1 participant