Commit bb9ea73
authored
feat(webui): meaningful session names in the Activity Log + link the Sessions page (#836)
* feat(webui): meaningful session names in the Activity Log + link the Sessions page
The Activity Log's Session filter showed opaque id suffixes ("...139c9",
"...851e1"), which tell the user nothing about which AI client a session was.
Root cause was a two-sided miss. Activity.vue already read
`a.metadata?.client_name` — but the backend never writes that field, so the
label always fell through to the id suffix. Meanwhile the client name has
existed all along on the session record (captured from the MCP `initialize`
handshake's clientInfo) and is already served by GET /api/v1/sessions.
Fixed by joining the two at read time: activity rows already carry session_id
as a foreign key, and the session record is the canonical home for the client
name. The alternative — denormalizing the name into every activity record —
would mean a storage lookup and cache in the tool-call hot path, and
EmitActivityToolCallCompleted already takes 17 parameters. It would also only
fix records written after the change, leaving existing rows showing hashes.
The filter now reads "Claude Code · 14:32". Two sessions of the same client
started in the same minute get a disambiguating suffix; a session with no
clientInfo keeps the old id-suffix fallback, so this is never worse than before.
Also links the Sessions page. A complete Sessions.vue and a /sessions route
already existed — they were simply never added to the personal-edition sidebar
(only to the Teams admin menu). So this unhides a page rather than building one.
Placed above Activity Log: a session is the parent of activity records.
Display names mirror the macOS tray's connectedClientNames so the two surfaces
agree on what a client is called.
* fix(webui): resolve sessions that appear after mount; bound and disambiguate labels
Four defects found by cross-model (Codex) review of the session-name join.
1. Stale join (the important one). loadSessions ran only on mount, but the
Activity Log is a LIVE page: a client that connects while it is open delivers
rows for a session the map has never seen, so its filter option stayed an
opaque "...139c9" until the page was remounted.
Now a watch on `activities` refreshes the join whenever a genuinely new
session id appears. Concurrent fetches are coalesced so an SSE burst cannot
fan out into N parallel requests.
2. Refetch storm, latent in the fix for (1). The core retains only the 100 most
recent sessions, so an old session's activity rows can outlive its record and
never become resolvable. Left alone, every refresh would see them as unknown
and refetch forever. Ids that are still missing after a fresh fetch are
remembered as unresolvable and never asked about again. (A failed FETCH is
deliberately not marked unresolvable — that is transient and should retry.)
Those rows keep the id-suffix fallback, i.e. exactly today's behaviour.
3. Colliding "disambiguators". The suffix was a fixed 5 chars, which is not
unique: two same-client sessions started in the same minute whose ids end in
the same 5 characters produced byte-identical labels — and for sessions with
no client name, the suffix IS the whole label. The suffix now grows until it
actually tells the group apart.
4. Unbounded client name. clientInfo.name is attacker-controllable: any MCP
client can send an arbitrary string at initialize and the core stores it
verbatim. Vue escapes it, so this is not XSS — but a 10KB name would be dumped
into a <select> option and wreck the filter layout. Unrecognised names are now
whitespace-collapsed and truncated to 32 chars. Recognised clients are
unaffected (they map to a short display name).
Also sorts the picker by epoch ms rather than by comparing ISO strings: those
carry a timezone offset, so lexical order is not chronological order across
offsets. Ties break on id, so the order is stable instead of depending on Map
insertion.
Verified live, without a page reload: a session created while the Activity Log
was open resolved from a hash to "Gemini · 08:33 AM" on the next refresh, using
exactly one extra /api/v1/sessions call — and five further refreshes triggered
zero more. 282/282 frontend tests pass (6 new, covering each defect above).1 parent 3bf4f3a commit bb9ea73
6 files changed
Lines changed: 429 additions & 15 deletions
File tree
- frontend
- src
- components
- utils
- views
- tests/unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
336 | 336 | | |
337 | 337 | | |
338 | 338 | | |
| 339 | + | |
339 | 340 | | |
340 | 341 | | |
341 | 342 | | |
| |||
344 | 345 | | |
345 | 346 | | |
346 | 347 | | |
| 348 | + | |
347 | 349 | | |
| 350 | + | |
348 | 351 | | |
349 | 352 | | |
350 | 353 | | |
351 | 354 | | |
352 | 355 | | |
| 356 | + | |
353 | 357 | | |
354 | 358 | | |
355 | 359 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
263 | 263 | | |
264 | 264 | | |
265 | 265 | | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
266 | 278 | | |
267 | 279 | | |
268 | 280 | | |
| |||
542 | 554 | | |
543 | 555 | | |
544 | 556 | | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
545 | 561 | | |
546 | 562 | | |
547 | 563 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
694 | 694 | | |
695 | 695 | | |
696 | 696 | | |
| 697 | + | |
697 | 698 | | |
698 | 699 | | |
699 | 700 | | |
| |||
766 | 767 | | |
767 | 768 | | |
768 | 769 | | |
| 770 | + | |
769 | 771 | | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
770 | 832 | | |
771 | | - | |
| 833 | + | |
772 | 834 | | |
773 | | - | |
774 | | - | |
775 | | - | |
776 | | - | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
777 | 843 | | |
778 | 844 | | |
779 | 845 | | |
780 | | - | |
781 | | - | |
782 | | - | |
783 | | - | |
784 | | - | |
785 | | - | |
786 | | - | |
787 | | - | |
788 | | - | |
789 | | - | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
790 | 866 | | |
791 | 867 | | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
| 874 | + | |
792 | 875 | | |
793 | 876 | | |
794 | 877 | | |
| |||
1182 | 1265 | | |
1183 | 1266 | | |
1184 | 1267 | | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
1185 | 1274 | | |
1186 | 1275 | | |
1187 | 1276 | | |
| |||
1191 | 1280 | | |
1192 | 1281 | | |
1193 | 1282 | | |
| 1283 | + | |
1194 | 1284 | | |
1195 | 1285 | | |
1196 | 1286 | | |
| |||
0 commit comments