Say what a tool did, not where it ran

An agent event set its label to the SSH profile's name, so the transcript read
"homeserver · ls -la" -- naming the machine rather than the thing that was done.
Built-in tools set no label at all and fell back to the function name, so a
saved memory read "memory_add". The status line said "Running shell_run…" and
the approval card had its own hand-written wording. Four places, four answers,
nothing checking that any of them agreed.

services/tool_labels.py is the one table all of them read now. Bash, Read,
Write, List, Web search, Memory saved; an icon each, instead of everything
being the sparkle.

The precedence is inverted on purpose. Tool events are persisted in
Message.tool_calls_json, so every agent row already on disk carries the profile
name -- a resolver that preferred the stored value would fix nothing for any
transcript that already exists. So a name the table knows resolves from the
table, and a name it does not -- a custom HTTP tool, an MCP tool, whose labels
are per row and cannot be tabulated -- keeps its own. One rule, both cases
correct. The machine moves to `detail`, where "where this ran" belongs.

tool_label and tool_icon are Jinja globals because a message bubble is rendered
from four handlers, and a fifth thing each of them must remember to pass is a
fifth thing one of them will forget.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaroslav Beneš
2026-08-03 10:58:02 +02:00
parent 8a3a225fea
commit 374982174f
8 changed files with 352 additions and 26 deletions
@@ -195,6 +195,12 @@
<path d="M13.5 3.5V9H19M8.5 13h7M8.5 16.5h7"/>
</symbol>
<!-- Changing part of a file. A plus over a minus, which is what a diff looks
like everywhere else; the pencil is already taken by writing one whole. -->
<symbol id="i-diff" viewBox="0 0 24 24">
<path d="M12 3.5v7M8.5 7h7M8.5 17h7"/>
</symbol>
<!-- A drag handle. Dots rather than lines: lines at this size read as a
hamburger, which means something else entirely. -->
<symbol id="i-grip" viewBox="0 0 24 24">