diff --git a/src/cli/components/ListenerStatusUI.tsx b/src/cli/components/ListenerStatusUI.tsx index e87c191..c97ae24 100644 --- a/src/cli/components/ListenerStatusUI.tsx +++ b/src/cli/components/ListenerStatusUI.tsx @@ -5,6 +5,7 @@ import { useEffect, useState } from "react"; interface ListenerStatusUIProps { agentId: string; connectionId: string; + conversationId?: string; onReady: (callbacks: { updateStatus: (status: "idle" | "receiving" | "processing") => void; updateRetryStatus: (attempt: number, nextRetryIn: number) => void; @@ -13,7 +14,7 @@ interface ListenerStatusUIProps { } export function ListenerStatusUI(props: ListenerStatusUIProps) { - const { agentId, connectionId, onReady } = props; + const { agentId, connectionId, conversationId, onReady } = props; const [status, setStatus] = useState<"idle" | "receiving" | "processing">( "idle", ); @@ -34,7 +35,7 @@ export function ListenerStatusUI(props: ListenerStatusUIProps) { }); }, [onReady]); - const adeUrl = `https://app.letta.com/agents/${agentId}?deviceId=${connectionId}`; + const adeUrl = `https://app.letta.com/agents/${agentId}?deviceId=${connectionId}${conversationId ? `&conversationId=${conversationId}` : ""}`; const statusText = retryInfo ? `Reconnecting (attempt ${retryInfo.attempt}, retry in ${Math.round(retryInfo.nextRetryIn / 1000)}s)` diff --git a/src/cli/subcommands/listen.tsx b/src/cli/subcommands/listen.tsx index cb3989c..b882de4 100644 --- a/src/cli/subcommands/listen.tsx +++ b/src/cli/subcommands/listen.tsx @@ -10,12 +10,18 @@ import { settingsManager } from "../../settings-manager"; import { ListenerStatusUI } from "../components/ListenerStatusUI"; export async function runListenSubcommand(argv: string[]): Promise { + // Preprocess args to support --conv as alias for --conversation + const processedArgv = argv.map((arg) => + arg === "--conv" ? "--conversation" : arg, + ); + // Parse arguments const { values } = parseArgs({ - args: argv, + args: processedArgv, options: { name: { type: "string" }, agent: { type: "string" }, + conversation: { type: "string", short: "C" }, help: { type: "boolean", short: "h" }, }, allowPositionals: false, @@ -24,7 +30,7 @@ export async function runListenSubcommand(argv: string[]): Promise { // Show help if (values.help) { console.log( - "Usage: letta listen --name [--agent ]\n", + "Usage: letta listen --name [--agent ] [--conversation ]\n", ); console.log( "Register this letta-code instance to receive messages from Letta Cloud.\n", @@ -36,10 +42,17 @@ export async function runListenSubcommand(argv: string[]): Promise { console.log( " --agent Bind connection to specific agent (required for CLI usage)", ); + console.log(" --conversation , --conv , -C "); + console.log( + " Route messages to a specific conversation", + ); console.log(" -h, --help Show this help message\n"); console.log("Examples:"); console.log(' letta listen --name "george" --agent agent-abc123'); - console.log(' letta listen --name "laptop-work" --agent agent-xyz789\n'); + console.log(' letta listen --name "laptop-work" --agent agent-xyz789'); + console.log( + ' letta listen --name "daily-cron" --agent agent-abc123 --conv conv-xyz789\n', + ); console.log( "Once connected, this instance will listen for incoming messages from cloud agents.", ); @@ -51,6 +64,7 @@ export async function runListenSubcommand(argv: string[]): Promise { const connectionName = values.name; const agentId = values.agent; + const conversationId = values.conversation as string | undefined; if (!connectionName) { console.error("Error: --name is required\n"); @@ -102,6 +116,7 @@ export async function runListenSubcommand(argv: string[]): Promise { deviceId, connectionName, agentId, + ...(conversationId && { conversationId }), }), }); @@ -131,6 +146,7 @@ export async function runListenSubcommand(argv: string[]): Promise { { updateStatusCallback = callbacks.updateStatus; updateRetryStatusCallback = callbacks.updateRetryStatus; @@ -150,6 +166,7 @@ export async function runListenSubcommand(argv: string[]): Promise { deviceId, connectionName, agentId, + defaultConversationId: conversationId, onStatusChange: (status) => { clearRetryStatusCallback?.(); updateStatusCallback?.(status); diff --git a/src/websocket/listen-client.ts b/src/websocket/listen-client.ts index 87b0402..a7ca785 100644 --- a/src/websocket/listen-client.ts +++ b/src/websocket/listen-client.ts @@ -34,6 +34,7 @@ interface StartListenerOptions { deviceId: string; connectionName: string; agentId?: string; + defaultConversationId?: string; onConnected: () => void; onDisconnected: () => void; onError: (error: Error) => void; @@ -438,6 +439,7 @@ async function connectWithRetry( socket, opts.onStatusChange, opts.connectionId, + opts.defaultConversationId, ); opts.onStatusChange?.("idle", opts.connectionId); }) @@ -502,6 +504,7 @@ async function handleIncomingMessage( connectionId: string, ) => void, connectionId?: string, + defaultConversationId?: string, ): Promise { try { const agentId = msg.agentId; @@ -512,7 +515,8 @@ async function handleIncomingMessage( const requestedConversationId = msg.conversationId || undefined; // For sendMessageStream: "default" means use agent endpoint, else use conversations endpoint - const conversationId = requestedConversationId ?? "default"; + const conversationId = + requestedConversationId ?? defaultConversationId ?? "default"; if (!agentId) { return;