Skip to content

feat(frontend): Show readable summaries for tool activity in the playground - #6007

Open
ashrafchowdury wants to merge 12 commits into
release/v0.112.1from
fix/age-4106-readable-tool-activity-summaries
Open

feat(frontend): Show readable summaries for tool activity in the playground#6007
ashrafchowdury wants to merge 12 commits into
release/v0.112.1from
fix/age-4106-readable-tool-activity-summaries

Conversation

@ashrafchowdury

@ashrafchowdury ashrafchowdury commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Context

The playground labelled every tool call with its raw wire name, so the activity feed read like a terminal log. Under Codex that name is not a name at all. The runner stores the ACP display title as the identifier, so a shell call is recorded under the command itself and a file read under an English sentence containing an absolute sandbox path.

Our own tools were no better. The frontend unwrapped only Claude's mcp__server__tool form, so Codex's dotted mcp.agenta-tools.test_run fell through to a title-caser that strips underscores but not dots, and printed Mcp.agenta tools.test run.

Closes #5976.

Changes

A row now shows a plain sentence. The technical string sits beside it, and the exact wire name, arguments, and output stay in the expander.

Wire name Before Now
ours mcp.agenta-tools.test_run Mcp.agenta tools.test run Tested the agent
ours create_subscription Create subscription Created a trigger
ours query_spans Query spans Looked through runs
ours read_config Read config Read the agent's setup
ours rename_agent Rename agent Renamed the agent
ours __ag__request_input Request input + chip Ag Asked you some questions
ours request_connection Request connection Asked you to connect GitHub
builtin Bash Bash Ran a command rg -n 'todo' src
builtin Read Read Read a file SKILL.md
builtin Grep Grep Searched files handleSubmit\(
codex sed -n '1,80p' /tmp/agenta-…/agent.py the whole command, lower-cased Ran a command sed -n '1,80p' agent.py
codex Read file '/tmp/agenta-…/SKILL.md' the whole path, lower-cased Read a file SKILL.md
app tools__composio__github__SEARCH_ISSUES__a4f Search issues + chip Github Searched GitHub issues
app tools__composio__slack__SEND_MESSAGE__y Send message + chip Slack Sent a Slack message
app tools__composio__github__CREATE_AN_ISSUE__c1 Create an issue + chip Github Created a GitHub issue
app tools__composio__googlecalendar__EVENTS_LIST__c1 Events list + chip Googlecalendar Checked Google Calendar events
app mcp.linear.create_issue Mcp.linear.create issue Created a Linear issue
app discover_tools (found YOUTUBE_LIST_CHANNEL_VIDEOS) Discover tools Checked YouTube channel videos

Every row above comes from running the resolver, not from writing out what it should say.

The wording is derived, not tabulated

A verb table turns a verb noun name into both tenses, and a small glossary maps our internal nouns to the words the product uses. A subscription is a trigger, a span is a run, config is the agent's setup. A platform op we ship next month reads correctly with no code change, and so does a Composio action nobody has seen. Only four names carry hand-written wording, because derivation gets them wrong: test_run ("Tested a run" misses that the run is the agent), request_input and request_connection (both address the reader rather than describe the agent), and commit_revision (wording derives; the entry exists only for its commit-message summary).

Two guards keep it honest. An action whose verb is not in the table keeps its plain label, so an unfamiliar verb reads as it does today instead of as "Getted". And the glossary applies only to our own tools, because a gateway action like stripe__CANCEL_SUBSCRIPTION means a real subscription and must never be renamed to "trigger".

The verb can also sit at the end of an action name. EVENTS_LIST used to produce "Events list" in both tenses, so the row never visibly progressed. The tail word is checked when the head is not a verb.

App names come from the catalog, never from a list in the source

There are thousands of Composio integrations, so hardcoding names does not scale. The wire name only supports title case, which gets Github and Googlecalendar wrong. The real spelling comes from the tool catalog, the same query the agent config panel already uses.

The resolver is pure and synchronous while the catalog answers late, so a row resolves twice: once to learn the integration slug, then again with the real name once useToolIntegrationDetail answers. Only rows that have a slug subscribe. Rows live in a virtualized transcript and that query carries an IndexedDB persister, so a subscription on every row would charge every session for a lookup almost none of them need.

The app is named inside the sentence rather than sitting in a chip beside it, so the row reads as one thought. Where naming it would stutter ("Checked Google Calendar calendar settings"), the app moves back to the chip.

discover_tools gets its app from its own result. The arguments are keyword salad the model wrote for a search ("get youtube channel activities uploads recent"), but the result reports the integration and the ACTION token of the tool it matched. That token has the same shape a gateway wire name carries, so the discovery row and the row that later runs that tool derive their wording from one path. Only read-only verbs are adopted. The call found the tool, it did not run it, so SEND_EMAIL must never read "Sent an email".

Tense follows what actually happened

A denied or deferred call never ran, so it keeps the present tense instead of claiming "Tested the agent" beside the word "denied". A genuine failure reads as one thought, "Testing the agent failed", rather than claiming the action completed and contradicting it four words later.

The summary slot stopped leaking payloads

Gateway tools return their payload as a JSON string, which took the plain-text path and got sliced at 80 characters. A serialised payload is now read as structure. A file's contents and a command's stdout report their size instead ("194 lines"), because their first 80 characters are never the point and they leak whatever the file happens to start with. The generic "2 fields" fallback is gone, since a field count tells the reader nothing the status icon has not already said.

Everywhere a tool is named now says the same thing

The approval dock, the always-allow switch, and the turn inspector kept the old title-cased label, so the card gating a call and the row reporting it used different words for the same tool. All three now use the sentence, and all three pass the call's arguments, so they strip the sandbox root too. The dock's frame changed with them, because a gerund cannot slot into "wants to use ___":

Before: The agent wants to use Test run before it can keep going.
Now: The agent needs your approval before testing the agent.

A paused turn also said the same sentence three times at once: the turn status line, the composer dock, and the elicitation card. The card's copy goes, since it is the one with the questions and buttons already in front of you. The status line drops the jargon and reads "Waiting for you", which is true for all three parked states (approval, connect, elicitation). The dock keeps its line because it is the only one that mentions queued messages.

Two bugs found along the way

__ag__request_input is the wire name a real run streams, and the reserved slug namespace was never unwrapped. The generic parser read ag as the app and derived "Requested an Ag input" for every elicitation in the turn inspector.

An action name carrying its own article landed it mid-phrase. CREATE_AN_ISSUE produced "Created GitHub an issue". The article now comes off before the app goes in front of the noun.

Tests

  • pnpm vitest run src/components/AgentChatSlice src/hooks in web/oss: 485 passing across 37 files. 71 cover the resolver and 27 cover the row text, using tool names taken from recorded Codex sessions.
  • pnpm vitest run in web/packages/agenta-chat: 28 passing.
  • eslint, prettier, and tsc --noEmit clean on every changed file.

The row's text derivation moved out of ToolActivity.tsx into assets/toolRow.ts so it could be tested at all. The component had no test file, so the denied and deferred tense fix and the failure sentence shipped with no coverage. That move also surfaced an output cut that counted UTF-16 units where every other cap in the area counts code points, which could split a surrogate pair and render a replacement character.

Worth a reviewer's eye:

  • ElicitationWidget deliberately keeps the plain label rather than the sentence. It names the tool as an actor ("Asked by X"), where a gerund reads as broken English. For our own elicitation tools it now shows no attribution at all, because the card in front of you is the agent asking.
  • Mobile's chat row still prints the raw wire name. It does not call this resolver, and switching it over belongs with the skin re-plumb rather than here.

What to QA

  • Open an agent playground session on a Codex agent and run something that uses the platform tools. Rows read as sentences ("Tested the agent", "Saved changes"), not as Mcp.agenta tools.....
  • Have the agent run a shell command and read a file. You see "Ran a command" with the command beside it, and "Read a file" with just the filename, not the full /tmp/agenta/mounts/... path. The file row ends in a line count, not the first line of the file.
  • Ask the agent to build something that needs a connected app, so it calls discover_tools. Rows name the app it found with the real spelling ("Checked YouTube channel videos"), not the keywords it searched with.
  • Trigger an elicitation. The card shows the questions and the required count, with no "Asked by Request input" and no second "waiting on your input" line. The turn status line reads "Waiting for you".
  • Deny a tool approval. The row reads "Running a command" with "denied" beside it, and does not claim the call completed. Approve a different one and check the card first reads "The agent needs your approval before ...".
  • Switch to Build mode and expand a row. The first block is tool with the exact wire name, followed by input and output as before.
  • Open the turn inspector on a turn that used tools. Timeline rows read as sentences, with the call in the present tense and its result in the past.
  • Regression: run the same agent on Claude and on Pi. One tool reads identically on all three harnesses, and the collapsed group line still shows "Used N tools".
  • Regression: check both light and dark themes.

The playground labelled each tool call with its raw wire name. Under Codex that
name is not a name — the runner records shell calls under the command itself and
file reads under an English sentence — and our own tools leaked through as
"Mcp.agenta tools.test run", since only Claude's `mcp__` wrapper was unwrapped.

Rows now read as plain sentences ("Tested the agent", "Ran a command") with the
command or filename beside them; the exact wire name, arguments and output stay
in the expander. The summary slot no longer spills raw JSON either.

Wording is derived rather than tabulated: a verb table plus a small product
glossary turn `verb_noun` names into both tenses, so a newly shipped platform op
or Composio action reads correctly with no code change. App names come from the
tool catalog instead of a hardcoded brand list.
@linear-code

linear-code Bot commented Aug 13, 2026

Copy link
Copy Markdown

AGE-4106

@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
agenta-documentation Error Error Aug 13, 2026 8:20pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Improved tool activity displays with clearer running, completed, failed, deferred, and approval-related messages.
    • Added richer details for shell commands, files, integrations, and external tools.
    • Added concise output summaries with safe JSON parsing and Unicode-aware truncation.
    • Improved source labels, tool names, query wording, and action descriptions.
  • Bug Fixes

    • Improved handling of unknown, denied, deferred, and unsuccessful tool results.
    • Simplified elicitation prompts and removed redundant requester text.
    • Updated waiting-state text to “waiting for you.”

Walkthrough

The PR adds canonical tool display resolution, shared tool-row state and output helpers, catalog-aware activity rendering, input-aware approval text, and updated elicitation messaging. Tests cover platform, MCP, shell, file, search, connection, builtin, output, and failure cases.

Changes

Tool activity display

Layer / File(s) Summary
Tool display resolution and coverage
web/oss/src/components/AgentChatSlice/assets/toolDisplay.ts, web/oss/src/components/AgentChatSlice/assets/toolDisplay.test.ts
Tool names are canonicalized and classified. Running and completed activity text now includes input, output, app, source, and technical details.
Shared row state and output summaries
web/oss/src/components/AgentChatSlice/assets/toolRow.ts, web/oss/src/components/AgentChatSlice/assets/toolRow.test.ts, web/packages/agenta-chat/src/model/toolSummary.ts, web/packages/agenta-chat/tests/unit/model/toolSummary.test.ts
Shared helpers classify tool outcomes and summarize structured or serialized output with 80-code-point Unicode-safe truncation.
Activity and approval rendering
web/oss/src/components/AgentChatSlice/components/ToolActivity.tsx, web/oss/src/components/AgentChatSlice/components/ApprovalDock.tsx, web/oss/src/components/AgentChatSlice/components/Inspector/EventRow.tsx, web/oss/src/hooks/useAlwaysAllowTool.tsx
Rows, approvals, events, and grant labels use shared activity text, catalog metadata, source labels, details, and state handling.
Elicitation and waiting-state text
web/oss/src/components/AgentChatSlice/components/clientTools/ElicitationWidget.tsx, web/oss/src/components/AgentChatSlice/components/clientTools/ElicitationWidget.settleOnce.test.tsx, web/oss/src/components/AgentChatSlice/components/TurnActivity.tsx, web/oss/tests/playwright/acceptance/agent-chat/index.ts
Built-in client tools omit redundant requester attribution. Required-field counts remain visible. Waiting text changes to “waiting for you.”

Estimated code review effort: 4 (Complex) | ~60 minutes

Merge Risk: 🔵 Low · up to 6941f

The change is mergeable with owner follow-up: the approval UI may include one call's query or arguments in an “always allow” label even though the permission applies to every use of that tool, which could mislead users about its scope.

Sequence Diagram(s)

sequenceDiagram
  participant ToolEvent
  participant resolveToolDisplay
  participant ToolCatalog
  participant ToolActivity
  ToolEvent->>resolveToolDisplay: pass tool input and output
  resolveToolDisplay->>ToolCatalog: resolve source and integration metadata
  ToolCatalog-->>resolveToolDisplay: return catalog metadata
  resolveToolDisplay-->>ToolActivity: return activity, kind, source, and detail
  ToolActivity->>ToolActivity: apply row state and output summary
  ToolActivity-->>ToolEvent: render readable activity text
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy issue #5976 by adding readable summaries while preserving exact tool details in expanded views.
Out of Scope Changes check ✅ Passed The changes remain related to consistent tool activity presentation, summaries, status wording, and supporting output formatting.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 60.00%.
Title check ✅ Passed The title clearly summarizes the main change: readable tool activity summaries in the frontend playground.
Description check ✅ Passed The description directly explains the tool activity presentation changes, supported tool types, behavior, tests, and QA scope.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/age-4106-readable-tool-activity-summaries

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.

The row read "Searched issues" with a separate "GitHub" chip. Issue #5976 asks
for "Searched GitHub issues", which reads as one thought instead of two.

The app now modifies the object, and the article moves in front of it ("Created a
Linear issue"). Naming the app in the sentence retires the chip, so the
provenance is stated once. An action whose verb we do not know still has no
sentence to fold into, and keeps its chip.

The sentence now depends on the catalog's app name, which arrives asynchronously,
so `resolveToolDisplay` takes an optional `appName` and the row resolves twice:
once for the integration slug, then again with the real name.
A Google Calendar action on "calendar settings" folded into "Got Google Calendar
calendar settings". When the object already names the app, the sentence drops it
and the chip carries it instead, so the row reads "Got calendar settings" beside
a "Google Calendar" chip.

Matched on whole words only. A near-miss like Gmail against "email" shares no
word and still reads "Sent a Gmail email", which is redundant but not wrong;
matching substrings would start guessing.
The stutter guard only matched whole words, so it fired on the catalog's "Google
Calendar" but not on the title-cased slug "Googlecalendar" the row paints first.
It now also matches an object word inside the squashed slug, with a length floor
so "file" does not match "googledrive".

Erring toward skipping is safe: the app moves to the chip, so nothing is lost.

Also drops our own MCP server's chip in the package mirror, which OSS already did
via canonicalToolName but the copy never reproduced.
Three simplifications, no behaviour change.

Drops the copy of this logic in the chat package's skin registry. Nothing calls
that resolver: the store starts empty, nobody calls registerChatSkin, and mobile
renders raw names from @agenta/chat/model instead. Mirroring 340 lines into a
dead path was speculative work that would drift before anything consumed it. The
re-plumb PR can copy from OSS when it happens. The package's toolSummary change
stays, since mobile does use rowSummary.

Removes the builder closure. The app name arrives late, so conjugate returned a
function that resolveToolDisplay called later; threading the name through
parseShape instead does the same job with one less layer, and retires the
ActivityBuilder type, the fixed() wrapper, and a ParsedShape field that had gone
dead.

Tightens comment blocks that ran six to ten lines each, against this repo's
one-line rule.
A row showed "Ran a c…" beside a full sandbox path. All three spans in the header
carried min-w-0 truncate, so flex shrank whichever it liked and the one thing that
must stay readable lost. The sentence no longer yields; the detail and the status
beside it absorb the squeeze.

The path was the real noise. Every path in a session sits under the sandbox root,
so it is identical on every row and says nothing. Stripping it turns
"find /tmp/agenta-sandbox-agent-wzkOSy/agents/skills" into "find agents/skills".
Generated id segments go with it; a named directory like workspace/ stays, since
it carries no digit.

Quote stripping now only unwraps quotes the login-shell wrapper added. It was
unanchored and ate the closing quote of a command ending in -name '*.md'.
…ched with

A tool search's arguments are model-written keyword salad ("list google calendar events date range"), so a row built from them read as keywords with a verb glued on. The result is the better source: discover_tools reports the integration and the ACTION token of the tool it matched, and that token is the same shape a gateway wire name carries — so the discovery row and the row that later runs that tool derive their wording from one path. Only read-only verbs are adopted: the call found the tool, it did not run it, so SEND_EMAIL must not read "Sent an email". With no matched tool the query still carries the row, trimmed to a short qualifier once the catalog can spell the app.

Also in the sentence builder: the verb may sit at the END of an action name (EVENTS_LIST), and those used to read with no tense at all in either direction. An article the name carries ("Create an issue") now comes off before the app goes in front of the noun, which had been landing it mid-phrase as "Created GitHub an issue". request_connection names the app it is waiting on, read from its own arguments, and says the run is blocked on the reader rather than that the agent is asking. rename_agent says "the agent": a session has exactly one.
…be tested

partSentence, hasLanded, rowSummary and the output summariser lived inside ToolActivity.tsx, which has no test file — so the denied/deferred tense fix and the failure sentence shipped with no coverage. They move to assets/toolRow.ts with 27 tests.

One bug surfaced on the way: the 80-character output cut counted UTF-16 units while every other cap in this area counts code points, so emoji-heavy output could split a surrogate pair and render a replacement character. It cuts on code points now.

The catalog subscription keys on sourceKey alone rather than on kind, and MCP tools stop reporting one — an MCP server name is not a catalog integration, so that lookup had nothing to find and the row paid for it anyway.
The transcript learned the plain-English sentence; the approval dock, the always-allow switch and the turn inspector kept the old title-cased label, so the card gating a call and the row reporting it used different words for the same tool. All three also resolved without the call's arguments, so none stripped the sandbox root — a Codex shell approval showed 60 characters of raw command as its "name".

The dock's frame had to change with it: a gerund cannot slot into "wants to use ___", so it now reads "The agent needs your approval before running a command". The inspector splits by event, a call taking the present tense and a result the past.

ElicitationWidget deliberately keeps the label. It names the tool as an actor ("Asked by …"), where the sentence reads as broken English; that one needs a copy decision, not a mechanical swap.
A cleanup pass over this branch. No behaviour change except where noted below.

Dead code, each verified rather than assumed: "check" was the one QUERY_VERBS entry with no VERB_FORMS counterpart, so both readers bailed before reaching it; the preposition regex listed five alternatives where exactly two of 37 verb forms end in one; parseShape's bare-identifier tail branched on a source that parseGatewayToolName can only return for names containing "__", which that branch has already excluded; and ToolDisplayOverride's label/source fields were set by no registry entry, which is what forced the source expression into a nested ternary.

Wasted work: discover_tools parsed its own result up to eight times per render — two override hooks each walked to the same capability, and the resolver runs once bare then again with the catalog name, on a payload carrying a JSON Schema per capability. One `app()` hook now returns slug and action from one read, and it is not called at all on the bare resolve, where its result was provably discarded. clamp built a code-point array unconditionally, so a 2 KB command became a 2000-element array to keep 48 characters; short strings now skip it.

Duplication: echoesApp and qualifierFor each spelled out the same "is this word part of the app's name?" rule 90 lines apart, now one appWordTest. ToolActivity repeated the resolve-then-re-resolve-with-catalog dance four times, now displayFor plus a memoized useCatalogDisplay — the conditional-subscription split that keeps virtualized rows off the IndexedDB-backed query is unchanged. PATH_KEYS had drifted to four keys in a different precedence from the entities helper it mirrors; realigned.

Two fixes that are not cleanup. `__ag__request_input` is the wire name a real run streams, and canonicalToolName did not unwrap our reserved slug namespace — the generic parser read "ag" as the app and derived "Requested an Ag input" for every elicitation in the turn inspector. And request_input now reads "Waiting for your answers" / "Asked you some questions": the run is parked on the reader there, which "Asking you for details" left implicit.

The chat package's copy gets the code-point cut it never received (it still split surrogate pairs at 80 UTF-16 units) plus a test. Its five "copied verbatim" banners named ToolActivity.tsx, which this branch moved the code out of, and its byte-parity claim is no longer achievable now that rowSummary deliberately differs — both corrected to say what is actually true.
A paused turn showed the same sentence in three places at once: the turn's status line ("Waiting for your input"), the composer dock ("The agent is waiting for your response — new messages will be queued"), and the elicitation card's subtext ("Waiting on your input").

The card's copy goes, since it is the one with the questions and buttons already in front of you. The status line drops "input" for plain "Waiting for you" — it is shared by all three parked states (approval, connect, elicitation), so it cannot name any one of them, and "Your turn" was not an option next to an "Inspect turn" button. The dock keeps its line: it is the only one that says new messages will be queued.

The card also stopped naming its own asker. "Asked by Request input" restates what the card in front of you already is; a third-party tool rendering through the same widget (anything with render.kind "elicitation") is still named, because there it is real information.

The acceptance spec asserted the removed text. It could never have matched in any case — it wanted a literal "request_input" where the label resolves to "Request input" — so it now asserts the required-field count the subtext actually carries.
@ashrafchowdury
ashrafchowdury changed the base branch from main to release/v0.112.1 August 13, 2026 19:52
web/CLAUDE.md sets a hard rule — at most one short line per comment, longer only for a genuinely surprising constraint — and this branch had drifted well past it: toolDisplay.ts was 27% comment across 15 blocks of four or more lines, several of them narrating in prose what the code already says.

31 blocks rewritten, 73 lines gone, no behaviour change. The four long blocks that remain in toolDisplay.ts predate this branch (the file header, canonicalToolName, and the two call-description helpers) and are left alone rather than grown into the diff. The two of mine still over one line are parseShape and resolveToolDisplay, both at four lines: Codex recording a shell call under its own command — because otel.ts uses the ACP title as the identifier — and the catalog's two-pass resolve are the cases the rule's exception is for, since neither is visible from the code.
@ashrafchowdury
ashrafchowdury changed the base branch from release/v0.112.1 to main August 13, 2026 20:19
@ashrafchowdury
ashrafchowdury changed the base branch from main to release/v0.112.1 August 14, 2026 08:08
@ashrafchowdury

Copy link
Copy Markdown
Contributor Author

Regression testing it / final code review / after that, I will add some preview demo

@ashrafchowdury
ashrafchowdury marked this pull request as ready for review August 14, 2026 08:09
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request labels Aug 14, 2026
@ashrafchowdury ashrafchowdury changed the title fix(frontend): Show readable summaries for tool activity in the playground feat(frontend): Show readable summaries for tool activity in the playground Aug 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
web/oss/src/components/AgentChatSlice/components/clientTools/ElicitationWidget.tsx (1)

299-300: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep changed in-code comments to one short line. These three sites exceed the repository comment-length rule.

  • web/oss/src/components/AgentChatSlice/components/clientTools/ElicitationWidget.tsx#L299-L300: shorten the requester-attribution rationale.
  • web/oss/src/components/AgentChatSlice/components/clientTools/ElicitationWidget.tsx#L357-L359: shorten the JSX comment.
  • web/oss/tests/playwright/acceptance/agent-chat/index.ts#L83-L84: shorten the test rationale comment.

Source: Coding guidelines

web/packages/agenta-chat/src/model/toolSummary.ts (1)

23-59: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Extract the mirrored helpers instead of hand-porting them.

SETTLED, isDeferredError, isUnknownResultError, isNotHandledOutput, parseJsonish, and summarizeOutput are byte-for-byte copies of web/oss/src/components/AgentChatSlice/assets/toolRow.ts. This PR fixes the UTF-16 truncation bug in this copy only because the copy had already drifted, which shows the manual port process fails silently. Move the pure helpers into one shared module and let both toolRow.ts and this file import them. Keep only the intentional divergence (rowSummary error wording) local.

The same two-line "Mirrors … Port changes both ways." note also repeats four times in this range. One file-level note is enough. 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: d299e43d-6a25-411d-817e-3f09c722e7a5

📥 Commits

Reviewing files that changed from the base of the PR and between 53a9d82 and 6941f58.

📒 Files selected for processing (14)
  • web/oss/src/components/AgentChatSlice/assets/toolDisplay.test.ts
  • web/oss/src/components/AgentChatSlice/assets/toolDisplay.ts
  • web/oss/src/components/AgentChatSlice/assets/toolRow.test.ts
  • web/oss/src/components/AgentChatSlice/assets/toolRow.ts
  • web/oss/src/components/AgentChatSlice/components/ApprovalDock.tsx
  • web/oss/src/components/AgentChatSlice/components/Inspector/EventRow.tsx
  • web/oss/src/components/AgentChatSlice/components/ToolActivity.tsx
  • web/oss/src/components/AgentChatSlice/components/TurnActivity.tsx
  • web/oss/src/components/AgentChatSlice/components/clientTools/ElicitationWidget.settleOnce.test.tsx
  • web/oss/src/components/AgentChatSlice/components/clientTools/ElicitationWidget.tsx
  • web/oss/src/hooks/useAlwaysAllowTool.tsx
  • web/oss/tests/playwright/acceptance/agent-chat/index.ts
  • web/packages/agenta-chat/src/model/toolSummary.ts
  • web/packages/agenta-chat/tests/unit/model/toolSummary.test.ts

Comment on lines +567 to +568
{inSentence(friendly?.activity.running ?? "") ||
current.toolName}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not describe a per-tool grant with this call's arguments.

friendly is resolved with current.input, so its sentence can embed call-specific detail. resolveToolDisplay folds a query argument into the sentence for gateway tools. A gate on tools__composio__github__SEARCH_ISSUES__… with input {query: "open bugs"} yields "Searching GitHub open bugs", so the switch reads "Always allow searching GitHub open bugs for this agent". The grant applies to every call of that tool with any arguments, so the label misstates the scope of the permission the user is about to write.

Resolve a second, input-free display for this label.

🐛 Proposed fix: resolve the grant label without the call input

Add beside friendly (near Line 252):

 const friendly = current ? resolveToolDisplay(current.toolName, current.input) : null
+// The grant covers the tool, not this call, so its label must not carry this call's arguments.
+const grantLabel = current ? resolveToolDisplay(current.toolName).activity.running : ""

Then use it here:

-                                                {inSentence(friendly?.activity.running ?? "") ||
-                                                    current.toolName}
+                                                {inSentence(grantLabel) || current.toolName}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{inSentence(friendly?.activity.running ?? "") ||
current.toolName}
const friendly = current ? resolveToolDisplay(current.toolName, current.input) : null
const grantLabel = current ? resolveToolDisplay(current.toolName).activity.running : ""
{inSentence(grantLabel) || current.toolName}

await expect(page.getByText(/Asked by .*request_input/)).toBeVisible()
// Our own elicitation tool goes unnamed — the card IS the agent asking —
// so the subtext is the required-field count alone.
await expect(page.getByText("1 required")).toBeVisible()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
file="web/oss/tests/playwright/acceptance/agent-chat/index.ts"
printf '%s\n' '--- test context ---'
sed -n '70,95p' "$file"
printf '%s\n' '--- related assertions and rendered text ---'
rg -n -C 3 '1 required|Asked by|requester|required' web/oss/tests/playwright web/packages web/oss --glob '*.{ts,tsx}' | head -200

Repository: Agenta-AI/agenta

Length of output: 20169


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- requester text sources ---'
rg -n -C 4 --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  'Asked by|requester|elicitation.*required|required.*count|required-field count' .
printf '%s\n' '--- locator API usage ---'
rg -n -C 2 'getByText\([^)]*exact|exact:\s*true' web/oss/tests/playwright/acceptance --glob '*.{ts,tsx}' | head -120

Repository: Agenta-AI/agenta

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- focused web matches ---'
rg -n -C 5 --glob '*.{ts,tsx,css}' --glob '!**/generated/**' --glob '!**/node_modules/**' \
  'Asked by|asked by|requester|elicitation' web | head -300
printf '%s\n' '--- exact assertion candidates ---'
rg -n -C 2 'getByText\("1 required"|getByText\([^,]+,\s*\{exact:\s*true\}' \
  web/oss/tests/playwright/acceptance/agent-chat web/packages --glob '*.{ts,tsx}' | head -120

Repository: Agenta-AI/agenta

Length of output: 32868


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- focused literal matches ---'
rg -n -i -C 4 --glob '*.{ts,tsx}' \
  'request input|asked by|subtext|required.*field|field.*required' \
  web/packages/agenta-ui web/packages/agenta-entity-ui web/oss \
  --glob '!**/tests/**' | head -300
printf '%s\n' '--- candidate files ---'
rg -l -i --glob '*.{ts,tsx}' \
  'request input|asked by|elicitation|required.*field|field.*required' \
  web/packages/agenta-ui web/packages/agenta-entity-ui web/oss \
  --glob '!**/tests/**' | head -120

Repository: Agenta-AI/agenta

Length of output: 37079


🏁 Script executed:

#!/bin/bash
set -e
for file in \
  web/oss/src/components/AgentChatSlice/components/clientTools/ElicitationWidget.tsx \
  web/oss/src/components/AgentChatSlice/components/clientTools/ClientToolPart.tsx \
  web/oss/src/components/AgentChatSlice/components/clientTools/registry.tsx; do
  printf '\n--- %s ---\n' "$file"
  wc -l "$file"
  ast-grep outline "$file" | head -120
done

Repository: Agenta-AI/agenta

Length of output: 1466


🏁 Script executed:

#!/bin/bash
set -e
file="web/oss/src/components/AgentChatSlice/components/clientTools/ElicitationWidget.tsx"
printf '%s\n' '--- ElicitationWidget implementation ---'
sed -n '1,230p' "$file"
printf '%s\n' '--- remaining rendering ---'
sed -n '230,444p' "$file"
printf '%s\n' '--- client tool wrapper ---'
cat -n web/oss/src/components/AgentChatSlice/components/clientTools/ClientToolPart.tsx

Repository: Agenta-AI/agenta

Length of output: 21982


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path

widget = Path("web/oss/src/components/AgentChatSlice/components/clientTools/ElicitationWidget.tsx").read_text()
fixture = Path("web/oss/tests/playwright/acceptance/agent-chat/assets/elicitationStream.ts").read_text()

assert 'CLIENT_TOOL_NAMES.has(canonicalToolName(meta.toolName))' in widget
assert 'const subtext = [asker, requiredCount > 0 ? `${requiredCount} required` : null]' in widget
assert 'export const REQUEST_INPUT_TOOL_NAME = "__ag__request_input"' in fixture
assert 'toolName?: string' in fixture

# Model the rendered subtext branch used by the widget.
def subtext(is_own_tool: bool, required_count: int) -> str:
    asker = None if is_own_tool else "Asked by Request input"
    return " · ".join(x for x in (asker, f"{required_count} required" if required_count > 0 else None) if x)

assert subtext(True, 1) == "1 required"
assert subtext(False, 1) == "Asked by Request input · 1 required"
assert "1 required" in subtext(False, 1)
assert subtext(False, 1) != "1 required"

print("own tool:", subtext(True, 1))
print("named tool:", subtext(False, 1))
print("substring matcher would accept named tool:", "1 required" in subtext(False, 1))
print("exact matcher rejects named tool:", subtext(False, 1) != "1 required")
PY

Repository: Agenta-AI/agenta

Length of output: 310


Use an exact required-count assertion.

getByText("1 required") can match Asked by Request input · 1 required. Add {exact: true} to check the complete subtext.

@github-actions

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-pr-6007.up.railway.app/w
Project agenta-oss-clone-spike
Image tag pr-6007-79b7c13
Status Deployed
Railway logs Open logs
Workflow logs View workflow run
Updated at 2026-08-14T08:20:38.937Z

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 size:XXL This PR changes 1000+ lines, ignoring generated files. ux

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Show readable summaries for tool activity in playground

1 participant