@agent-relay/dashboard 2.0.88 → 2.0.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/404.html +1 -1
- package/out/_next/static/chunks/270-8c0b8109123a0c5f.js +73 -0
- package/out/_next/static/chunks/{1028-ff682899d23dc669.js → 9626-10e71fc51b892784.js} +1 -1
- package/out/_next/static/chunks/app/app/[[...slug]]/{page-f33c6214e21ccfdd.js → page-4f01a33b51f23cea.js} +1 -1
- package/out/_next/static/chunks/app/{page-2e38080856c3c293.js → page-2644ed4067d978e0.js} +1 -1
- package/out/about.html +1 -1
- package/out/about.txt +1 -1
- package/out/app/onboarding.html +1 -1
- package/out/app/onboarding.txt +1 -1
- package/out/app.html +1 -1
- package/out/app.txt +2 -2
- package/out/blog/go-to-bed-wake-up-to-a-finished-product.html +1 -1
- package/out/blog/go-to-bed-wake-up-to-a-finished-product.txt +1 -1
- package/out/blog/let-them-cook-multi-agent-orchestration.html +1 -1
- package/out/blog/let-them-cook-multi-agent-orchestration.txt +1 -1
- package/out/blog.html +1 -1
- package/out/blog.txt +1 -1
- package/out/careers.html +1 -1
- package/out/careers.txt +1 -1
- package/out/changelog.html +1 -1
- package/out/changelog.txt +1 -1
- package/out/cloud/link.html +1 -1
- package/out/cloud/link.txt +1 -1
- package/out/complete-profile.html +1 -1
- package/out/complete-profile.txt +1 -1
- package/out/connect-repos.html +1 -1
- package/out/connect-repos.txt +1 -1
- package/out/contact.html +1 -1
- package/out/contact.txt +1 -1
- package/out/dev/cli-tools.html +1 -1
- package/out/dev/cli-tools.txt +1 -1
- package/out/dev/log-viewer.html +1 -1
- package/out/dev/log-viewer.txt +1 -1
- package/out/docs.html +1 -1
- package/out/docs.txt +1 -1
- package/out/history.html +1 -1
- package/out/history.txt +1 -1
- package/out/index.html +1 -1
- package/out/index.txt +2 -2
- package/out/login.html +1 -1
- package/out/login.txt +1 -1
- package/out/metrics.html +1 -1
- package/out/metrics.txt +1 -1
- package/out/pricing.html +1 -1
- package/out/pricing.txt +1 -1
- package/out/privacy.html +1 -1
- package/out/privacy.txt +1 -1
- package/out/providers/setup/claude.html +1 -1
- package/out/providers/setup/claude.txt +1 -1
- package/out/providers/setup/codex.html +1 -1
- package/out/providers/setup/codex.txt +1 -1
- package/out/providers/setup/cursor.html +1 -1
- package/out/providers/setup/cursor.txt +1 -1
- package/out/providers.html +1 -1
- package/out/providers.txt +1 -1
- package/out/security.html +1 -1
- package/out/security.txt +1 -1
- package/out/signup.html +1 -1
- package/out/signup.txt +1 -1
- package/out/terms.html +1 -1
- package/out/terms.txt +1 -1
- package/package.json +4 -4
- package/src/components/hooks/useAllDMs.ts +88 -0
- package/src/components/hooks/useDirectMessage.ts +12 -0
- package/src/providers/MessageProvider.tsx +16 -18
- package/out/_next/static/chunks/3238-24c1e4b1cefe3c71.js +0 -73
- /package/out/_next/static/{hDG4FHMjeVX6ES941qVjL → SiFRCTvfNgk02ZX5qxVBQ}/_buildManifest.js +0 -0
- /package/out/_next/static/{hDG4FHMjeVX6ES941qVjL → SiFRCTvfNgk02ZX5qxVBQ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAllDMs Hook
|
|
3
|
+
*
|
|
4
|
+
* Fetches ALL DM conversations in the workspace using the workspace-level API
|
|
5
|
+
* (`allDmConversations`). Unlike the SDK's built-in `useDMs` which only returns
|
|
6
|
+
* conversations the dashboard agent participates in, this hook returns agent-to-agent
|
|
7
|
+
* DMs as well, making them visible in the DM sidebar.
|
|
8
|
+
*
|
|
9
|
+
* Listens for `dm.received` and `group_dm.received` WebSocket events to
|
|
10
|
+
* automatically refetch when new DMs arrive.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
14
|
+
import { useRelay, useEvent } from '@relaycast/react';
|
|
15
|
+
import { useRelayConfigStatus } from '../../providers/RelayConfigProvider';
|
|
16
|
+
|
|
17
|
+
export interface AllDmConversation {
|
|
18
|
+
id: string;
|
|
19
|
+
channelId?: string;
|
|
20
|
+
type: string;
|
|
21
|
+
participants: string[];
|
|
22
|
+
lastMessage: {
|
|
23
|
+
text: string;
|
|
24
|
+
agentName: string;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
} | null;
|
|
27
|
+
messageCount: number;
|
|
28
|
+
unreadCount?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface UseAllDMsResult {
|
|
32
|
+
conversations: AllDmConversation[];
|
|
33
|
+
loading: boolean;
|
|
34
|
+
error: Error | null;
|
|
35
|
+
refetch: () => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function useAllDMs(): UseAllDMsResult {
|
|
39
|
+
const { configured } = useRelayConfigStatus();
|
|
40
|
+
const [conversations, setConversations] = useState<AllDmConversation[]>([]);
|
|
41
|
+
const [loading, setLoading] = useState(true);
|
|
42
|
+
const [error, setError] = useState<Error | null>(null);
|
|
43
|
+
const fetchingRef = useRef(false);
|
|
44
|
+
|
|
45
|
+
// useRelay() throws if not inside RelayProvider, but we always are.
|
|
46
|
+
// When relay is not configured, the provider uses dummy credentials and
|
|
47
|
+
// allDmConversations will fail — we guard with `configured`.
|
|
48
|
+
let relay: ReturnType<typeof useRelay> | null = null;
|
|
49
|
+
try {
|
|
50
|
+
relay = useRelay();
|
|
51
|
+
} catch {
|
|
52
|
+
// Not inside RelayProvider — relay stays null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const fetchConversations = useCallback(async () => {
|
|
56
|
+
if (!configured || !relay || fetchingRef.current) return;
|
|
57
|
+
fetchingRef.current = true;
|
|
58
|
+
try {
|
|
59
|
+
const data = await relay.allDmConversations();
|
|
60
|
+
setConversations(Array.isArray(data) ? data as AllDmConversation[] : []);
|
|
61
|
+
setError(null);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
64
|
+
} finally {
|
|
65
|
+
setLoading(false);
|
|
66
|
+
fetchingRef.current = false;
|
|
67
|
+
}
|
|
68
|
+
}, [configured, relay]);
|
|
69
|
+
|
|
70
|
+
// Initial fetch
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!configured) {
|
|
73
|
+
setLoading(false);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
void fetchConversations();
|
|
77
|
+
}, [configured, fetchConversations]);
|
|
78
|
+
|
|
79
|
+
// Refetch when DM events arrive via WebSocket
|
|
80
|
+
useEvent('dm.received', () => {
|
|
81
|
+
void fetchConversations();
|
|
82
|
+
});
|
|
83
|
+
useEvent('group_dm.received', () => {
|
|
84
|
+
void fetchConversations();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return { conversations, loading, error, refetch: fetchConversations };
|
|
88
|
+
}
|
|
@@ -41,10 +41,22 @@ export function useDirectMessage({
|
|
|
41
41
|
for (const msg of messages) {
|
|
42
42
|
const { from, to } = msg;
|
|
43
43
|
if (!from || !to) continue;
|
|
44
|
+
// Messages involving the selected human
|
|
44
45
|
if (from === humanName && agentNameSet.has(to)) derived.add(to);
|
|
45
46
|
if (to === humanName && agentNameSet.has(from)) derived.add(from);
|
|
46
47
|
if (selectedDmAgents.includes(from) && agentNameSet.has(to)) derived.add(to);
|
|
47
48
|
if (selectedDmAgents.includes(to) && agentNameSet.has(from)) derived.add(from);
|
|
49
|
+
// Include agents from DM messages (non-channel messages) so agent-to-agent
|
|
50
|
+
// DMs are visible in the DM view — but only if the message involves the
|
|
51
|
+
// current human or an already-selected DM agent to avoid cross-conversation leakage.
|
|
52
|
+
if (!to.startsWith('#')) {
|
|
53
|
+
const involvesHuman = (from === humanName || to === humanName);
|
|
54
|
+
const involvesSelected = selectedDmAgents.includes(from) || selectedDmAgents.includes(to);
|
|
55
|
+
if (involvesHuman || involvesSelected) {
|
|
56
|
+
if (agentNameSet.has(from)) derived.add(from);
|
|
57
|
+
if (agentNameSet.has(to)) derived.add(to);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
const participants = new Set<string>([...selectedDmAgents, ...derived]);
|
|
@@ -12,13 +12,11 @@
|
|
|
12
12
|
import React, { createContext, useContext, useState, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
13
13
|
import type { Message } from '../types';
|
|
14
14
|
import type { HumanUser } from '../components/MentionAutocomplete';
|
|
15
|
-
import {
|
|
16
|
-
useDMs as useRelayDMs,
|
|
17
|
-
} from '@relaycast/react';
|
|
18
15
|
import { useMessages as useMessagesHook } from '../components/hooks/useMessages';
|
|
19
16
|
import { useThread } from '../components/hooks/useThread';
|
|
20
17
|
import { usePresence, type UserPresence } from '../components/hooks/usePresence';
|
|
21
18
|
import { useDirectMessage } from '../components/hooks/useDirectMessage';
|
|
19
|
+
import { useAllDMs } from '../components/hooks/useAllDMs';
|
|
22
20
|
import { useCloudWorkspace } from './CloudWorkspaceProvider';
|
|
23
21
|
import { useAgentContext } from './AgentProvider';
|
|
24
22
|
import { useRelayConfigStatus } from './RelayConfigProvider';
|
|
@@ -378,14 +376,14 @@ function MessageProviderInner({ children, data, rawData: _rawData, enableReactio
|
|
|
378
376
|
// Relay DMs and message normalization
|
|
379
377
|
// ---------------------------------------------------------------------------
|
|
380
378
|
|
|
381
|
-
const
|
|
379
|
+
const allDMsState = useAllDMs();
|
|
382
380
|
const normalizedRelayMessages = useMemo(() => {
|
|
383
381
|
const sourceMessages = data?.messages ?? [];
|
|
384
|
-
if (!relayConfigured ||
|
|
382
|
+
if (!relayConfigured || allDMsState.conversations.length === 0) {
|
|
385
383
|
return sourceMessages;
|
|
386
384
|
}
|
|
387
|
-
return normalizeRelayDmMessageTargets(sourceMessages,
|
|
388
|
-
}, [data?.messages, relayConfigured,
|
|
385
|
+
return normalizeRelayDmMessageTargets(sourceMessages, allDMsState.conversations);
|
|
386
|
+
}, [data?.messages, relayConfigured, allDMsState.conversations]);
|
|
389
387
|
|
|
390
388
|
// ---------------------------------------------------------------------------
|
|
391
389
|
// Core message hook
|
|
@@ -442,7 +440,7 @@ function MessageProviderInner({ children, data, rawData: _rawData, enableReactio
|
|
|
442
440
|
const { visibleMessages: dedupedVisibleMessages, participantAgents: dmParticipantAgents } = useDirectMessage({
|
|
443
441
|
currentHuman,
|
|
444
442
|
currentUserName: currentUser?.displayName ?? null,
|
|
445
|
-
messages,
|
|
443
|
+
messages: currentHuman ? normalizedRelayMessages : messages,
|
|
446
444
|
agents,
|
|
447
445
|
selectedDmAgents,
|
|
448
446
|
removedDmAgents,
|
|
@@ -467,9 +465,9 @@ function MessageProviderInner({ children, data, rawData: _rawData, enableReactio
|
|
|
467
465
|
});
|
|
468
466
|
}
|
|
469
467
|
|
|
470
|
-
if (relayConfigured &&
|
|
468
|
+
if (relayConfigured && allDMsState.conversations.length > 0) {
|
|
471
469
|
const currentUserName = currentUser?.displayName.toLowerCase();
|
|
472
|
-
for (const conversation of
|
|
470
|
+
for (const conversation of allDMsState.conversations) {
|
|
473
471
|
for (const participant of conversation.participants) {
|
|
474
472
|
const name = getRelayDmParticipantName(participant);
|
|
475
473
|
if (!name) continue;
|
|
@@ -491,7 +489,7 @@ function MessageProviderInner({ children, data, rawData: _rawData, enableReactio
|
|
|
491
489
|
}
|
|
492
490
|
|
|
493
491
|
return Array.from(seenUsers.values());
|
|
494
|
-
}, [normalizedRelayMessages, agents, currentUser,
|
|
492
|
+
}, [normalizedRelayMessages, agents, currentUser, allDMsState.conversations, relayConfigured, relayAgentName]);
|
|
495
493
|
|
|
496
494
|
// ---------------------------------------------------------------------------
|
|
497
495
|
// Human unread counts
|
|
@@ -500,12 +498,12 @@ function MessageProviderInner({ children, data, rawData: _rawData, enableReactio
|
|
|
500
498
|
const humanUnreadCounts = useMemo(() => {
|
|
501
499
|
if (!currentUser) return {};
|
|
502
500
|
|
|
503
|
-
if (relayConfigured &&
|
|
501
|
+
if (relayConfigured && allDMsState.conversations.length > 0) {
|
|
504
502
|
const counts: Record<string, number> = {};
|
|
505
503
|
const currentUserName = currentUser.displayName.toLowerCase();
|
|
506
504
|
const agentNames = new Set(agents.filter((a) => !a.isHuman).map((a) => a.name.toLowerCase()));
|
|
507
505
|
|
|
508
|
-
for (const conversation of
|
|
506
|
+
for (const conversation of allDMsState.conversations) {
|
|
509
507
|
if (!conversation.unreadCount) continue;
|
|
510
508
|
|
|
511
509
|
const match = conversation.participants.find((p) => {
|
|
@@ -546,7 +544,7 @@ function MessageProviderInner({ children, data, rawData: _rawData, enableReactio
|
|
|
546
544
|
}
|
|
547
545
|
|
|
548
546
|
return counts;
|
|
549
|
-
}, [combinedAgents, currentUser, normalizedRelayMessages, dmSeenAt,
|
|
547
|
+
}, [combinedAgents, currentUser, normalizedRelayMessages, dmSeenAt, allDMsState.conversations, agents, relayConfigured]);
|
|
550
548
|
|
|
551
549
|
const markDmSeen = useCallback((username: string) => {
|
|
552
550
|
setDmSeenAt((prev) => {
|
|
@@ -839,16 +837,16 @@ function MessageProviderInnerWithSend({ children, data, rawData, enableReactions
|
|
|
839
837
|
// Since SendProvider only needs them for local (non-cloud) channel message rendering,
|
|
840
838
|
// we derive them here at this level too.
|
|
841
839
|
const { configured: relayConfigured } = useRelayConfigStatus();
|
|
842
|
-
const
|
|
840
|
+
const allDMsState = useAllDMs();
|
|
843
841
|
const { currentUser } = useCloudWorkspace();
|
|
844
842
|
|
|
845
843
|
const normalizedRelayMessages = useMemo(() => {
|
|
846
844
|
const sourceMessages = data?.messages ?? [];
|
|
847
|
-
if (!relayConfigured ||
|
|
845
|
+
if (!relayConfigured || allDMsState.conversations.length === 0) {
|
|
848
846
|
return sourceMessages;
|
|
849
847
|
}
|
|
850
|
-
return normalizeRelayDmMessageTargets(sourceMessages,
|
|
851
|
-
}, [data?.messages, relayConfigured,
|
|
848
|
+
return normalizeRelayDmMessageTargets(sourceMessages, allDMsState.conversations);
|
|
849
|
+
}, [data?.messages, relayConfigured, allDMsState.conversations]);
|
|
852
850
|
|
|
853
851
|
const [localUsername] = useState<string | null>(
|
|
854
852
|
typeof window !== 'undefined' ? localStorage.getItem('relay_username') : null
|