Repository navigation
feat(gui-app): agent mention rows — last-activity time, archived demotion and badge - #1127
Conversation
…tion and badge
The @ mention Agent rows (Agents submenu and root search) spent their
trailing slot on the interface label ("Chat", "Terminal · ...") - which
says nothing a user picking between same-named rows can use - and
listed archived chats indistinguishably from live ones, at equal rank.
- The trailing slot now shows the Agent's last-activity time on the
sidebar rows' compact ladder (now/10m/4h/1d/1w/short date), sampled
once when the row mounts - menu rows do not tick. `updatedAt` is a
real activity clock for Agents (it bumps on streaming ticks); rows
without one (files, categories, terminals - whose updatedAt is a
start time) carry null and render no time. Harness name and the
reference-only marker stay in the detail; the preview panel keeps
the interface label, where there is room for it.
- Archived Agents (chat and terminal records both carry archivedAt)
rank below live ones at EQUAL match quality: archived-ness slots
between match score and recency in rankAgentEntries, so it outweighs
recency but never relevance - an archived prefix hit still beats a
live substring hit. The provider order feeds root search as the
candidates' input order, where the fuzzy pass tie-breaks by input
index and the prefix/substring tiers resort stably - so the same
rule holds there: demotion within a match tier, never across tiers.
- Archived rows carry an "Archived" badge (semibold, muted - the
sidebar's marker styling) as a flag on MentionMenuEntry rather than
text in `detail`, so it can never truncate away. The row is not
dimmed: opacity is this menu's disabled-row signal, and archived
rows stay selectable.
Signed-off-by: Pranshu Gupta <traycer@MacBook-Pro.local>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
Summary by CodeRabbit
WalkthroughAgent mention entries now include archive and activity metadata. Providers rank archived Agents below active Agents within equal match tiers and use recency as a tie-breaker. Mention rows display compact activity times and an ChangesArchived Agent mention flow
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant AgentRecords
participant useMentionItems
participant MentionProviders
participant MentionMenuItem
AgentRecords->>useMentionItems: Provide archivedAt values
useMentionItems->>MentionProviders: Build Agent mention entries
MentionProviders->>MentionProviders: Rank by match, archive status, and recency
MentionProviders->>MentionMenuItem: Return ranked entries
MentionMenuItem->>MentionMenuItem: Render activity time and Archived marker
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@clients/gui-app/src/components/chat/composer/menu/mention-menu-item.tsx`:
- Around line 15-18: The MentionRowTime label is initialized only on mount and
does not update when timestamp changes. Derive or memoize the label from
props.timestamp so rerenders reflect newer timestamps. In
clients/gui-app/src/components/chat/composer/menu/mention-menu-item.tsx lines
15-18, update MentionRowTime; in
clients/gui-app/src/components/chat/composer/menu/__tests__/mention-menu-item.test.tsx
lines 28-40, rerender the same row with a newer updatedAt and assert the label
changes.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 1afff80b-e5ad-46d1-ba40-65adc9507511
📒 Files selected for processing (12)
clients/gui-app/src/components/chat/composer/__tests__/composer-mention-dismissal-flow.test.tsxclients/gui-app/src/components/chat/composer/__tests__/composer-mention-flow.test.tsxclients/gui-app/src/components/chat/composer/__tests__/composer-picker-store.test.tsclients/gui-app/src/components/chat/composer/menu/__tests__/mention-menu-item.test.tsxclients/gui-app/src/components/chat/composer/menu/mention-menu-item.tsxclients/gui-app/src/components/chat/composer/picker/__tests__/use-mention-items.test.tsclients/gui-app/src/components/chat/composer/picker/use-mention-items.tsclients/gui-app/src/lib/composer/mentions/__tests__/providers.test.tsxclients/gui-app/src/lib/composer/mentions/__tests__/root-search-ranking.test.tsclients/gui-app/src/lib/composer/mentions/mention-entry-display.tsxclients/gui-app/src/lib/composer/mentions/providers.tsxclients/gui-app/src/lib/composer/types.ts
Two review findings on the Agent row timestamp: - The menu keys rows by index, so a ranked reorder while the picker stays open reuses a row's component instance for a different Agent; the time leaf's once-sampled label kept the previous occupant's time. The leaf is now keyed by the entry id, remounting - and resampling - when a different Agent takes the row. - The record's updatedAt is a mutation clock, not a pure activity clock: the host's archive write bumps it, as do renames and other metadata writes. An archived row would therefore always read "Archived · now" after the archive action. Archived rows now carry no time label at all - the badge alone marks them - and the archived-recency ordering caveat (roughly most-recently- archived/touched first; the pre-archive activity time is unrecoverable client-side) is documented at the comparator. Live rows keep the timestamp; a rename refreshing it is the accepted residual until an activity-specific clock exists host-side. Also a comment-hygiene pass over the branch diff: change-narration wording rewritten as timeless descriptions. Signed-off-by: Pranshu Gupta <traycer@MacBook-Pro.local>
Merges `main` and answers the Codex/CodeRabbit threads on #1128. The merge itself broke two files nothing conflicted in: `MentionMenuEntry` grew `updatedAt`/`archived` on main (#1127) while this branch grew `labelPrefix`. GitHub rows leave the new time slot null - their age is already composed into `detail` beside the repository, and filling both renders it twice. Correctness: - Sent-message chips render GitHub mentions. Their `path` is a synthetic token (`github-pr:org/repo#12`), so the generic chip read a basename off it and showed the raw token as the tooltip: `#4917` became `traycer#4917` with a file icon the moment the message was posted. - The live search is gated on `supported`, like the catalog reads already were. A host swap could negotiate the method away while the section stayed open. - The held-row key includes the scope. Holding rows across a host, epic or roots change offered selectable mentions the new scope cannot resolve. - Placeholder catalog responses are reported as the unanswered case rather than as rows. Same class as above: `keepPreviousData` hands over the PREVIOUS scope's answer, and every row in it is committable. - A placeholder response no longer arms the automatic stale follow-up, which spent a GitHub request deciding for a scope that had not answered yet. - `sourceStatus` and `notice` are gated on `wanted` like the rows, so a `gh-unavailable` banner cannot outlive the search that produced it. - URL host comparison is case-folded, like owner and repo already were. - `onStart` judges a new `@` by the root rules. The store can still hold the departing session's drilled step there, so `@x, y` could open the menu. - Persisted filters are coerced to their section on read - identity-preserving when there is nothing to coerce, because the selector's result identity is what decides whether to re-render. - The refresh control uses `AgentSpinningDots` rather than a spun icon. Tests: the vacuous halves are the point - marker assertions that checked for a marker the fixture never produces, a round-trip that passed if both sides went empty, absence assertions with no positive control, and store assertions comparing a local constant to itself. Category lookups go by label, not index. Not changed, with reasons in the thread replies: the menu's `min(70vh,24rem)` cap is the repository's viewport-cap idiom, not a fixed bound; the catalog's Zustand store is deliberate and outlives query `gcTime` by design. Signed-off-by: Hardik Shingala <hardik@traycer.ai>
Clears #889's conflict and picks up the Model Providers wire contract (#1058), the provider CLI version management (#1103) and the agent mention rows (#1127) that landed while this branch was in review. Conflicts, all in the notifications family plus the RPC registry: - protocol/src/host/registry.ts: both sides split a fourth literal out of the base definition to stay under TS7056 - ours for the post-v1 notification minors, main's for `providers.*`. Kept both, so the registry now merges five literals. `DuplicateHostRpcMethodNames` needed the PROVIDERS x NOTIFICATION pair neither side wrote; without it the duplicate-method tripwire silently stops covering one of the ten pairs while still compiling. - stores/notifications/merged-notifications.ts: ours extracted the attention sort into `comparePartitionedAttentionOrder` (tier, then plane, so a local `createdAt` is never compared against a cloud relay one); main added the D7 local-pack-first key. Composed as tier -> plane -> pack locality. `compareProviderPackLocalFirst` already carries the newest-first then `feedId` tail `compareFeedCandidates` did, so nothing below the locality key changed. - Seven notification suites: kept our `useNotificationHost` mock beside main's `importOriginal`-based `use-host-directory-entry` mock. OSS graph compiles; protocol 147 files / 2310 tests green; the 36 gui-app notification suites green (493 tests). Signed-off-by: Hardik Shingala <hardik@traycer.ai>
Summary
Polish for the
@mention Agent rows (Agents submenu + the same rows in root search):now/10m/4h/1d/1w/short date,formatCompactRelativeTimefromlib/relative-time.ts— the exact formatter the sidebar chat rows use). Sampled once when the time leaf mounts (useStateinitializer): menu rows deliberately do not tick. The leaf is keyed byentry.id, so a ranked reorder while the picker stays open (the menu keys rows by index) remounts and resamples for the Agent that now occupies the row instead of retaining the previous occupant's time. Rows without an activity clock — files, categories, terminals, whoseupdatedAtis a start time — carrynulland render no time. Harness name + the reference-only marker stay in the detail; the preview panel keeps the interface label, where there's room.updatedAtis a mutation clock). The record'supdatedAtapproximates activity (it bumps on streaming ticks) but is also bumped by the host's archive write, renames, and other metadata writes. Consequences handled: archived rows show no time at all (the badge alone marks them — otherwise every archived row would read "Archived · now" right after archiving), and the archived-recency ordering within a match band is documented at the comparator as "most recently archived/touched first" (the pre-archive activity time is overwritten and unrecoverable client-side; the sidebar rows read the same clock). Accepted residual on live rows: a rename refreshes the displayed time. A proper fix needs an activity-specific timestamp on the host's projections — out of scope for this repo.archivedAt; archived-ness now slots between match score and recency inrankAgentEntries— it outweighs recency (an archived record more recent than a live one still sorts below it) but never relevance (an archived prefix hit still beats a live substring hit). Composition with feat(gui-app): fuzzy-rank the slash-command menu + step-aware picker height #1045's prefix/substring tiers in root search: the provider's ranked order feeds root search as the candidates' input order, the fuzzy pass tie-breaks equal scores by input index, and the tier resort is stable — so demotion applies within a match tier, never across tiers. Documented inrankAgentEntries' doc comment and pinned by a registry-level test (same-label archived/live tie → live first; archived prefix vs live substring → archived first via tiers).MentionMenuEntry(per the entry-model note: a flag, not text indetail, so it can never truncate away), rendered semibold + muted to match the sidebar's archived marker. The row is not dimmed (sidebar usesopacity-55): opacity is this menu's disabled-row signal, and archived rows stay selectable.Not touched, flagged for the record: plain terminal rows' detail is the working directory (not a kind label), so they don't have the "Chat"-style problem; their
updatedAtiscreatedAt, which is why they get no time label.Validation
bunx vitest run src/components/chat/composer src/lib/composer src/hooks/composer— green (newmention-menu-itemrender suite: time ladder, badge+detail+time, no-clock rows, reorder-resample regression — a different Agent taking the row gets a fresh time; providers ranking suites: archived-below-live at equal quality incl. recency-vs-archived, relevance-over-archived, root-search tier composition, archived rows carry no time; entry-buildingarchivedAt→archivedmapping)bun run compileclean,react-doctor(changed files) clean, pre-commit passed at commit, DCO signed, no new deps