Skip to content

Commit 910a319

Browse files
authored
Merge pull request #61 from AgentWorkforce/fix/dm-visibility-dashboard-e2e
Harden dashboard DM visibility + naming coverage
2 parents 353db41 + 6b09c7b commit 910a319

13 files changed

Lines changed: 333 additions & 97 deletions

package-lock.json

Lines changed: 71 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
],
99
"scripts": {
1010
"dev": "concurrently \"npm run dev -w @agent-relay/dashboard\" \"npm run dev -w @agent-relay/dashboard-server\"",
11+
"dev:relay": "npm run build && cd ../relay && node dist/src/cli/bootstrap.js up --verbose",
1112
"dev:mock": "concurrently \"npm run dev -w @agent-relay/dashboard\" \"npm run dev:mock -w @agent-relay/dashboard-server\"",
1213
"dev:mock:cloud": "concurrently \"NEXT_PUBLIC_FORCE_CLOUD_MODE=true npm run dev -w @agent-relay/dashboard\" \"npm run dev:mock -w @agent-relay/dashboard-server\"",
1314
"build": "npm run build -w @agent-relay/dashboard-server && npm run build -w @agent-relay/dashboard && rm -rf packages/dashboard-server/out && cp -r packages/dashboard/out packages/dashboard-server/out",

packages/dashboard-server/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@
3333
"prepublishOnly": "npm run build"
3434
},
3535
"dependencies": {
36-
"@agent-relay/config": "^2.4.7",
36+
"@agent-relay/config": "^3.1.11",
3737
"@agent-relay/protocol": "^2.3.14",
38-
"@agent-relay/sdk": "^3.0.1",
38+
"@agent-relay/sdk": "^3.1.11",
3939
"@agent-relay/storage": "^2.3.14",
40-
"@agent-relay/trajectory": "^2.4.7",
41-
"@agent-relay/utils": "^2.4.7",
40+
"@agent-relay/trajectory": "^3.1.11",
41+
"@agent-relay/utils": "^3.1.11",
4242
"@relaycast/sdk": "^0.5.0",
4343
"@relaycast/types": "^0.5.0",
4444
"express": "^5.2.1",

packages/dashboard-server/src/proxy-server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ export function createServer(options: DashboardServerOptions = {}): DashboardSer
386386
const startupConfig = resolveRelaycastConfig();
387387
if (startupConfig?.apiKey) {
388388
console.log(`[dashboard] Relaycast workspace key: ${startupConfig.apiKey}`);
389-
console.log('[dashboard] View messages at https://app.relaycast.dev');
389+
console.log('[dashboard] View messages at https://agentrelay.dev/observer');
390390
}
391391

392392
registerAgentRoutes(app, ctx);
@@ -574,7 +574,7 @@ async function bootstrapRelayApiKeyFromBroker(
574574
if (key) {
575575
setRelayApiKey(key);
576576
console.log(`[dashboard] Relaycast workspace key: ${key}`);
577-
console.log('[dashboard] View messages at https://app.relaycast.dev');
577+
console.log('[dashboard] View messages at https://agentrelay.dev/observer');
578578
return;
579579
}
580580
} catch {

packages/dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"dependencies": {
4747
"@nangohq/frontend": "^0.69.20",
4848
"@relaycast/react": "^0.5.0",
49+
"@relaycast/sdk": "^0.5.0",
4950
"@relaycast/types": "^0.5.0",
5051
"@xterm/addon-fit": "^0.11.0",
5152
"@xterm/addon-search": "^0.16.0",

packages/dashboard/src/components/ChannelChat.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function ChannelChat({
7878

7979
const isDm = channel.startsWith('dm:');
8080
const channelDisplay = isDm
81-
? channel.split(':').slice(1).filter(u => u !== currentUser).join(', ')
81+
? channel.split(':').slice(1).filter(u => u !== currentUser).join(' ')
8282
: channel;
8383

8484
// Convert online user presence to HumanUser format for mentions

packages/dashboard/src/components/ChannelSidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,12 @@ function ChannelItem({ channel, displayName, isSelected, unreadCount, onSelect,
388388

389389
/**
390390
* Format DM channel name for display.
391-
* dm:alice:bob -> "alice, bob" (excluding current user if known)
391+
* dm:alice:bob -> "alicebob"
392392
*/
393393
function formatDmName(channel: string): string {
394394
if (!channel.startsWith('dm:')) return channel;
395395
const parts = channel.split(':').slice(1);
396-
return parts.join(', ');
396+
return parts.join(' ');
397397
}
398398

399399
export default ChannelSidebar;

packages/dashboard/src/lib/relaycastMessageAdapters.test.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
22
import type { Message } from '../types';
3-
import { normalizeRelayDmMessageTargets } from './relaycastMessageAdapters.js';
3+
import { getRelayDmParticipantName, normalizeRelayDmMessageTargets } from './relaycastMessageAdapters.js';
44

55
function setRelayUsername(value?: string): void {
66
const storage = (globalThis as { localStorage?: Storage }).localStorage;
@@ -93,6 +93,35 @@ describe('normalizeRelayDmMessageTargets', () => {
9393
expect(normalized[0]?.to).toBe('Natty');
9494
});
9595

96+
it('maps dm_* targets to object participants using agent_name', () => {
97+
const messages: Message[] = [
98+
{
99+
id: 'msg-2a',
100+
from: 'Natty',
101+
to: 'dm_7b62c72644b9316e7e10a992',
102+
content: 'hello',
103+
timestamp: '2026-02-24T12:00:06.000Z',
104+
},
105+
];
106+
107+
const normalized = normalizeRelayDmMessageTargets(messages, [
108+
{
109+
id: 'dm_7b62c72644b9316e7e10a992',
110+
participants: [{ agent_name: 'Natty' }, { agent_name: 'test-broker-new' }],
111+
},
112+
]);
113+
114+
expect(normalized[0]?.to).toBe('test-broker-new');
115+
});
116+
117+
it('normalizes participant names using getRelayDmParticipantName', () => {
118+
expect(getRelayDmParticipantName({ agent_name: 'Lead', name: 'ignored' })).toBe('Lead');
119+
expect(getRelayDmParticipantName({ agentName: 'Codex-Worker' })).toBe('Codex-Worker');
120+
expect(getRelayDmParticipantName('Test-Broker')).toBe('Test-Broker');
121+
expect(getRelayDmParticipantName({ username: 'human-user' })).toBe('human-user');
122+
expect(getRelayDmParticipantName(123)).toBeNull();
123+
});
124+
96125
it('leaves non-dm and unknown dm targets unchanged', () => {
97126
const messages: Message[] = [
98127
{

packages/dashboard/src/lib/relaycastMessageAdapters.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,30 @@ type RelayDmConversationLike = {
1212
participants: unknown[];
1313
};
1414

15+
export function getRelayDmParticipantName(participant: unknown): string | null {
16+
if (typeof participant === 'string') {
17+
return normalizeRelayIdentity(participant);
18+
}
19+
20+
if (participant === null || typeof participant !== 'object') {
21+
return null;
22+
}
23+
24+
const record = participant as Record<string, unknown>;
25+
const rawName = (
26+
record.agent_name
27+
?? record.agentName
28+
?? record.name
29+
?? record.username
30+
);
31+
32+
if (typeof rawName !== 'string') {
33+
return null;
34+
}
35+
36+
return normalizeRelayIdentity(rawName);
37+
}
38+
1539
function normalizeRelayIdentity(value: string): string {
1640
const trimmed = value.trim();
1741
if (!trimmed) return '';
@@ -39,20 +63,17 @@ function resolveDmRecipient(
3963
const senderKey = normalizeRelayIdentity(sender).toLowerCase();
4064

4165
for (const participant of participants) {
42-
if (typeof participant !== 'string') continue;
43-
const normalized = normalizeRelayIdentity(participant);
66+
const normalized = getRelayDmParticipantName(participant);
4467
if (!normalized) continue;
4568
if (normalized.toLowerCase() !== senderKey) {
4669
return normalized;
4770
}
4871
}
4972

5073
for (const participant of participants) {
51-
if (typeof participant !== 'string') continue;
52-
const normalized = normalizeRelayIdentity(participant);
53-
if (normalized) {
54-
return normalized;
55-
}
74+
const normalized = getRelayDmParticipantName(participant);
75+
if (!normalized) continue;
76+
return normalized;
5677
}
5778

5879
return null;

packages/dashboard/src/providers/ChannelProvider.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,20 @@ export function ChannelProvider({ children }: ChannelProviderProps) {
118118

119119
// Relay channel state
120120
const relayChannelsState = useRelayChannels();
121+
const relayChannelsLoading = relayChannelsState.loading;
122+
const relayChannelsRaw = relayChannelsState.channels;
123+
124+
// Stabilize the mapped channels array — only recompute when the serialized
125+
// channel list actually changes (avoids infinite re-render loops from new
126+
// array references returned by the relay hook on every render).
127+
const relayChannelsKey = useMemo(
128+
() => JSON.stringify(relayChannelsRaw.map(c => c.name + ':' + (c.topic ?? '') + ':' + (c.isArchived ?? false) + ':' + (c.memberCount ?? 0))),
129+
[relayChannelsRaw],
130+
);
121131
const relayMappedChannels = useMemo(
122-
() => relayChannelsState.channels.map(mapRelayChannelToDashboard),
123-
[relayChannelsState.channels],
132+
() => relayChannelsRaw.map(mapRelayChannelToDashboard),
133+
// eslint-disable-next-line react-hooks/exhaustive-deps
134+
[relayChannelsKey],
124135
);
125136

126137
// Channel list state
@@ -225,7 +236,7 @@ export function ChannelProvider({ children }: ChannelProviderProps) {
225236
const activeChannels = relayMappedChannels.filter((channel) => channel.status !== 'archived');
226237
const archivedChannels = relayMappedChannels.filter((channel) => channel.status === 'archived');
227238
setChannelListsFromResponse({ channels: activeChannels, archivedChannels });
228-
setIsChannelsLoading(relayChannelsState.loading);
239+
setIsChannelsLoading(relayChannelsLoading);
229240
return;
230241
}
231242

@@ -253,7 +264,7 @@ export function ChannelProvider({ children }: ChannelProviderProps) {
253264
fetchChannels();
254265
}, [
255266
relayConfigured,
256-
relayChannelsState,
267+
relayChannelsLoading,
257268
relayMappedChannels,
258269
effectiveActiveWorkspaceId,
259270
isWorkspaceFeaturesEnabled,

0 commit comments

Comments
 (0)