Skip to content

feat: tool card rendering polish — monospace titles, raw-input preference, gemini noise filter#12

Merged
0xmrpeter merged 3 commits into
Open-ACP:mainfrom
kevin-david:feat-tool-card-rendering-polish
May 18, 2026
Merged

feat: tool card rendering polish — monospace titles, raw-input preference, gemini noise filter#12
0xmrpeter merged 3 commits into
Open-ACP:mainfrom
kevin-david:feat-tool-card-rendering-polish

Conversation

@kevin-david

Copy link
Copy Markdown
Contributor

Why

Two tool-card rendering issues, both surfaced while testing the adapter against gemini-acp but applicable to any agent that emits paths/commands in tool calls.

What changes

1. Render code-y titles as monospace instead of **bold**. Execute/read/edit/write/delete/search-kind titles are user content — shell commands, file paths, search patterns. Inline code reads better than bold and avoids accidental markdown collisions in command strings. Descriptive labels (Update Topic Context, Invoke Subagent) stay bold via the CODEY_KINDS allow-list.

2. Restructure buildTitle to prefer rawInput over displayTitle. Some agents (gemini-acp) emit prettified displayTitle strings with capitalized first letters that don't match the actual paths on disk (Life/... for the real life/...). Prefer the raw command, file_path, pattern, etc. so the title reflects truth.

3. Drop the final capitalize(entry.name) fallback. Gemini puts the real content directly in entry.name with no rawInput/displayTitle, and capitalizing the first letter corrupted real paths. Argless commands like Pwd are still lowercased via the EXECUTE_KINDS short-identifier regex branch above.

4. Extend evaluateNoise to flag gemini's internal bookkeeping tools (Update Topic Context, Invoke Subagent) as noise so they're hidden at low/medium and only shown with 👁️ at high.

Test plan

  • Verified life/finance/foo.md no longer renders as Life/finance/foo.md
  • Verified pwd (was Pwd) renders correctly via EXECUTE_KINDS branch
  • Verified Update Topic Context no longer appears in medium-mode tool cards
  • Reviewer: test on Claude Code too to confirm no regression for agents that provide proper rawInput

🤖 Generated with Claude Code

Comment thread src/activity.ts Outdated
Comment thread src/activity.ts
…nput

Two related tweaks to how tool cards present tool names/paths:

1. Render execute/read/edit/write/delete/search-kind titles as inline `code`
   instead of **bold**. Shell commands, file paths, and search patterns are
   code-y user content — monospace reads better and avoids accidental
   markdown collisions. Descriptive labels ("Update Topic Context",
   "Invoke Subagent") stay bold.

2. Restructure buildTitle to prefer rawInput-derived values over the agent's
   displayTitle. Some agents (gemini-acp) emit prettified displayTitle
   strings with capitalized first letters that don't match the actual paths
   on disk ("Life/..." for the real "life/..."). Prefer the raw command,
   file_path, pattern, etc. so the title reflects truth.

3. Drop the final capitalize(entry.name) fallback. Gemini puts the real
   content directly in entry.name with no rawInput/displayTitle, and
   capitalizing the first letter corrupted real paths. Argless commands
   like "Pwd" are still lowercased via the EXECUTE_KINDS branch
   (short-identifier regex).

4. Extend evaluateNoise to flag gemini's internal bookkeeping tools
   (Update Topic Context, Invoke Subagent) as noise so they're hidden at
   low/medium and only shown with 👁️ at high.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kevin-david kevin-david force-pushed the feat-tool-card-rendering-polish branch from b50baf3 to 7f34799 Compare May 11, 2026 05:07
@0xmrpeter

Copy link
Copy Markdown
Contributor

The four issues you're tackling here are all real and worth fixing — especially the gemini re-emission deduplication, which would have caused very noisy Discord threads. Good investigation work!

A few things to resolve before this leaves DRAFT:

