diff --git a/ui/src/ui/app-render.helpers.ts b/ui/src/ui/app-render.helpers.ts
index d95414729..acce8198d 100644
--- a/ui/src/ui/app-render.helpers.ts
+++ b/ui/src/ui/app-render.helpers.ts
@@ -1,5 +1,6 @@
-import { html } from "lit";
+import { html, nothing } from "lit";
 import { repeat } from "lit/directives/repeat.js";
+import { parseAgentSessionKey } from "../../../src/routing/session-key.js";
 import { t } from "../i18n/index.ts";
 import { refreshChat } from "./app-chat.ts";
 import { syncUrlWithSessionKey } from "./app-settings.ts";
@@ -84,11 +85,37 @@ export function renderTab(state: AppViewState, tab: Tab) {
 
 export function renderChatControls(state: AppViewState) {
   const mainSessionKey = resolveMainSessionKey(state.hello, state.sessionsResult);
+
+  // Determine which agent is "active" from the current session key
+  const agents = state.agentsList?.agents ?? [];
+  const defaultAgentId = state.agentsList?.defaultId ?? agents[0]?.id ?? "main";
+  const parsedSession = parseAgentSessionKey(state.sessionKey);
+  const currentAgentId = parsedSession?.agentId ?? defaultAgentId;
+
   const sessionOptions = resolveSessionOptions(
     state.sessionKey,
     state.sessionsResult,
     mainSessionKey,
+    currentAgentId,
   );
+
+  const switchToAgent = (agentId: string) => {
+    // Find the most recent session for this agent; fall back to webchat key
+    const agentSessions = (state.sessionsResult?.sessions ?? [])
+      .filter((s) => parseAgentSessionKey(s.key)?.agentId === agentId)
+      .sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
+    const bestKey = agentSessions[0]?.key ?? `agent:${agentId}:webchat`;
+    resetChatStateForSessionSwitch(state, bestKey);
+    void (state as unknown as OpenClawApp).loadAssistantIdentity();
+    syncUrlWithSessionKey(
+      state as unknown as Parameters<typeof syncUrlWithSessionKey>[0],
+      bestKey,
+      true,
+    );
+    void loadChatHistory(state as unknown as ChatState);
+  };
+
+  const plusIcon = html`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>`;
   const disableThinkingToggle = state.onboarding;
   const disableFocusToggle = state.onboarding;
   const showThinking = state.onboarding ? false : state.settings.chatShowThinking;
@@ -129,6 +156,25 @@ export function renderChatControls(state: AppViewState) {
   `;
   return html`
     <div class="chat-controls">
+      ${agents.length > 1 ? html`
+        <label class="field chat-controls__agent">
+          <select
+            .value=${currentAgentId}
+            ?disabled=${!state.connected}
+            @change=${(e: Event) => {
+              const agentId = (e.target as HTMLSelectElement).value;
+              switchToAgent(agentId);
+            }}
+          >
+            ${agents.map((a) => {
+              const name = a.identity?.name?.trim() || a.name?.trim() || a.id;
+              const emoji = a.identity?.emoji?.trim() ?? "";
+              return html`<option value=${a.id} ?selected=${a.id === currentAgentId}>${emoji ? `${emoji} ${name}` : name}</option>`;
+            })}
+          </select>
+        </label>
+        <span class="chat-controls__separator" style="opacity:0.3;">|</span>
+      ` : nothing}
       <label class="field chat-controls__session">
         <select
           .value=${state.sessionKey}
@@ -166,6 +212,14 @@ export function renderChatControls(state: AppViewState) {
           )}
         </select>
       </label>
+      <button
+        class="btn btn--sm btn--icon"
+        ?disabled=${!state.connected}
+        title="New session"
+        @click=${() => (state as unknown as OpenClawApp).handleSendChat("/new")}
+      >
+        ${plusIcon}
+      </button>
       <button
         class="btn btn--sm btn--icon"
         ?disabled=${state.chatLoading || !state.connected}
@@ -353,15 +407,23 @@ function resolveSessionOptions(
   sessionKey: string,
   sessions: SessionsListResult | null,
   mainSessionKey?: string | null,
+  filterAgentId?: string | null,
 ) {
   const seen = new Set<string>();
   const options: Array<{ key: string; displayName?: string }> = [];
 
+  // Helper: does a key belong to the filtered agent?
+  const matchesAgent = (key: string) => {
+    if (!filterAgentId) return true;
+    const p = parseAgentSessionKey(key);
+    return p ? p.agentId === filterAgentId : false;
+  };
+
   const resolvedMain = mainSessionKey && sessions?.sessions?.find((s) => s.key === mainSessionKey);
   const resolvedCurrent = sessions?.sessions?.find((s) => s.key === sessionKey);
 
-  // Add main session key first
-  if (mainSessionKey) {
+  // Add main session key first (only if it belongs to the selected agent)
+  if (mainSessionKey && matchesAgent(mainSessionKey)) {
     seen.add(mainSessionKey);
     options.push({
       key: mainSessionKey,
@@ -369,8 +431,8 @@ function resolveSessionOptions(
     });
   }
 
-  // Add current session key next
-  if (!seen.has(sessionKey)) {
+  // Add current session key next (if it belongs to the selected agent)
+  if (!seen.has(sessionKey) && matchesAgent(sessionKey)) {
     seen.add(sessionKey);
     options.push({
       key: sessionKey,
@@ -378,9 +440,12 @@ function resolveSessionOptions(
     });
   }
 
-  // Add sessions from the result
+  // Add sessions from the result, filtered to the selected agent, sorted newest first
   if (sessions?.sessions) {
-    for (const s of sessions.sessions) {
+    const filtered = sessions.sessions
+      .filter((s) => matchesAgent(s.key))
+      .sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
+    for (const s of filtered) {
       if (!seen.has(s.key)) {
         seen.add(s.key);
         options.push({
