Merge branch 'feature/mobile-app'
This commit is contained in:
@@ -41,15 +41,15 @@
|
||||
"@playwright/test": "^1.56.1",
|
||||
"@types/express": "^4.17.20",
|
||||
"@types/node": "^20.9.0",
|
||||
"@types/react": "^18.2.37",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.0",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"@types/ws": "^8.5.8",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"concurrently": "^8.2.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"tsx": "^4.6.0",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^7.1.10",
|
||||
|
||||
@@ -30,12 +30,23 @@ export const LoadConversationRequestMessageSchema = z.object({
|
||||
conversationId: z.string(),
|
||||
});
|
||||
|
||||
export const ListConversationsRequestMessageSchema = z.object({
|
||||
type: z.literal("list_conversations_request"),
|
||||
});
|
||||
|
||||
export const DeleteConversationRequestMessageSchema = z.object({
|
||||
type: z.literal("delete_conversation_request"),
|
||||
conversationId: z.string(),
|
||||
});
|
||||
|
||||
export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
|
||||
UserTextMessageSchema,
|
||||
AudioChunkMessageSchema,
|
||||
AbortRequestMessageSchema,
|
||||
AudioPlayedMessageSchema,
|
||||
LoadConversationRequestMessageSchema,
|
||||
ListConversationsRequestMessageSchema,
|
||||
DeleteConversationRequestMessageSchema,
|
||||
]);
|
||||
|
||||
export type SessionInboundMessage = z.infer<typeof SessionInboundMessageSchema>;
|
||||
@@ -189,6 +200,28 @@ export const SessionStateMessageSchema = z.object({
|
||||
}),
|
||||
});
|
||||
|
||||
export const ListConversationsResponseMessageSchema = z.object({
|
||||
type: z.literal("list_conversations_response"),
|
||||
payload: z.object({
|
||||
conversations: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
lastUpdated: z.string(),
|
||||
messageCount: z.number(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
export const DeleteConversationResponseMessageSchema = z.object({
|
||||
type: z.literal("delete_conversation_response"),
|
||||
payload: z.object({
|
||||
conversationId: z.string(),
|
||||
success: z.boolean(),
|
||||
error: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
|
||||
ActivityLogMessageSchema,
|
||||
AssistantChunkMessageSchema,
|
||||
@@ -201,6 +234,8 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
|
||||
AgentUpdateMessageSchema,
|
||||
AgentStatusMessageSchema,
|
||||
SessionStateMessageSchema,
|
||||
ListConversationsResponseMessageSchema,
|
||||
DeleteConversationResponseMessageSchema,
|
||||
]);
|
||||
|
||||
export type SessionOutboundMessage = z.infer<
|
||||
@@ -219,6 +254,8 @@ export type AgentCreatedMessage = z.infer<typeof AgentCreatedMessageSchema>;
|
||||
export type AgentUpdateMessage = z.infer<typeof AgentUpdateMessageSchema>;
|
||||
export type AgentStatusMessage = z.infer<typeof AgentStatusMessageSchema>;
|
||||
export type SessionStateMessage = z.infer<typeof SessionStateMessageSchema>;
|
||||
export type ListConversationsResponseMessage = z.infer<typeof ListConversationsResponseMessageSchema>;
|
||||
export type DeleteConversationResponseMessage = z.infer<typeof DeleteConversationResponseMessageSchema>;
|
||||
|
||||
// Type exports for payload types
|
||||
export type ActivityLogPayload = z.infer<typeof ActivityLogPayloadSchema>;
|
||||
|
||||
@@ -15,7 +15,7 @@ import { getSystemPrompt } from "./agent/system-prompt.js";
|
||||
import { getAllTools } from "./agent/llm-openai.js";
|
||||
import { TTSManager } from "./agent/tts-manager.js";
|
||||
import { STTManager } from "./agent/stt-manager.js";
|
||||
import { saveConversation } from "./persistence.js";
|
||||
import { saveConversation, listConversations, deleteConversation } from "./persistence.js";
|
||||
import { experimental_createMCPClient } from "ai";
|
||||
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
||||
import { createTerminalMcpServer } from "./terminal-mcp/index.js";
|
||||
@@ -257,6 +257,14 @@ export class Session {
|
||||
case "load_conversation_request":
|
||||
await this.handleLoadConversation();
|
||||
break;
|
||||
|
||||
case "list_conversations_request":
|
||||
await this.handleListConversations();
|
||||
break;
|
||||
|
||||
case "delete_conversation_request":
|
||||
await this.handleDeleteConversation(msg.conversationId);
|
||||
break;
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
@@ -292,6 +300,71 @@ export class Session {
|
||||
await this.sendSessionState();
|
||||
}
|
||||
|
||||
/**
|
||||
* List all conversations
|
||||
*/
|
||||
public async handleListConversations(): Promise<void> {
|
||||
try {
|
||||
const conversations = await listConversations();
|
||||
this.emit({
|
||||
type: "list_conversations_response",
|
||||
payload: {
|
||||
conversations: conversations.map(conv => ({
|
||||
id: conv.id,
|
||||
lastUpdated: conv.lastUpdated.toISOString(),
|
||||
messageCount: conv.messageCount,
|
||||
})),
|
||||
},
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
`[Session ${this.clientId}] Failed to list conversations:`,
|
||||
error
|
||||
);
|
||||
this.emit({
|
||||
type: "activity_log",
|
||||
payload: {
|
||||
id: uuidv4(),
|
||||
timestamp: new Date(),
|
||||
type: "error",
|
||||
content: `Failed to list conversations: ${error.message}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a conversation
|
||||
*/
|
||||
public async handleDeleteConversation(conversationId: string): Promise<void> {
|
||||
try {
|
||||
await deleteConversation(conversationId);
|
||||
this.emit({
|
||||
type: "delete_conversation_response",
|
||||
payload: {
|
||||
conversationId,
|
||||
success: true,
|
||||
},
|
||||
});
|
||||
console.log(
|
||||
`[Session ${this.clientId}] Deleted conversation ${conversationId}`
|
||||
);
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
`[Session ${this.clientId}] Failed to delete conversation ${conversationId}:`,
|
||||
error
|
||||
);
|
||||
this.emit({
|
||||
type: "delete_conversation_response",
|
||||
payload: {
|
||||
conversationId,
|
||||
success: false,
|
||||
error: error.message,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send current session state (live agents and commands) to client
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@ export function useWebSocket(url: string, conversationId?: string | null): UseWe
|
||||
const [currentConversationId, setCurrentConversationId] = useState<string | null>(null);
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
const handlersRef = useRef<Map<SessionOutboundMessage['type'], Set<(message: SessionOutboundMessage) => void>>>(new Map());
|
||||
const reconnectTimeoutRef = useRef<number>();
|
||||
const reconnectTimeoutRef = useRef<number | undefined>(undefined);
|
||||
|
||||
const connect = useCallback(() => {
|
||||
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
||||
|
||||
Reference in New Issue
Block a user