Skip to content

Conversation

@khaliqgant
Copy link
Collaborator

This implements a Slack-like messaging system where human users can:

  • Register as first-class "user" entities in the relay daemon
  • Join/leave channels for group communication
  • Send channel messages to all channel members
  • Send direct messages to other users or agents
  • Receive messages from agents and other users

Key changes:

  • Protocol: Added EntityType (agent|user), CHANNEL_* message types
  • Router: Added channel membership tracking, user entity support
  • Connection: Added entityType, displayName, avatarUrl to HELLO
  • RelayClient: Added entityType support for user connections
  • UserBridge: New bridge between dashboard WebSocket and daemon
  • Dashboard: Integrated UserBridge for real-time user messaging

All 1030 tests pass including 148 new tests for channels/users.

claude added 4 commits January 6, 2026 16:45
This implements a Slack-like messaging system where human users can:
- Register as first-class "user" entities in the relay daemon
- Join/leave channels for group communication
- Send channel messages to all channel members
- Send direct messages to other users or agents
- Receive messages from agents and other users

Key changes:
- Protocol: Added EntityType (agent|user), CHANNEL_* message types
- Router: Added channel membership tracking, user entity support
- Connection: Added entityType, displayName, avatarUrl to HELLO
- RelayClient: Added entityType support for user connections
- UserBridge: New bridge between dashboard WebSocket and daemon
- Dashboard: Integrated UserBridge for real-time user messaging

All 1030 tests pass including 148 new tests for channels/users.
- Add useChannels hook for WebSocket channel management
- Add ChannelSidebar component with Slack-like UI
- Add ChannelChat component for message display and input
- Wire UserBridge into presence WebSocket handler
- Add API endpoints for channel operations
- Export useChannels from hooks index
- Record trail for implementation
Keep both trajectory entries from HEAD and origin/main
- Prefix unused tokenExpiresAt with underscore in onboarding.ts
- Remove unused UserBridgeOptions import in user-bridge.test.ts
- Wrap case block declarations in braces in useChannels.ts
@khaliqgant khaliqgant requested a review from Copilot January 6, 2026 17:21
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a Slack-like messaging system that enables human users to become first-class entities in the relay daemon, allowing them to join channels, send channel messages, and communicate directly with AI agents and other users.

Key Changes:

  • Added EntityType ('agent' | 'user') to distinguish between AI agents and human users throughout the protocol
  • Implemented channel operations (join/leave/message) with membership tracking in the router
  • Created UserBridge to connect dashboard WebSocket users to the relay daemon as 'user' entities

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/protocol/types.ts Added EntityType type and 6 new CHANNEL_* message types to protocol
src/protocol/channels.ts New protocol types and utilities for channel/DM operations
src/wrapper/client.ts Extended RelayClient config with entityType and user display fields
src/daemon/connection.ts Added entityType tracking to connection state
src/daemon/router.ts Implemented channel membership tracking and message routing
src/dashboard-server/user-bridge.ts New bridge connecting dashboard WebSocket users to relay daemon
src/dashboard-server/server.ts Integrated UserBridge with presence WebSocket and added channel REST APIs
src/dashboard/react-components/hooks/useChannels.ts React hook for channel operations via WebSocket
src/dashboard/react-components/ChannelSidebar.tsx UI component for channel navigation
src/dashboard/react-components/ChannelChat.tsx UI component for channel messaging
src/cloud/api/onboarding.ts Renamed unused variable to follow convention
.trajectories/* Updated trajectory tracking metadata

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if (ws.readyState !== 1) return; // Not OPEN

// Determine message type from envelope
const env = envelope as { type?: string; payload?: { channel?: string; body?: string }; from?: string; to?: string };
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This complex inline type assertion makes the code harder to read and maintain. Consider extracting this into a named interface or using a more specific envelope type from the protocol imports.

Copilot uses AI. Check for mistakes.
Comment on lines +1206 to +1207
private getConnectionByName(name: string): RoutableConnection | undefined {
return this.agents.get(name) ?? this.users.get(name);
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method performs two Map lookups for every call. Consider maintaining a single unified connection map indexed by name to avoid the double lookup, or cache the result if this method is called frequently.

Copilot uses AI. Check for mistakes.
Comment on lines +271 to +276
export function createDmChannelName(...participants: string[]): string {
if (participants.length < 2) {
throw new Error('DM requires at least 2 participants');
}
const sorted = [...participants].sort();
return `dm:${sorted.join(':')}`;
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error path where participants.length < 2 lacks test coverage. Add a test case that verifies an error is thrown when createDmChannelName is called with fewer than 2 participants.

Copilot uses AI. Check for mistakes.

case 'channel_message': {
const channelMsg: ChannelMessage = {
id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Date.now() combined with Math.random() for ID generation can lead to collisions. Consider using the uuid library (already imported in other files) to generate proper UUIDs for message IDs.

Copilot uses AI. Check for mistakes.
}

function MessageBubble({ message, isOwn }: MessageBubbleProps) {
const time = new Date(message.timestamp).toLocaleTimeString([], {
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The toLocaleTimeString call doesn't specify a locale, which will use the user's system locale. Consider passing an explicit locale (e.g., 'en-US') for consistent formatting across all users, or document that this is intentional for localization.

Suggested change
const time = new Date(message.timestamp).toLocaleTimeString([], {
const time = new Date(message.timestamp).toLocaleTimeString('en-US', {

Copilot uses AI. Check for mistakes.
- Change onMessage from method to property assignment
- Use 'any' type for callback params to avoid contravariance issues
- Update mock in tests to use property instead of method
@khaliqgant khaliqgant merged commit 562d86a into main Jan 6, 2026
9 checks passed
@khaliqgant khaliqgant deleted the claude/add-user-messaging-Gvg2c branch January 6, 2026 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants