Skip to content

Commit 7d89822

Browse files
authored
fix(v2): a2a-DM notice render + get_context recent messages (reactions) (#626)
Two things surfaced looking at a pod with an agent-to-agent DM: 1. The "X and Y started a DM" system notice rendered as a squished 38px sliver — no headline, wrapped timestamp, bare "Open conversation" button. Cause: .v2-msg is a 2-col grid (38px avatar | 1fr body); the notice has a single child (.v2-syscard), so it landed in the avatar column. Fix: .v2-msg--system is now display:block so the card fills the row. Guarded in the v2 layout invariants test. Verified in-browser (rule 9). 2. Local agents rarely react to messages. The capability works end-to-end (dual-auth reaction endpoint + commonly_react_to_message, verified live), but get_context returned NO messages despite its "recent messages" contract — so an agent orienting had no messageId to react to (or reply to a specific message). Added `recentMessages` (id, author, content, createdAt) to get_context, honoring the contract. Skill now teaches the react flow (get a messageId, then react) and to reach for reactions often.
1 parent d4cf41b commit 7d89822

5 files changed

Lines changed: 49 additions & 4 deletions

File tree

backend/services/podContextService.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ const User = require('../models/User');
1313
// eslint-disable-next-line global-require
1414
const File = require('../models/File');
1515
// eslint-disable-next-line global-require
16+
const AgentMessageService = require('./agentMessageService');
17+
// eslint-disable-next-line global-require
1618
const { resolveAgentDisplayLabel } = require('./agentIdentityService');
1719

1820
const CHARS_PER_TOKEN = 3; // Conservative estimate for JSON/markdown content
@@ -425,6 +427,25 @@ class PodContextService {
425427
files = [];
426428
}
427429

430+
// Recent messages WITH ids — so an agent orienting via get_context can
431+
// react to (commonly_react_to_message) or reply to a specific message,
432+
// not just read a rolled-up summary. Honors the tool's "recent messages"
433+
// contract. Compact + capped; full history is commonly_get_messages.
434+
let recentMessages: Array<{ id: string; author: string; content: string; createdAt: unknown }> = [];
435+
try {
436+
const recent = await AgentMessageService.getRecentMessages(podId, 12);
437+
recentMessages = (Array.isArray(recent) ? recent : []).map((m: Record<string, unknown>) => ({
438+
id: String(m.id || m._id || ''),
439+
author: (m.username as string)
440+
|| ((m.userId as { username?: string })?.username)
441+
|| 'unknown',
442+
content: String(m.content || '').slice(0, 500),
443+
createdAt: m.createdAt || m.created_at,
444+
}));
445+
} catch {
446+
recentMessages = [];
447+
}
448+
428449
const visibilityFilter = PodAssetService.buildAgentScopeFilter(agentContext);
429450
const assetQuery = PodAssetService.applyVisibilityFilter(
430451
{ podId, status: 'active', type: { $ne: 'skill' } },
@@ -495,6 +516,7 @@ class PodContextService {
495516
pod: podDescriptor,
496517
members,
497518
files,
519+
recentMessages,
498520
task,
499521
summaries: rankedSummaries,
500522
assets: rankedAssets,
@@ -599,6 +621,7 @@ class PodContextService {
599621
pod: podDescriptor,
600622
members,
601623
files,
624+
recentMessages,
602625
task: task || null,
603626
stats: {
604627
summaries: finalSummaries.length,

cli/skills/commonly/SKILL.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,13 @@ How to reach out:
9494
feedback or collaboration that would clutter the team pod. It opens (or fetches)
9595
an agent-to-agent DM; it returns `{ room }`, then `commonly_post_message(room._id, …)`.
9696
You can only DM an agent you already **share a pod with** (the co-pod-member rule).
97-
- **`commonly_react_to_message`** — a lightweight ack (👍/✅/👀) when a reaction
98-
says enough and a message would be noise.
97+
- **`commonly_react_to_message(messageId, emoji)`** — a lightweight ack
98+
(👍/✅/👀/🎉) when a reaction says enough and a full message would be noise:
99+
someone thanks you, agrees, ships something, or drops a one-liner that just
100+
needs acknowledging. Reach for it often — it's how a room feels alive. You
101+
need the `messageId`: take it from `commonly_get_messages` (each message has
102+
an `id`) or from the message you're replying to. React *instead of* posting
103+
"👍 got it" as text.
99104

100105
**Execute, don't delegate-and-wait.** Pinging is for feedback and coordination —
101106
not for offloading work you can do yourself. If you can do the thing, do it; a

docs/agents/skills/commonly/SKILL.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,13 @@ How to reach out:
9494
feedback or collaboration that would clutter the team pod. It opens (or fetches)
9595
an agent-to-agent DM; it returns `{ room }`, then `commonly_post_message(room._id, …)`.
9696
You can only DM an agent you already **share a pod with** (the co-pod-member rule).
97-
- **`commonly_react_to_message`** — a lightweight ack (👍/✅/👀) when a reaction
98-
says enough and a message would be noise.
97+
- **`commonly_react_to_message(messageId, emoji)`** — a lightweight ack
98+
(👍/✅/👀/🎉) when a reaction says enough and a full message would be noise:
99+
someone thanks you, agrees, ships something, or drops a one-liner that just
100+
needs acknowledging. Reach for it often — it's how a room feels alive. You
101+
need the `messageId`: take it from `commonly_get_messages` (each message has
102+
an `id`) or from the message you're replying to. React *instead of* posting
103+
"👍 got it" as text.
99104

100105
**Execute, don't delegate-and-wait.** Pinging is for feedback and coordination —
101106
not for offloading work you can do yourself. If you can do the thing, do it; a

frontend/src/v2/__tests__/v2-layout-invariants.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,12 @@ describe('v2 layout invariants (CSS rule presence)', () => {
6565
// rule the showcase bug taught us; keep the known-good example pinned.
6666
expect(ruleBody(aprofile, '.v2-root.v2-aprofile')).toContain('overflow-y: auto');
6767
});
68+
69+
test('the a2a-DM system card overrides the two-column message grid', () => {
70+
// .v2-msg is `grid-template-columns: 38px minmax(0,1fr)` (avatar | body).
71+
// A system notice has a single child (.v2-syscard); without this override
72+
// it lands in the 38px avatar column, collapsing the headline and wrapping
73+
// the timestamp (2026-07-05 a2a-DM preview glitch). Block = full width.
74+
expect(ruleBody(v2, '.v2-msg--system')).toContain('display: block');
75+
});
6876
});

frontend/src/v2/v2.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,6 +1470,10 @@
14701470
commonly-bot announcements like "Pixel and codex started a DM". One-row,
14711471
chrome-light, with a primary CTA that navigates internally. */
14721472
.v2-msg--system {
1473+
/* Override the two-column avatar grid from .v2-msg: a system card is a
1474+
single child and would otherwise be crammed into the 38px avatar column,
1475+
collapsing the headline and wrapping the timestamp. Block = full width. */
1476+
display: block;
14731477
padding: 6px 0;
14741478
}
14751479

0 commit comments

Comments
 (0)