-
-
+
+
- You
+ {displayName}
{timestamp && (
{timestamp}
)}
diff --git a/workspace/frontend/components/chat/chat-view.tsx b/workspace/frontend/components/chat/chat-view.tsx
index c9053464d..c5fe9cb10 100644
--- a/workspace/frontend/components/chat/chat-view.tsx
+++ b/workspace/frontend/components/chat/chat-view.tsx
@@ -85,7 +85,7 @@ async function refreshCachedSession(sessionId: string): Promise {
}
export function ChatView() {
- const { agents, currentSessionId, sessions, updateLastMessage, setSessionActive, agentModes, updateAgentMode, toggleAgentMode, stopAllAgents, activeSessionIds, stoppingSessionIds, renameSession, addParticipant, removeParticipant, consumeSkipFocus, createRoutine } = useWorkspace();
+ const { agents, currentUser, currentSessionId, sessions, updateLastMessage, setSessionActive, agentModes, updateAgentMode, toggleAgentMode, stopAllAgents, activeSessionIds, stoppingSessionIds, renameSession, addParticipant, removeParticipant, consumeSkipFocus, createRoutine } = useWorkspace();
const [showCreateRoutine, setShowCreateRoutine] = useState(false);
const {
isMobile,
@@ -317,6 +317,7 @@ export function ChatView() {
const handleSend = useCallback(
async (content: string, mentions: string[] = [], files: PendingFile[] = []) => {
if (!currentSessionId) return;
+ if (!currentUser.id || !currentUser.name.trim()) return;
// Create optimistic messages for instant feedback
const timestamp = Date.now();
@@ -324,8 +325,9 @@ export function ChatView() {
const userOptimisticMsg: WorkspaceMessage = {
messageId: `optimistic-user-${timestamp}`,
sessionId: currentSessionId,
- senderName: 'You',
- senderType: 'user',
+ senderId: currentUser.id,
+ senderName: currentUser.name,
+ senderType: 'human',
content: userContent,
messageType: 'chat',
mentions: [],
@@ -368,9 +370,10 @@ export function ChatView() {
await workspaceApi.sendMessage(
currentSessionId,
content || (attachments ? attachments.map((a) => a.filename).join(', ') : ''),
- 'user',
+ currentUser.name,
mentions.length > 0 ? mentions : undefined,
attachments,
+ currentUser.id,
);
forceRefresh();
} catch {
@@ -379,7 +382,7 @@ export function ChatView() {
setOptimisticMessages([]);
}
},
- [currentSessionId, forceRefresh, agents]
+ [currentSessionId, currentUser.id, currentUser.name, forceRefresh, agents]
);
const hasStatusMessages = displayMessages.some((m) => m.messageType === 'status' || m.messageType === 'thinking');
@@ -686,6 +689,7 @@ export function ChatView() {
onFocusChange={(focused) => focused ? notifyFocus() : notifyBlur()}
focusKey={focusKey}
onCreateRoutine={() => setShowCreateRoutine(true)}
+ disabled={!currentUser.name.trim()}
/>
diff --git a/workspace/frontend/components/identity/identity-dialog.tsx b/workspace/frontend/components/identity/identity-dialog.tsx
new file mode 100644
index 000000000..c97116cbf
--- /dev/null
+++ b/workspace/frontend/components/identity/identity-dialog.tsx
@@ -0,0 +1,70 @@
+'use client';
+
+import { useState } from 'react';
+import { User } from 'lucide-react';
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogDescription,
+} from '@/components/ui/dialog';
+import { Input } from '@/components/ui/input';
+import { Button } from '@/components/ui/button';
+
+interface IdentityDialogProps {
+ open: boolean;
+ onSubmit: (name: string) => void;
+}
+
+export function IdentityDialog({ open, onSubmit }: IdentityDialogProps) {
+ const [name, setName] = useState('');
+
+ const handleSubmit = () => {
+ const trimmed = name.trim();
+ if (!trimmed) return;
+ onSubmit(trimmed);
+ };
+
+ return (
+
+ );
+}
diff --git a/workspace/frontend/components/layout/sidebar-content.tsx b/workspace/frontend/components/layout/sidebar-content.tsx
index 77cf25539..8934b804f 100644
--- a/workspace/frontend/components/layout/sidebar-content.tsx
+++ b/workspace/frontend/components/layout/sidebar-content.tsx
@@ -4,7 +4,7 @@ import { useState, useEffect, useMemo } from 'react';
import {
Plus, MessageSquare, FileText, Globe, PlusSquare,
Settings, Copy, Check, ListTodo, CalendarClock,
- LogIn, LogOut, Shield, Moon, Sun, KeyRound, Share2, X, Crown,
+ LogIn, LogOut, Shield, Moon, Sun, KeyRound, Share2, X, Crown, Users,
} from 'lucide-react';
import { useTheme } from 'next-themes';
import { ScrollArea } from '@/components/ui/scroll-area';
@@ -71,7 +71,7 @@ function NavButton({
export function SidebarContent() {
const { isSidebarOpen, sidebarToggle, viewMode, setViewMode, setSelectedAgentName } = useLayout();
- const { agents, sessions, files, browserTabs, createSession, workspace, token, refreshWorkspace, todos, routines } = useWorkspace();
+ const { agents, sessions, files, browserTabs, createSession, workspace, token, refreshWorkspace, todos, routines, currentUser, onlineUsers } = useWorkspace();
const { user, isOpenAgentsDomain, signIn, signOut } = useOpenAgentsAuth();
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
@@ -241,6 +241,29 @@ export function SidebarContent() {
))}
+ {/* Online Users */}
+ {onlineUsers.length > 0 && (
+ <>
+
+
+ Online ({onlineUsers.length})
+
+
+ {onlineUsers.map((u) => (
+
+
+
+ {u.id === currentUser.id ? `${u.name} (you)` : u.name}
+
+
+ ))}
+
+ >
+ )}
+
{/* Collaboration */}
Collaboration
@@ -575,4 +598,3 @@ function SettingsDialogPortal({ open, onOpenChange, workspace, refreshWorkspace
);
}
-
diff --git a/workspace/frontend/components/monitor/monitor-overlay.tsx b/workspace/frontend/components/monitor/monitor-overlay.tsx
index 07fde3fa9..6ebdbc074 100644
--- a/workspace/frontend/components/monitor/monitor-overlay.tsx
+++ b/workspace/frontend/components/monitor/monitor-overlay.tsx
@@ -8,6 +8,7 @@ import {
} from '@/components/ui/dialog';
import { ChatMessages } from '@/components/chat/chat-messages';
import { ChatInput, type PendingFile } from '@/components/chat/chat-input';
+import { CreateRoutineDialog } from '@/components/routines/create-routine-dialog';
import { useWorkspace } from '@/lib/workspace-context';
import { useMessagePolling } from '@/hooks/use-polling';
import { workspaceApi } from '@/lib/api';
@@ -24,7 +25,8 @@ interface MonitorOverlayProps {
}
export function MonitorOverlay({ sessionId, session, initialMessages, open, onOpenChange }: MonitorOverlayProps) {
- const { agents, activeSessionIds, stoppingSessionIds, stopAllAgents, renameSession } = useWorkspace();
+ const { agents, currentUser, activeSessionIds, stoppingSessionIds, stopAllAgents, renameSession, createRoutine } = useWorkspace();
+ const [showCreateRoutine, setShowCreateRoutine] = useState(false);
const [editingTitle, setEditingTitle] = useState(false);
const [titleDraft, setTitleDraft] = useState('');
const titleInputRef = useRef(null);
@@ -107,14 +109,17 @@ export function MonitorOverlay({ sessionId, session, initialMessages, open, onOp
const handleSend = useCallback(
async (content: string, mentions: string[] = [], files: PendingFile[] = []) => {
+ if (!currentUser.id || !currentUser.name.trim()) return;
+
// Optimistic messages
const timestamp = Date.now();
const userContent = content || (files.length > 0 ? files.map((f) => f.file.name).join(', ') : '');
const userOptimisticMsg: WorkspaceMessage = {
messageId: `optimistic-user-${timestamp}`,
sessionId,
- senderName: 'You',
- senderType: 'user',
+ senderId: currentUser.id,
+ senderName: currentUser.name,
+ senderType: 'human',
content: userContent,
messageType: 'chat',
mentions: [],
@@ -156,16 +161,17 @@ export function MonitorOverlay({ sessionId, session, initialMessages, open, onOp
await workspaceApi.sendMessage(
sessionId,
content || (attachments ? attachments.map((a) => a.filename).join(', ') : ''),
- 'user',
+ currentUser.name,
mentions.length > 0 ? mentions : undefined,
attachments,
+ currentUser.id,
);
forceRefresh();
} catch {
setOptimisticMessages([]);
}
},
- [sessionId, forceRefresh, agents]
+ [sessionId, currentUser.id, currentUser.name, forceRefresh, agents]
);
return (
@@ -237,11 +243,18 @@ export function MonitorOverlay({ sessionId, session, initialMessages, open, onOp
{/* Input */}
-
+ setShowCreateRoutine(true)} />
);
}
diff --git a/workspace/frontend/lib/api.ts b/workspace/frontend/lib/api.ts
index d7946699a..ff2d7f64b 100644
--- a/workspace/frontend/lib/api.ts
+++ b/workspace/frontend/lib/api.ts
@@ -199,14 +199,17 @@ class WorkspaceApi {
senderName = 'user',
mentions?: string[],
attachments?: { fileId: string; filename: string; contentType: string; url: string }[],
+ senderId?: string,
): Promise