@agent-relay/dashboard 2.0.82 → 2.0.84
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/1028-da5d75e35d1420f1.js +1 -0
- package/out/_next/static/chunks/1528-78b17000a7e10bc6.js +2 -0
- package/out/_next/static/chunks/1695-4a5d33ba715e09b4.js +1 -0
- package/out/_next/static/chunks/1705-36c2180d00a4a569.js +1 -0
- package/out/_next/static/chunks/1dd3208c-e1f87c7b3dc1a820.js +1 -0
- package/out/_next/static/chunks/3663-47290254b8f6f5dd.js +1 -0
- package/out/_next/static/chunks/3677-4b225baf4801d9b9.js +73 -0
- package/out/_next/static/chunks/5118-7e8ada2df38eef07.js +1 -0
- package/out/_next/static/chunks/5888-15cbe97c90ed5fae.js +1 -0
- package/out/_next/static/chunks/6773-a45343a98df3abb5.js +1 -0
- package/out/_next/static/chunks/6940-b824612b605e79b3.js +9 -0
- package/out/_next/static/chunks/7894-f4a15249082a680d.js +1 -0
- package/out/_next/static/chunks/9175-b3617c1e5cbfed0e.js +1 -0
- package/out/_next/static/chunks/9372-1a804b8d08c7a236.js +1 -0
- package/out/_next/static/chunks/{ab6c8a12-0a58072fbb505134.js → ab6c8a12-91438a812d94ecf0.js} +1 -1
- package/out/_next/static/chunks/app/_not-found/page-8e8842f82d204726.js +1 -0
- package/out/_next/static/chunks/app/about/page-b78577a7da8fa459.js +1 -0
- package/out/_next/static/chunks/app/app/[[...slug]]/page-3dffd65b6344f53e.js +1 -0
- package/out/_next/static/chunks/app/app/onboarding/page-b89be9aa6264a5e1.js +1 -0
- package/out/_next/static/chunks/app/blog/go-to-bed-wake-up-to-a-finished-product/page-fbd00893ef69e499.js +1 -0
- package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/page-de2ea13649d0b6d3.js +1 -0
- package/out/_next/static/chunks/app/blog/page-a08e263c57a156fa.js +1 -0
- package/out/_next/static/chunks/app/careers/page-02228e1d6969b232.js +1 -0
- package/out/_next/static/chunks/app/changelog/page-1b5c1d79efc6e53a.js +1 -0
- package/out/_next/static/chunks/app/cloud/link/page-99654edffffb3af2.js +1 -0
- package/out/_next/static/chunks/app/complete-profile/page-59d146e5ddeafc5c.js +1 -0
- package/out/_next/static/chunks/app/connect-repos/page-995e16a976a6632c.js +1 -0
- package/out/_next/static/chunks/app/contact/page-273396a5ad57bcee.js +1 -0
- package/out/_next/static/chunks/app/dev/cli-tools/page-a71b80dcb2d5fc8d.js +1 -0
- package/out/_next/static/chunks/app/dev/log-viewer/page-46a6151ae1be0796.js +1 -0
- package/out/_next/static/chunks/app/docs/page-7c7cb603b24b7c40.js +1 -0
- package/out/_next/static/chunks/app/history/page-0c5cab1dab4e8886.js +1 -0
- package/out/_next/static/chunks/app/layout-96d72ba8ef8a43a0.js +1 -0
- package/out/_next/static/chunks/app/login/page-0ccbab34213df842.js +1 -0
- package/out/_next/static/chunks/app/metrics/page-8616272aeab9c8b0.js +1 -0
- package/out/_next/static/chunks/app/page-09ce10603ad9a251.js +1 -0
- package/out/_next/static/chunks/app/pricing/page-91c975079120c941.js +1 -0
- package/out/_next/static/chunks/app/privacy/{page-c21d51ac2dee3a88.js → page-a49ab271cc686644.js} +1 -1
- package/out/_next/static/chunks/app/providers/{page-59114505f4353512.js → page-d775d6eb5bc29e96.js} +1 -1
- package/out/_next/static/chunks/app/providers/setup/[provider]/page-ec4ef3cd80de807e.js +1 -0
- package/out/_next/static/chunks/app/security/page-d9da9bd9191e8f95.js +1 -0
- package/out/_next/static/chunks/app/signup/page-930eca0bf5fd299d.js +1 -0
- package/out/_next/static/chunks/app/terms/page-3e4827620b98613c.js +1 -0
- package/out/_next/static/chunks/framework-648e1ae7da590300.js +1 -0
- package/out/_next/static/chunks/{main-acb1b24265295d6a.js → main-2b1990080c292d92.js} +1 -1
- package/out/_next/static/chunks/main-app-9f6b7ff9e754a8f5.js +1 -0
- package/out/_next/static/chunks/pages/_app-a077b72e02273ab1.js +1 -0
- package/out/_next/static/chunks/pages/_error-84001666436a04e4.js +1 -0
- package/out/_next/static/chunks/{webpack-dd93b81e2659669c.js → webpack-7586035f1585f2db.js} +1 -1
- package/out/_next/static/css/eb9fc69d1e3d2bed.css +1 -0
- package/out/_next/static/{IxfA6RZu4trcsEMYlkQra → g3G0LMdB7lxcrU5mdM54m}/_buildManifest.js +1 -1
- package/out/about.html +2 -2
- package/out/about.txt +2 -2
- package/out/app/onboarding.html +1 -1
- package/out/app/onboarding.txt +2 -2
- 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 +3 -3
- 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 +2 -2
- package/out/blog/let-them-cook-multi-agent-orchestration.txt +2 -2
- package/out/blog.html +2 -2
- package/out/blog.txt +1 -1
- package/out/careers.html +2 -2
- package/out/careers.txt +2 -2
- package/out/changelog.html +2 -2
- package/out/changelog.txt +2 -2
- package/out/cloud/link.html +1 -1
- package/out/cloud/link.txt +2 -2
- package/out/complete-profile.html +2 -2
- package/out/complete-profile.txt +2 -2
- package/out/connect-repos.html +1 -1
- package/out/connect-repos.txt +2 -2
- package/out/contact.html +2 -2
- package/out/contact.txt +2 -2
- package/out/dev/cli-tools.html +1 -0
- package/out/dev/cli-tools.txt +7 -0
- package/out/dev/log-viewer.html +23 -0
- package/out/dev/log-viewer.txt +7 -0
- package/out/docs.html +2 -2
- package/out/docs.txt +2 -2
- package/out/history.html +1 -1
- package/out/history.txt +2 -2
- package/out/index.html +1 -1
- package/out/index.txt +2 -2
- package/out/login.html +2 -2
- package/out/login.txt +2 -2
- package/out/metrics.html +1 -1
- package/out/metrics.txt +2 -2
- package/out/pricing.html +2 -2
- package/out/pricing.txt +2 -2
- package/out/privacy.html +2 -2
- package/out/privacy.txt +2 -2
- package/out/providers/setup/claude.html +1 -1
- package/out/providers/setup/claude.txt +2 -2
- package/out/providers/setup/codex.html +1 -1
- package/out/providers/setup/codex.txt +2 -2
- package/out/providers/setup/cursor.html +1 -1
- package/out/providers/setup/cursor.txt +2 -2
- package/out/providers.html +1 -1
- package/out/providers.txt +2 -2
- package/out/security.html +2 -2
- package/out/security.txt +2 -2
- package/out/signup.html +2 -2
- package/out/signup.txt +2 -2
- package/out/terms.html +2 -2
- package/out/terms.txt +2 -2
- package/package.json +5 -1
- package/src/adapters/DashboardConfigProvider.tsx +56 -0
- package/src/adapters/cloudFetchAdapter.ts +278 -0
- package/src/adapters/index.ts +3 -0
- package/src/adapters/types.ts +508 -0
- package/src/app/app/[[...slug]]/DashboardPageClient.tsx +67 -18
- package/src/app/app/onboarding/page.tsx +870 -170
- package/src/app/cloud/link/page.tsx +14 -6
- package/src/app/connect-repos/page.tsx +9 -3
- package/src/app/dev/cli-tools/page.tsx +130 -0
- package/src/app/dev/log-viewer/MockLogViewer.tsx +132 -0
- package/src/app/dev/log-viewer/fixtures.ts +110 -0
- package/src/app/dev/log-viewer/page.tsx +288 -0
- package/src/app/history/page.tsx +28 -12
- package/src/app/page.tsx +1 -1
- package/src/app/providers/setup/[provider]/ProviderSetupClient.tsx +209 -59
- package/src/components/AgentCard.tsx +4 -4
- package/src/components/AgentLogPreview.tsx +2 -38
- package/src/components/App.tsx +441 -2624
- package/src/components/CliToolHarness.test.tsx +83 -0
- package/src/components/CliToolHarness.tsx +292 -0
- package/src/components/CoordinatorPanel.tsx +13 -6
- package/src/components/LogViewer.tsx +2 -42
- package/src/components/ProviderAuthFlow.tsx +201 -81
- package/src/components/ProvisioningProgress.tsx +1 -1
- package/src/components/ReactionChips.tsx +2 -1
- package/src/components/SpawnModal.test.tsx +51 -18
- package/src/components/SpawnModal.tsx +175 -207
- package/src/components/TerminalProviderSetup.tsx +1 -1
- package/src/components/ThreadPanel.tsx +2 -0
- package/src/components/WorkspaceContext.tsx +7 -19
- package/src/components/XTermLogViewer.tsx +190 -27
- package/src/components/channels/ChannelMessageList.tsx +94 -4
- package/src/components/channels/ChannelViewV1.tsx +35 -11
- package/src/components/channels/api.ts +21 -20
- package/src/components/channels/types.ts +16 -0
- package/src/components/hooks/index.ts +0 -19
- package/src/components/hooks/useMessages.test.ts +80 -0
- package/src/components/hooks/useMessages.ts +13 -4
- package/src/components/hooks/useOrchestrator.ts +1 -1
- package/src/components/hooks/usePresence.ts +45 -6
- package/src/components/hooks/useThread.ts +83 -46
- package/src/components/hooks/useTrajectory.ts +62 -5
- package/src/components/hooks/useWebSocket.test.ts +358 -0
- package/src/components/hooks/useWebSocket.ts +243 -5
- package/src/components/index.ts +2 -14
- package/src/components/layout/Header.tsx +9 -15
- package/src/components/layout/Sidebar.tsx +1 -8
- package/src/components/settings/SettingsPage.tsx +108 -47
- package/src/components/settings/index.ts +0 -3
- package/src/landing/blogData.ts +1 -1
- package/src/lib/agent-merge.test.ts +2 -2
- package/src/lib/api.ts +8 -38
- package/src/lib/identity.test.ts +139 -0
- package/src/lib/identity.ts +48 -0
- package/src/lib/relaycastMessageAdapters.test.ts +182 -0
- package/src/lib/relaycastMessageAdapters.ts +105 -0
- package/src/lib/sanitize-logs.test.ts +227 -0
- package/src/lib/sanitize-logs.ts +202 -0
- package/src/providers/AgentProvider.tsx +799 -0
- package/src/providers/ChannelProvider.tsx +528 -0
- package/src/providers/CloudWorkspaceProvider.tsx +402 -0
- package/src/providers/MessageProvider.tsx +875 -0
- package/src/providers/RelayConfigProvider.tsx +94 -0
- package/src/providers/SendProvider.tsx +497 -0
- package/src/providers/SettingsProvider.tsx +247 -0
- package/src/providers/index.ts +26 -0
- package/src/types/index.ts +10 -10
- package/out/_next/static/chunks/11-9a2993a37266dcb3.js +0 -9
- package/out/_next/static/chunks/118-ae2b650136a5a5fc.js +0 -1
- package/out/_next/static/chunks/1dd3208c-40ab0fc0f60392b8.js +0 -1
- package/out/_next/static/chunks/202-fc0763dd7488e58f.js +0 -1
- package/out/_next/static/chunks/259-83b77fa1b91ba5aa.js +0 -1
- package/out/_next/static/chunks/407-0c82986cf79c8ecb.js +0 -1
- package/out/_next/static/chunks/528-f5f676996d613c25.js +0 -2
- package/out/_next/static/chunks/663-ddb04081febc3678.js +0 -1
- package/out/_next/static/chunks/687-88b6b139a6bb0e2e.js +0 -1
- package/out/_next/static/chunks/695-51d25b1988644374.js +0 -1
- package/out/_next/static/chunks/773-54a2641043c81e55.js +0 -1
- package/out/_next/static/chunks/app/_not-found/page-6da9b72091e5b511.js +0 -1
- package/out/_next/static/chunks/app/about/page-fff7c6457683f243.js +0 -1
- package/out/_next/static/chunks/app/app/[[...slug]]/page-f7eca1b66fb4249b.js +0 -1
- package/out/_next/static/chunks/app/app/onboarding/page-129abc5da2e67971.js +0 -1
- package/out/_next/static/chunks/app/blog/go-to-bed-wake-up-to-a-finished-product/page-5d5f28fd126b692f.js +0 -1
- package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/page-b194f207fbd91862.js +0 -1
- package/out/_next/static/chunks/app/blog/page-b9bd9d8703fca76a.js +0 -1
- package/out/_next/static/chunks/app/careers/page-a4bd8d5f4de8f4eb.js +0 -1
- package/out/_next/static/chunks/app/changelog/page-9a1f6ad1743d63c5.js +0 -1
- package/out/_next/static/chunks/app/cloud/link/page-0844c5699b027c3b.js +0 -1
- package/out/_next/static/chunks/app/complete-profile/page-39ed5a67916beb87.js +0 -1
- package/out/_next/static/chunks/app/connect-repos/page-297eddee0c39f2a3.js +0 -1
- package/out/_next/static/chunks/app/contact/page-3c1dd8690217fade.js +0 -1
- package/out/_next/static/chunks/app/docs/page-1875e981f2c3fd13.js +0 -1
- package/out/_next/static/chunks/app/history/page-2d5c5695c9e8b40c.js +0 -1
- package/out/_next/static/chunks/app/layout-0a4b99656da25511.js +0 -1
- package/out/_next/static/chunks/app/login/page-f69c076f5a6fc520.js +0 -1
- package/out/_next/static/chunks/app/metrics/page-bebbee055669a17e.js +0 -1
- package/out/_next/static/chunks/app/page-0ee604f7070d14c0.js +0 -1
- package/out/_next/static/chunks/app/pricing/page-eeae7d594af333b6.js +0 -1
- package/out/_next/static/chunks/app/providers/setup/[provider]/page-daf9b3e05e77ae19.js +0 -1
- package/out/_next/static/chunks/app/security/page-cd562730fe84a0a2.js +0 -1
- package/out/_next/static/chunks/app/signup/page-c242ca08101a84ff.js +0 -1
- package/out/_next/static/chunks/app/terms/page-c7001720e7941dc6.js +0 -1
- package/out/_next/static/chunks/framework-3664cab31236a9fa.js +0 -1
- package/out/_next/static/chunks/main-app-7f73a939a312a228.js +0 -1
- package/out/_next/static/chunks/pages/_app-10a93ab5b7c32eb3.js +0 -1
- package/out/_next/static/chunks/pages/_error-2d792b2a41857be4.js +0 -1
- package/out/_next/static/css/8968d98ed4c4d33f.css +0 -1
- package/src/components/BillingResult.tsx +0 -447
- package/src/components/CloudSessionProvider.tsx +0 -130
- package/src/components/SessionExpiredModal.tsx +0 -128
- package/src/components/WorkspaceStatusIndicator.tsx +0 -396
- package/src/components/hooks/useSession.ts +0 -209
- package/src/components/hooks/useWorkspaceMembers.ts +0 -132
- package/src/components/hooks/useWorkspaceStatus.ts +0 -237
- package/src/components/settings/BillingSettingsPanel.tsx +0 -564
- package/src/components/settings/TeamSettingsPanel.tsx +0 -560
- package/src/components/settings/WorkspaceSettingsPanel.tsx +0 -1368
- package/src/lib/cloudApi.ts +0 -893
- /package/out/_next/static/{IxfA6RZu4trcsEMYlkQra → g3G0LMdB7lxcrU5mdM54m}/_ssgManifest.js +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Channels API Service
|
|
3
3
|
*
|
|
4
|
-
* Channels are handled entirely by the
|
|
5
|
-
* Real-time messaging uses the
|
|
4
|
+
* Channels are handled entirely by the broker (not cloud).
|
|
5
|
+
* Real-time messaging uses the broker's CHANNEL_* protocol while the HTTP API now reads from broker storage.
|
|
6
6
|
*
|
|
7
7
|
* Cloud channels were removed because:
|
|
8
|
-
* -
|
|
8
|
+
* - Broker already has full channel protocol support (CHANNEL_JOIN, CHANNEL_MESSAGE, etc.)
|
|
9
9
|
* - Having two parallel implementations caused confusion
|
|
10
10
|
* - See trajectory traj_fnmapojrllau for architectural decision
|
|
11
11
|
*/
|
|
@@ -50,7 +50,7 @@ export class ApiError extends Error {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// =============================================================================
|
|
53
|
-
// Channel API Functions -
|
|
53
|
+
// Channel API Functions - broker-backed with minimal placeholders
|
|
54
54
|
// =============================================================================
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -96,7 +96,7 @@ export async function getChannel(
|
|
|
96
96
|
_workspaceId: string,
|
|
97
97
|
channelId: string
|
|
98
98
|
): Promise<GetChannelResponse> {
|
|
99
|
-
// Minimal channel details until
|
|
99
|
+
// Minimal channel details until broker exposes metadata
|
|
100
100
|
return {
|
|
101
101
|
channel: {
|
|
102
102
|
id: channelId,
|
|
@@ -229,7 +229,7 @@ export async function createChannel(
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
/**
|
|
232
|
-
* Send a message to a channel via
|
|
232
|
+
* Send a message to a channel via broker API
|
|
233
233
|
*/
|
|
234
234
|
export async function sendMessage(
|
|
235
235
|
workspaceId: string,
|
|
@@ -249,6 +249,7 @@ export async function sendMessage(
|
|
|
249
249
|
channel: channelId,
|
|
250
250
|
body: request.content,
|
|
251
251
|
thread: request.threadId,
|
|
252
|
+
attachmentIds: request.attachmentIds,
|
|
252
253
|
workspaceId,
|
|
253
254
|
}),
|
|
254
255
|
});
|
|
@@ -279,7 +280,7 @@ export async function sendMessage(
|
|
|
279
280
|
}
|
|
280
281
|
|
|
281
282
|
/**
|
|
282
|
-
* Join a channel via
|
|
283
|
+
* Join a channel via broker API
|
|
283
284
|
*/
|
|
284
285
|
export async function joinChannel(
|
|
285
286
|
workspaceId: string,
|
|
@@ -320,7 +321,7 @@ export async function joinChannel(
|
|
|
320
321
|
}
|
|
321
322
|
|
|
322
323
|
/**
|
|
323
|
-
* Leave a channel via
|
|
324
|
+
* Leave a channel via broker API
|
|
324
325
|
*/
|
|
325
326
|
export async function leaveChannel(
|
|
326
327
|
workspaceId: string,
|
|
@@ -416,7 +417,7 @@ export async function deleteChannel(
|
|
|
416
417
|
_workspaceId: string,
|
|
417
418
|
_channelId: string
|
|
418
419
|
): Promise<void> {
|
|
419
|
-
//
|
|
420
|
+
// Broker deletes automatically when empty; nothing to do client-side
|
|
420
421
|
return;
|
|
421
422
|
}
|
|
422
423
|
|
|
@@ -428,31 +429,31 @@ export async function markRead(
|
|
|
428
429
|
_channelId: string,
|
|
429
430
|
_upToMessageId?: string
|
|
430
431
|
): Promise<void> {
|
|
431
|
-
// TODO: add mark-read to
|
|
432
|
+
// TODO: add mark-read to broker; no-op for now
|
|
432
433
|
return;
|
|
433
434
|
}
|
|
434
435
|
|
|
435
436
|
/**
|
|
436
|
-
* Pin a message (no-op in
|
|
437
|
+
* Pin a message (no-op in broker mode)
|
|
437
438
|
*/
|
|
438
439
|
export async function pinMessage(
|
|
439
440
|
_workspaceId: string,
|
|
440
441
|
_channelId: string,
|
|
441
442
|
_messageId: string
|
|
442
443
|
): Promise<void> {
|
|
443
|
-
// Pinning not supported in
|
|
444
|
+
// Pinning not supported in broker mode
|
|
444
445
|
return;
|
|
445
446
|
}
|
|
446
447
|
|
|
447
448
|
/**
|
|
448
|
-
* Unpin a message (no-op in
|
|
449
|
+
* Unpin a message (no-op in broker mode)
|
|
449
450
|
*/
|
|
450
451
|
export async function unpinMessage(
|
|
451
452
|
_workspaceId: string,
|
|
452
453
|
_channelId: string,
|
|
453
454
|
_messageId: string
|
|
454
455
|
): Promise<void> {
|
|
455
|
-
// Unpinning not supported in
|
|
456
|
+
// Unpinning not supported in broker mode
|
|
456
457
|
return;
|
|
457
458
|
}
|
|
458
459
|
|
|
@@ -478,7 +479,7 @@ export interface AvailableMember {
|
|
|
478
479
|
|
|
479
480
|
/**
|
|
480
481
|
* Get available members for channel invites
|
|
481
|
-
* Returns workspace members (humans) and agents from linked
|
|
482
|
+
* Returns workspace members (humans) and agents from linked brokers
|
|
482
483
|
*/
|
|
483
484
|
export async function getAvailableMembers(
|
|
484
485
|
workspaceId?: string
|
|
@@ -519,14 +520,14 @@ export async function getAvailableMembers(
|
|
|
519
520
|
// =============================================================================
|
|
520
521
|
|
|
521
522
|
/**
|
|
522
|
-
* Search messages (returns empty in
|
|
523
|
+
* Search messages (returns empty in broker mode - search via relay)
|
|
523
524
|
*/
|
|
524
525
|
export async function searchMessages(
|
|
525
526
|
_workspaceId: string,
|
|
526
527
|
query: string,
|
|
527
528
|
_options?: { channelId?: string; limit?: number; offset?: number }
|
|
528
529
|
): Promise<SearchResponse> {
|
|
529
|
-
// Search not implemented in
|
|
530
|
+
// Search not implemented in broker mode
|
|
530
531
|
return {
|
|
531
532
|
results: [],
|
|
532
533
|
total: 0,
|
|
@@ -685,17 +686,17 @@ export async function getChannelMembers(
|
|
|
685
686
|
// =============================================================================
|
|
686
687
|
|
|
687
688
|
/**
|
|
688
|
-
* Always returns true - channels now only use
|
|
689
|
+
* Always returns true - channels now only use broker/relay
|
|
689
690
|
*/
|
|
690
691
|
export function isRealApiEnabled(): boolean {
|
|
691
692
|
return true;
|
|
692
693
|
}
|
|
693
694
|
|
|
694
695
|
/**
|
|
695
|
-
* No-op - API mode is fixed to
|
|
696
|
+
* No-op - API mode is fixed to broker/local
|
|
696
697
|
*/
|
|
697
698
|
export function setApiMode(_useReal: boolean): void {
|
|
698
|
-
console.log('[ChannelsAPI] Mode is fixed to
|
|
699
|
+
console.log('[ChannelsAPI] Mode is fixed to broker-based implementation');
|
|
699
700
|
}
|
|
700
701
|
|
|
701
702
|
export function getApiMode(): 'real' | 'mock' {
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { ThreadMetadata } from '../../types/threading';
|
|
9
|
+
import type { Agent } from '../../types';
|
|
10
|
+
import type { HumanUser } from '../MentionAutocomplete';
|
|
11
|
+
import type { UserPresence } from '../hooks/usePresence';
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* Channel visibility types.
|
|
@@ -296,6 +299,17 @@ export interface ChannelMessageListProps {
|
|
|
296
299
|
unreadState?: UnreadState;
|
|
297
300
|
/** Current user name */
|
|
298
301
|
currentUser: string;
|
|
302
|
+
/** Current user info (for avatar fallback) */
|
|
303
|
+
currentUserInfo?: {
|
|
304
|
+
displayName: string;
|
|
305
|
+
avatarUrl?: string;
|
|
306
|
+
};
|
|
307
|
+
/** Online users for avatar fallback */
|
|
308
|
+
onlineUsers?: UserPresence[];
|
|
309
|
+
/** Agents for avatar fallback */
|
|
310
|
+
agents?: Agent[];
|
|
311
|
+
/** Known human users for avatar fallback */
|
|
312
|
+
humanUsers?: HumanUser[];
|
|
299
313
|
/** Whether loading more messages */
|
|
300
314
|
isLoadingMore?: boolean;
|
|
301
315
|
/** Whether there are more messages to load */
|
|
@@ -306,6 +320,8 @@ export interface ChannelMessageListProps {
|
|
|
306
320
|
onThreadClick?: (messageId: string) => void;
|
|
307
321
|
/** Callback when clicking on a member name (for DM navigation) */
|
|
308
322
|
onMemberClick?: (memberId: string, entityType: 'user' | 'agent') => void;
|
|
323
|
+
/** Callback when toggling a reaction on a message */
|
|
324
|
+
onReaction?: (messageId: string, emoji: string, hasReacted: boolean) => void;
|
|
309
325
|
}
|
|
310
326
|
|
|
311
327
|
/**
|
|
@@ -13,13 +13,6 @@ export {
|
|
|
13
13
|
type OrchestratorEvent,
|
|
14
14
|
} from './useOrchestrator';
|
|
15
15
|
export { useAgentLogs, type UseAgentLogsOptions, type UseAgentLogsReturn, type LogLine, type LogConnectionState } from './useAgentLogs';
|
|
16
|
-
export {
|
|
17
|
-
useSession,
|
|
18
|
-
type UseSessionOptions,
|
|
19
|
-
type UseSessionReturn,
|
|
20
|
-
type SessionError,
|
|
21
|
-
type CloudUser,
|
|
22
|
-
} from './useSession';
|
|
23
16
|
export { useTrajectory } from './useTrajectory';
|
|
24
17
|
export {
|
|
25
18
|
useRecentRepos,
|
|
@@ -27,12 +20,6 @@ export {
|
|
|
27
20
|
type UseRecentReposReturn,
|
|
28
21
|
type RecentRepo,
|
|
29
22
|
} from './useRecentRepos';
|
|
30
|
-
export {
|
|
31
|
-
useWorkspaceStatus,
|
|
32
|
-
type UseWorkspaceStatusOptions,
|
|
33
|
-
type UseWorkspaceStatusReturn,
|
|
34
|
-
type WorkspaceStatus,
|
|
35
|
-
} from './useWorkspaceStatus';
|
|
36
23
|
export {
|
|
37
24
|
useWorkspaceRepos,
|
|
38
25
|
type UseWorkspaceReposOptions,
|
|
@@ -46,12 +33,6 @@ export {
|
|
|
46
33
|
type ChannelMessage,
|
|
47
34
|
type ChannelConnectionState,
|
|
48
35
|
} from './useChannels';
|
|
49
|
-
export {
|
|
50
|
-
useWorkspaceMembers,
|
|
51
|
-
filterOnlineUsersByWorkspace,
|
|
52
|
-
type UseWorkspaceMembersOptions,
|
|
53
|
-
type UseWorkspaceMembersReturn,
|
|
54
|
-
} from './useWorkspaceMembers';
|
|
55
36
|
export {
|
|
56
37
|
usePinnedAgents,
|
|
57
38
|
type UsePinnedAgentsReturn,
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import { renderHook, act } from '@testing-library/react';
|
|
4
|
+
import { useMessages } from './useMessages';
|
|
5
|
+
|
|
6
|
+
const mockSendMessage = vi.fn();
|
|
7
|
+
|
|
8
|
+
vi.mock('../../lib/api', () => ({
|
|
9
|
+
api: {
|
|
10
|
+
sendMessage: (...args: unknown[]) => mockSendMessage(...args),
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
describe('useMessages', () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
mockSendMessage.mockReset();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('keeps optimistic messages in sending status after successful send', async () => {
|
|
20
|
+
mockSendMessage.mockResolvedValue({ success: true });
|
|
21
|
+
const baseMessages: Parameters<typeof useMessages>[0]['messages'] = [];
|
|
22
|
+
|
|
23
|
+
const { result } = renderHook(() =>
|
|
24
|
+
useMessages({
|
|
25
|
+
messages: baseMessages,
|
|
26
|
+
senderName: 'Dashboard',
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
let ok = false;
|
|
31
|
+
await act(async () => {
|
|
32
|
+
ok = await result.current.sendMessage('Lead', 'hello world');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(ok).toBe(true);
|
|
36
|
+
expect(result.current.messages).toHaveLength(1);
|
|
37
|
+
expect(result.current.messages[0]?.status).toBe('sending');
|
|
38
|
+
expect(result.current.messages[0]?.to).toBe('Lead');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('uses canonical message id from send response when available', async () => {
|
|
42
|
+
mockSendMessage.mockResolvedValue({ success: true, data: { messageId: 'evt_123' } });
|
|
43
|
+
const baseMessages: Parameters<typeof useMessages>[0]['messages'] = [];
|
|
44
|
+
|
|
45
|
+
const { result } = renderHook(() =>
|
|
46
|
+
useMessages({
|
|
47
|
+
messages: baseMessages,
|
|
48
|
+
senderName: 'Dashboard',
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
await act(async () => {
|
|
53
|
+
await result.current.sendMessage('Lead', 'hello world');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(result.current.messages[0]?.id).toBe('evt_123');
|
|
57
|
+
expect(result.current.messages[0]?.status).toBe('sending');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('removes optimistic message and surfaces error on failed send', async () => {
|
|
61
|
+
mockSendMessage.mockResolvedValue({ success: false, error: 'send failed' });
|
|
62
|
+
const baseMessages: Parameters<typeof useMessages>[0]['messages'] = [];
|
|
63
|
+
|
|
64
|
+
const { result } = renderHook(() =>
|
|
65
|
+
useMessages({
|
|
66
|
+
messages: baseMessages,
|
|
67
|
+
senderName: 'Dashboard',
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
let ok = true;
|
|
72
|
+
await act(async () => {
|
|
73
|
+
ok = await result.current.sendMessage('Lead', 'hello world');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(ok).toBe(false);
|
|
77
|
+
expect(result.current.messages).toHaveLength(0);
|
|
78
|
+
expect(result.current.sendError).toBe('send failed');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -64,8 +64,11 @@ export function useMessages({
|
|
|
64
64
|
// This allows us to show new messages that arrive after viewing
|
|
65
65
|
const [seenThreads, setSeenThreads] = useState<Map<string, number>>(new Map());
|
|
66
66
|
|
|
67
|
-
// Effective sender name for the current user (used for filtering own messages)
|
|
68
|
-
|
|
67
|
+
// Effective sender name for the current user (used for filtering own messages).
|
|
68
|
+
// Read localStorage directly as a fallback so we never display "Dashboard".
|
|
69
|
+
const effectiveSenderName = senderName
|
|
70
|
+
|| (typeof window !== 'undefined' ? localStorage.getItem('relay_username') : null)
|
|
71
|
+
|| 'You';
|
|
69
72
|
|
|
70
73
|
// Optimistic messages: shown immediately before server confirms
|
|
71
74
|
// These have status='sending' and a temp ID prefixed with 'optimistic-'
|
|
@@ -272,8 +275,14 @@ export function useMessages({
|
|
|
272
275
|
const result = await api.sendMessage(request);
|
|
273
276
|
|
|
274
277
|
if (result.success) {
|
|
275
|
-
//
|
|
276
|
-
// the real
|
|
278
|
+
// If the server returned a canonical message ID, update the optimistic
|
|
279
|
+
// message so dedup logic can match it when the real event arrives.
|
|
280
|
+
const canonicalId = result.data?.messageId;
|
|
281
|
+
if (canonicalId) {
|
|
282
|
+
setOptimisticMessages((prev) =>
|
|
283
|
+
prev.map((m) => (m.id === optimisticId ? { ...m, id: canonicalId } : m)),
|
|
284
|
+
);
|
|
285
|
+
}
|
|
277
286
|
return true;
|
|
278
287
|
}
|
|
279
288
|
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* - Handles user presence announcements
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
10
|
+
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
|
11
11
|
import { getWebSocketUrl } from '../../lib/config';
|
|
12
|
+
import { usePresence as useRelayPresence } from '@relaycast/react';
|
|
13
|
+
import { useRelayConfigStatus } from '../../providers/RelayConfigProvider';
|
|
12
14
|
|
|
13
15
|
/** User presence information */
|
|
14
16
|
export interface UserPresence {
|
|
@@ -75,6 +77,9 @@ function getPresenceUrl(): string {
|
|
|
75
77
|
|
|
76
78
|
export function usePresence(options: UsePresenceOptions = {}): UsePresenceReturn {
|
|
77
79
|
const { currentUser, wsUrl, autoConnect = true, onEvent, workspaceId } = options;
|
|
80
|
+
const relayPresenceState = useRelayPresence();
|
|
81
|
+
const { configured: relayConfigured } = useRelayConfigStatus();
|
|
82
|
+
const usingRelayPresence = relayConfigured;
|
|
78
83
|
|
|
79
84
|
const [onlineUsers, setOnlineUsers] = useState<UserPresence[]>([]);
|
|
80
85
|
const [typingUsers, setTypingUsers] = useState<TypingIndicator[]>([]);
|
|
@@ -94,6 +99,16 @@ export function usePresence(options: UsePresenceOptions = {}): UsePresenceReturn
|
|
|
94
99
|
const onEventRef = useRef(onEvent);
|
|
95
100
|
onEventRef.current = onEvent; // Keep ref in sync with callback prop
|
|
96
101
|
|
|
102
|
+
const relayOnlineUsers = useMemo<UserPresence[]>(() => {
|
|
103
|
+
return relayPresenceState.agents
|
|
104
|
+
.filter((agent) => agent.status === 'online' && agent.type === 'human')
|
|
105
|
+
.map((agent) => ({
|
|
106
|
+
username: agent.name,
|
|
107
|
+
connectedAt: agent.createdAt ?? agent.lastSeen,
|
|
108
|
+
lastSeen: agent.lastSeen,
|
|
109
|
+
}));
|
|
110
|
+
}, [relayPresenceState]);
|
|
111
|
+
|
|
97
112
|
// Clear stale typing indicators (after 3 seconds of no update)
|
|
98
113
|
useEffect(() => {
|
|
99
114
|
const interval = setInterval(() => {
|
|
@@ -107,6 +122,7 @@ export function usePresence(options: UsePresenceOptions = {}): UsePresenceReturn
|
|
|
107
122
|
}, []);
|
|
108
123
|
|
|
109
124
|
const connect = useCallback(() => {
|
|
125
|
+
if (usingRelayPresence) return;
|
|
110
126
|
const user = currentUserRef.current;
|
|
111
127
|
if (!user) return; // Don't connect without user info
|
|
112
128
|
if (wsRef.current?.readyState === WebSocket.OPEN) return;
|
|
@@ -251,7 +267,7 @@ export function usePresence(options: UsePresenceOptions = {}): UsePresenceReturn
|
|
|
251
267
|
} catch (e) {
|
|
252
268
|
console.error('[usePresence] Failed to create WebSocket:', e);
|
|
253
269
|
}
|
|
254
|
-
}, [wsUrl]); // Use
|
|
270
|
+
}, [wsUrl, usingRelayPresence]); // Use refs for currentUser/workspace/onEvent to avoid dependencies
|
|
255
271
|
|
|
256
272
|
const disconnect = useCallback(() => {
|
|
257
273
|
// Clear reconnect timeout first
|
|
@@ -288,6 +304,7 @@ export function usePresence(options: UsePresenceOptions = {}): UsePresenceReturn
|
|
|
288
304
|
|
|
289
305
|
// Send typing indicator
|
|
290
306
|
const sendTyping = useCallback((isTyping: boolean) => {
|
|
307
|
+
if (usingRelayPresence) return;
|
|
291
308
|
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return;
|
|
292
309
|
const user = currentUserRef.current;
|
|
293
310
|
if (!user) return;
|
|
@@ -312,10 +329,11 @@ export function usePresence(options: UsePresenceOptions = {}): UsePresenceReturn
|
|
|
312
329
|
sendTyping(false);
|
|
313
330
|
}, 3000);
|
|
314
331
|
}
|
|
315
|
-
}, []); // Use ref for currentUser to avoid dependency
|
|
332
|
+
}, [usingRelayPresence]); // Use ref for currentUser to avoid dependency
|
|
316
333
|
|
|
317
334
|
// Connect when user is available
|
|
318
335
|
useEffect(() => {
|
|
336
|
+
if (usingRelayPresence) return;
|
|
319
337
|
if (!autoConnect || !currentUserRef.current) return;
|
|
320
338
|
|
|
321
339
|
// Prevent connecting if already connected or connecting
|
|
@@ -329,10 +347,11 @@ export function usePresence(options: UsePresenceOptions = {}): UsePresenceReturn
|
|
|
329
347
|
disconnect();
|
|
330
348
|
};
|
|
331
349
|
// Callbacks are now stable (use refs internally), so only need to depend on user identity
|
|
332
|
-
}, [autoConnect, currentUser?.username, connect, disconnect]);
|
|
350
|
+
}, [autoConnect, currentUser?.username, connect, disconnect, usingRelayPresence]);
|
|
333
351
|
|
|
334
352
|
// Send leave on page unload
|
|
335
353
|
useEffect(() => {
|
|
354
|
+
if (usingRelayPresence) return;
|
|
336
355
|
const handleUnload = () => {
|
|
337
356
|
const user = currentUserRef.current;
|
|
338
357
|
if (wsRef.current?.readyState === WebSocket.OPEN && user) {
|
|
@@ -346,10 +365,11 @@ export function usePresence(options: UsePresenceOptions = {}): UsePresenceReturn
|
|
|
346
365
|
|
|
347
366
|
window.addEventListener('beforeunload', handleUnload);
|
|
348
367
|
return () => window.removeEventListener('beforeunload', handleUnload);
|
|
349
|
-
}, []); // Use ref for currentUser to avoid dependency
|
|
368
|
+
}, [usingRelayPresence]); // Use ref for currentUser to avoid dependency
|
|
350
369
|
|
|
351
370
|
// Visibility change listener: reconnect when tab becomes visible
|
|
352
371
|
useEffect(() => {
|
|
372
|
+
if (usingRelayPresence) return;
|
|
353
373
|
const handleVisibilityChange = () => {
|
|
354
374
|
if (document.visibilityState === 'visible') {
|
|
355
375
|
// Check if connection is dead and reconnect
|
|
@@ -365,7 +385,26 @@ export function usePresence(options: UsePresenceOptions = {}): UsePresenceReturn
|
|
|
365
385
|
return () => {
|
|
366
386
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
367
387
|
};
|
|
368
|
-
}, [connect]);
|
|
388
|
+
}, [connect, usingRelayPresence]);
|
|
389
|
+
|
|
390
|
+
// If Relay SDK presence is active, ensure any legacy presence socket is closed.
|
|
391
|
+
useEffect(() => {
|
|
392
|
+
if (!usingRelayPresence) return;
|
|
393
|
+
disconnect();
|
|
394
|
+
}, [disconnect, usingRelayPresence]);
|
|
395
|
+
|
|
396
|
+
if (usingRelayPresence) {
|
|
397
|
+
const relayConnected = !relayPresenceState.loading && !relayPresenceState.error;
|
|
398
|
+
return {
|
|
399
|
+
onlineUsers: relayOnlineUsers,
|
|
400
|
+
typingUsers: [],
|
|
401
|
+
sendTyping: () => undefined,
|
|
402
|
+
isConnected: relayConnected,
|
|
403
|
+
connectionState: relayPresenceState.loading
|
|
404
|
+
? 'reconnecting'
|
|
405
|
+
: (relayPresenceState.error ? 'disconnected' : 'connected'),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
369
408
|
|
|
370
409
|
return {
|
|
371
410
|
onlineUsers,
|