Blocking:

  1. onToolCall dedup guard calls upsert() instead of get()upsert overwrites the existing entry with a fresh "running" state, discarding any completed/failed status that prior onToolUpdate calls may have already applied. The guard should read the existing entry (.get()) and re-render it as-is, not replace it.

  2. Existing test regressionformatting.test.ts:82 asserts **config.ts** (bold) for a read kind spec, but this PR adds read to CODEY_KINDS which makes it render as monospace (`config.ts` instead). The test needs updating to match the new expected format, otherwise CI will fail.

  3. Dead code after restructure — the old EXECUTE_KINDS.has(kind) block (now containing only the description check) is unreachable for any tool that has a command or short-identifier name. It should be folded into the new top block or removed to avoid confusing future readers.

Non-blocking:

  • includes("topic") in evaluateNoise is a broad substring match — it'd silently suppress any future tool whose name contains "topic" (e.g., get_forum_topic, create_topic). An exact-match Set of known gemini internal tool names would be safer and more explicit.
  • isCodeyKind is a one-liner wrapper around CODEY_KINDS.has() with no added readability — could be inlined at the call site.

Looking forward to seeing this land once those are sorted!

- onToolCall dedup guard now reads the existing previous-map entry via
  .get() and re-renders it as-is. Previously it called upsert(), which
  rebuilt the entry from the (stale) re-emitted tool_call event and
  clobbered any status/content already applied via onToolUpdate.

- Update renderSpecSection medium/high-mode test expectations: read-kind
  spec now renders as `code` (monospace) instead of **bold** since this
  PR added "read" to CODEY_KINDS.

- Fold the trailing EXECUTE_KINDS description fallback in buildTitle()
  into the top EXECUTE_KINDS block — it was an unreachable-looking
  remnant after the restructure. Functionally identical.

- Replace evaluateNoise substring checks with an exact-match Set of
  known gemini internal tool names. includes("topic") would have
  silently suppressed legitimate future tools like get_forum_topic.

- Inline the one-line isCodeyKind wrapper at its single call site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kevin-david added a commit to kevin-david/discord-adapter that referenced this pull request May 13, 2026
- onToolCall dedup guard now reads the existing previous-map entry via
  .get() and re-renders it as-is. Previously it called upsert(), which
  rebuilt the entry from the (stale) re-emitted tool_call event and
  clobbered any status/content already applied via onToolUpdate.

- Update renderSpecSection medium/high-mode test expectations: read-kind
  spec now renders as `code` (monospace) instead of **bold** since this
  PR added "read" to CODEY_KINDS.

- Fold the trailing EXECUTE_KINDS description fallback in buildTitle()
  into the top EXECUTE_KINDS block — it was an unreachable-looking
  remnant after the restructure. Functionally identical.

- Replace evaluateNoise substring checks with an exact-match Set of
  known gemini internal tool names. includes("topic") would have
  silently suppressed legitimate future tools like get_forum_topic.

- Inline the one-line isCodeyKind wrapper at its single call site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous exact-match Set used names from the PR description that
turned out to be wrong for the live event path. Gemini-acp emits tool
titles two ways:

  1. Live events use invocation.getDescription(), producing dynamic
     strings: 'Update topic to: "<title>"' and 'Update tactical
     intent: "<intent>"'.
  2. Replayed/completed events use the tool's static displayName:
     "Update Topic Context", "Invoke Subagent".

The exact set covered only path (2), so live topic-update events were
slipping through unfiltered. Add format-anchored prefixes ('update
topic to: "', 'update tactical intent: "') alongside the exact set.
The trailing `: "` is part of gemini's format and keeps the match
specific enough that a future tool named "Update topic permissions"
won't be hidden by accident.

Live subagent invocations emit "Invoke <agent-name>" with a user-
configurable suffix — unmatchable without enumerating every agent
name, so we leave them visible rather than over-match with a bare
"invoke " prefix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kevin-david kevin-david marked this pull request as ready for review May 13, 2026 14:57
@0xmrpeter 0xmrpeter merged commit 068349b into Open-ACP:main May 18, 2026
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.

2 participants