diff --git a/docs/develop-guides/changelog.md b/docs/develop-guides/changelog.md
index b41926bd..2a17d0ce 100644
--- a/docs/develop-guides/changelog.md
+++ b/docs/develop-guides/changelog.md
@@ -33,6 +33,7 @@
### 修复
+- 调整聊天首页的智能体切换入口:在无历史对话时,智能体数量 `<= 3` 且 `chat-main` 宽度不小于 `380px` 时继续使用横向 segmented;当智能体数量 `>= 4` 或内容区宽度小于 `380px` 时自动收敛为“当前智能体 + 下拉按钮”形式,避免多智能体或窄屏场景下入口被截断
- 发布前一致性修复:统一 0.6.0 版本号(backend/package/web)、更新 dev/prod 镜像标签语义(`0.6.0.dev` / `0.6.0`),并为 `/api/system/health` 补充 `version` 字段,提升部署可观测性与发版追溯能力
- 收敛“状态工作台”自动弹出规则:前端不再因为共享 `workspace` 或文件系统天然存在内容而默认展开,改为仅在 `/home/gem/user-data/uploads` 或 `/home/gem/user-data/outputs` 下检测到实际文件时自动弹出;手动打开、关闭、刷新和伸缩交互保持不变
- 调整智能体 todo 展示语义:待办状态不再作为 `capabilities` 前端开关,而是直接根据运行态 `agent_state.todos` 渲染;同时将 todo 入口从 Agent Panel 移到输入框内的轻量浮层,并让右侧“状态工作台”收敛为文件系统视图,输入框按钮文案同步由“状态”调整为“文件”
diff --git a/docs/develop-guides/roadmap.md b/docs/develop-guides/roadmap.md
index 858a8057..f7cbb9bf 100644
--- a/docs/develop-guides/roadmap.md
+++ b/docs/develop-guides/roadmap.md
@@ -52,6 +52,7 @@
- 重写界面设计规范:参考 `DESIGN.md` 写法补充视觉气质、颜色 token、组件状态、布局层级、响应式与 Agent Prompt Guide,并基于该规范收敛首页视觉表现,移除装饰性渐变、重阴影、hover 位移和入场动画。
- 修复对话摘要中间件的工具结果卸载链路:摘要触发时改为将大体积 `ToolMessage` 写入当前 agent 可见的 sandbox outputs 路径,修正 `summary_offload` 路径拼接错误、`messages` 触发条件下不会真正裁剪历史的问题,并避免将 system message 重复纳入摘要与最终消息列表;补充对应单元测试覆盖。
- 调整智能体对话中的工具调用展示:连续工具调用默认折叠为“调用了 N 个工具”的轻量摘要,展开后改为弱化时间线样式,减少工具结果卡片对正文阅读节奏的干扰。
+- 调整聊天首页的智能体切换入口:在无历史对话时,智能体数量 `<= 3` 且 `chat-main` 宽度不小于 `380px` 时继续使用横向 segmented;当智能体数量 `>= 4` 或内容区宽度小于 `380px` 时自动收敛为“当前智能体 + 下拉按钮”形式。
---
diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue
index 44ec53fd..847829f5 100644
--- a/web/src/components/AgentChatComponent.vue
+++ b/web/src/components/AgentChatComponent.vue
@@ -153,6 +153,37 @@
/>
+
+
+
+
+
+
+
+
+
{
if (!currentAgentId.value || !agents.value || !agents.value.length) return null
return agents.value.find((a) => a.id === currentAgentId.value) || null
})
+const startAgents = computed(() => agents.value || [])
const chatsList = computed(() => threads.value || [])
const currentChatId = computed(() => chatState.currentThreadId)
const currentThread = computed(() => {
@@ -409,6 +449,7 @@ const currentThreadAgentName = computed(() => {
}
return currentAgentName.value
})
+const currentAgentIcon = computed(() => getAgentIconComponent(currentAgentId.value))
// 检查当前智能体是否支持文件上传
const supportsFileUpload = computed(() => {
@@ -530,7 +571,7 @@ const getAgentIconComponent = (agentId) => {
}
const agentSegmentOptions = computed(() => {
- return (agents.value || []).map((agent) => {
+ return startAgents.value.map((agent) => {
const IconComponent = getAgentIconComponent(agent.id)
return {
label: () =>
@@ -543,8 +584,16 @@ const agentSegmentOptions = computed(() => {
})
})
+const showStartAgentSelector = computed(() => {
+ return !props.singleMode && !conversations.value.length && startAgents.value.length > 1
+})
+
+const showStartAgentDropdown = computed(() => {
+ return showStartAgentSelector.value && (startAgents.value.length >= 4 || localUIState.chatMainWidth < 380)
+})
+
const showStartAgentSegment = computed(() => {
- return !props.singleMode && !conversations.value.length && agentSegmentOptions.value.length > 1
+ return showStartAgentSelector.value && !showStartAgentDropdown.value
})
const handleStartAgentChange = async (agentId) => {
@@ -594,9 +643,11 @@ onMounted(() => {
}
if (window.ResizeObserver && chatMainRef.value) {
+ localUIState.chatMainWidth = chatMainRef.value.clientWidth || window.innerWidth
chatMainResizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const width = entry.contentRect.width
+ localUIState.chatMainWidth = width
const isTakingSpace = chatUIStore.isSidebarOpen && !isSidebarFloating.value
if (isTakingSpace) {
@@ -1969,6 +2020,80 @@ watch(currentChatId, (threadId, oldThreadId) => {
}
}
+.agent-switcher-wrapper {
+ display: flex;
+ justify-content: center;
+ margin: 0 auto 18px;
+}
+
+.agent-switcher-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+ max-width: 100%;
+ padding: 4px 12px;
+ border: 1px solid var(--gray-150);
+ border-radius: 8px;
+ background: var(--gray-0);
+ color: var(--gray-900);
+ cursor: pointer;
+ transition:
+ background-color 0.2s ease,
+ border-color 0.2s ease,
+ color 0.2s ease;
+
+ &:hover {
+ background: var(--gray-0);
+ border-color: var(--gray-200);
+ }
+}
+
+.agent-switcher-icon,
+.agent-switcher-chevron {
+ flex-shrink: 0;
+ color: var(--gray-600);
+}
+
+.agent-switcher-text {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+:deep(.agent-switcher-menu) {
+ min-width: 220px;
+}
+
+:deep(.agent-switcher-menu-item) {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+:deep(.agent-switcher-menu-icon) {
+ flex-shrink: 0;
+ color: var(--gray-600);
+}
+
+:deep(.agent-switcher-menu-text) {
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+:deep(.agent-switcher-menu-badge) {
+ flex-shrink: 0;
+ padding: 1px 8px;
+ border-radius: 999px;
+ background: var(--main-30);
+ color: var(--main-700);
+ font-size: 12px;
+}
+
.example-questions {
margin-top: 16px;
text-align: center;
@@ -2184,6 +2309,15 @@ watch(currentChatId, (threadId, oldThreadId) => {
}
}
+ .agent-switcher-wrapper {
+ margin-bottom: 8px;
+ }
+
+ .agent-switcher-btn {
+ width: 100%;
+ justify-content: center;
+ }
+
.chat-header {
.header__left {
.text {