fix: adjust sub-agent notice spacing in minimal mode
This commit is contained in:
parent
2700a25702
commit
a5412dae34
@ -23,6 +23,7 @@ export function created() {
|
||||
|
||||
export async function mounted() {
|
||||
debugLog('Vue应用已挂载');
|
||||
console.log('[DEBUG_SYSTEM][boot] 前端调试版本已加载 2026-04-05');
|
||||
if (window.ensureCsrfToken) {
|
||||
window.ensureCsrfToken().catch((err) => {
|
||||
console.warn('CSRF token 初始化失败:', err);
|
||||
|
||||
@ -1,6 +1,16 @@
|
||||
// @ts-nocheck
|
||||
import { debugLog } from './common';
|
||||
|
||||
const SUB_AGENT_DONE_PREFIX_RE = /^(?:✅\s*)?子智能体\s*#?\s*(\d+)\s*任务摘要[::]/;
|
||||
|
||||
function parseSubAgentDoneLabel(rawContent: any): string | null {
|
||||
const content = (rawContent || '').toString().trim();
|
||||
if (!content) return null;
|
||||
const match = content.match(SUB_AGENT_DONE_PREFIX_RE);
|
||||
if (!match || !/已完成/.test(content)) return null;
|
||||
return `子智能体${match[1]} 任务完成`;
|
||||
}
|
||||
|
||||
export const historyMethods = {
|
||||
// ==========================================
|
||||
// 关键功能:获取并显示历史对话内容
|
||||
@ -315,6 +325,41 @@ export const historyMethods = {
|
||||
this.messages.push(currentAssistantMessage);
|
||||
currentAssistantMessage = null;
|
||||
}
|
||||
if (message.role === 'system') {
|
||||
const rawContent = message.content || '';
|
||||
const label = parseSubAgentDoneLabel(rawContent);
|
||||
console.log('[DEBUG_SYSTEM][history] 命中 role=system 历史消息', {
|
||||
rawContent,
|
||||
matchedSubAgentDone: !!label
|
||||
});
|
||||
if (label) {
|
||||
// 历史中的 system 通知转换为 assistant system action,避免被 role=system 过滤掉
|
||||
this.messages.push({
|
||||
role: 'assistant',
|
||||
actions: [
|
||||
{
|
||||
id: `history-system-${Date.now()}-${Math.random()}`,
|
||||
type: 'system',
|
||||
content: label,
|
||||
variant: 'sub_agent_done',
|
||||
streaming: false,
|
||||
timestamp: Date.now()
|
||||
}
|
||||
],
|
||||
streamingThinking: '',
|
||||
streamingText: '',
|
||||
currentStreamingType: null,
|
||||
activeThinkingId: null,
|
||||
awaitingFirstContent: false,
|
||||
generatingLabel: ''
|
||||
});
|
||||
} else {
|
||||
console.log('[DEBUG_SYSTEM][history] role=system 被历史加载拦截(非子智能体完成)', {
|
||||
rawContent
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
debugLog('处理其他类型消息:', message.role);
|
||||
this.messages.push({
|
||||
|
||||
@ -17,6 +17,17 @@ export const taskPollingMethods = {
|
||||
const eventType = event.type;
|
||||
const eventData = event.data || {};
|
||||
const eventIdx = event.idx;
|
||||
if (
|
||||
eventType === 'system_message' ||
|
||||
eventType === 'sub_agent_waiting' ||
|
||||
(eventType === 'user_message' && eventData?.sub_agent_notice)
|
||||
) {
|
||||
console.log('[DEBUG_SYSTEM][event] 捕获关键事件', {
|
||||
eventType,
|
||||
eventIdx,
|
||||
eventData
|
||||
});
|
||||
}
|
||||
|
||||
// 事件去重检查
|
||||
if (typeof eventIdx === 'number') {
|
||||
@ -145,6 +156,10 @@ export const taskPollingMethods = {
|
||||
this.handleUserMessage(eventData, eventIdx);
|
||||
break;
|
||||
|
||||
case 'system_message':
|
||||
this.handleSystemMessage(eventData, eventIdx);
|
||||
break;
|
||||
|
||||
default:
|
||||
debugLog(`[TaskPolling] 未知事件类型: ${eventType}`);
|
||||
}
|
||||
@ -767,6 +782,20 @@ export const taskPollingMethods = {
|
||||
this.conditionalScrollToBottom();
|
||||
},
|
||||
|
||||
handleSystemMessage(data: any) {
|
||||
const content = (data?.content || data?.message || '').trim();
|
||||
console.log('[DEBUG_SYSTEM][polling] 收到 system_message 事件', {
|
||||
raw: data,
|
||||
normalizedContent: content,
|
||||
currentConversationId: this.currentConversationId
|
||||
});
|
||||
if (!content) {
|
||||
console.log('[DEBUG_SYSTEM][polling] system_message 内容为空,跳过');
|
||||
return;
|
||||
}
|
||||
this.appendSystemAction(content);
|
||||
},
|
||||
|
||||
handleTaskError(data: any) {
|
||||
const shouldRetry = Boolean(data?.retry);
|
||||
if (shouldRetry) {
|
||||
|
||||
@ -16,6 +16,30 @@ import {
|
||||
} from '../../composables/usePanelResize';
|
||||
import { debugLog } from './common';
|
||||
|
||||
const SUB_AGENT_DONE_PREFIX_RE = /^(?:✅\s*)?子智能体\s*#?\s*(\d+)\s*任务摘要[::]/;
|
||||
|
||||
function parseSubAgentDoneLabel(rawContent: any): string | null {
|
||||
const content = (rawContent || '').toString().trim();
|
||||
if (!content) {
|
||||
console.log('[DEBUG_SYSTEM][ui] parseSubAgentDoneLabel: 空内容');
|
||||
return null;
|
||||
}
|
||||
const match = content.match(SUB_AGENT_DONE_PREFIX_RE);
|
||||
const isCompleted = /已完成/.test(content);
|
||||
console.log('[DEBUG_SYSTEM][ui] parseSubAgentDoneLabel', {
|
||||
content,
|
||||
regex: SUB_AGENT_DONE_PREFIX_RE.toString(),
|
||||
matched: !!match,
|
||||
matchAgentId: match?.[1],
|
||||
isCompleted
|
||||
});
|
||||
if (!match || !isCompleted) {
|
||||
return null;
|
||||
}
|
||||
const agentId = match[1];
|
||||
return `子智能体${agentId} 任务完成`;
|
||||
}
|
||||
|
||||
export const uiMethods = {
|
||||
ensureScrollListener() {
|
||||
if (this._scrollListenerReady) {
|
||||
@ -759,10 +783,18 @@ export const uiMethods = {
|
||||
},
|
||||
|
||||
addSystemMessage(content) {
|
||||
if (this.hideSystemMessages) {
|
||||
console.log('[DEBUG_SYSTEM][ui] addSystemMessage 入参', { content });
|
||||
const subAgentDoneLabel = parseSubAgentDoneLabel(content);
|
||||
if (!subAgentDoneLabel) {
|
||||
// 其他 system 消息全部隐藏,且不进入渲染链路,避免出现空白块
|
||||
console.log('[DEBUG_SYSTEM][ui] addSystemMessage 被拦截(非子智能体完成)', { content });
|
||||
return;
|
||||
}
|
||||
this.chatAddSystemMessage(content);
|
||||
console.log('[DEBUG_SYSTEM][ui] addSystemMessage 通过,写入 action', {
|
||||
original: content,
|
||||
normalizedLabel: subAgentDoneLabel
|
||||
});
|
||||
this.chatAddSystemMessage(subAgentDoneLabel, { variant: 'sub_agent_done' });
|
||||
this.$forceUpdate();
|
||||
this.conditionalScrollToBottom();
|
||||
},
|
||||
|
||||
@ -1,7 +1,15 @@
|
||||
<template>
|
||||
<div class="messages-area" ref="rootEl">
|
||||
<div class="messages-flow">
|
||||
<div v-for="(msg, index) in filteredMessages" :key="index" class="message-block">
|
||||
<div
|
||||
v-for="(msg, index) in filteredMessages"
|
||||
:key="index"
|
||||
class="message-block"
|
||||
:class="{
|
||||
'message-block--sub-agent-notice': isSubAgentNoticeOnlyMessage(msg),
|
||||
'message-block--before-sub-agent-notice': isFollowedBySubAgentNotice(index)
|
||||
}"
|
||||
>
|
||||
<div v-if="msg.role === 'user'" class="user-message">
|
||||
<div class="message-header icon-label">
|
||||
<span class="icon icon-sm" :style="iconStyleSafe('user')" aria-hidden="true"></span>
|
||||
@ -24,7 +32,7 @@
|
||||
<div v-else-if="msg.role === 'assistant'" class="assistant-message">
|
||||
<!-- 在 assistant 消息前显示 AI Assistant 头部 -->
|
||||
<!-- 只有当前一条消息是 user 且当前消息有内容时才显示 -->
|
||||
<div v-if="index > 0 && filteredMessages[index - 1].role === 'user' && (msg.actions?.length > 0 || msg.awaitingFirstContent)" class="message-header icon-label">
|
||||
<div v-if="index > 0 && filteredMessages[index - 1].role === 'user' && (hasRenderableAssistantActions(msg.actions || []) || msg.awaitingFirstContent)" class="message-header icon-label">
|
||||
<span class="icon icon-sm" :style="iconStyleSafe('bot')" aria-hidden="true"></span>
|
||||
<span>{{ aiAssistantName }}</span>
|
||||
<span v-if="assistantWorkLabel(index)" class="assistant-work-status">{{ assistantWorkLabel(index) }}</span>
|
||||
@ -52,6 +60,7 @@
|
||||
</div>
|
||||
<template v-if="blockDisplayMode === 'minimal'">
|
||||
<MinimalBlocks
|
||||
v-if="hasRenderableAssistantActions(msg.actions || [])"
|
||||
:actions="msg.actions || []"
|
||||
:conversation-running="streamingMessage"
|
||||
:is-latest-message="index === latestMessageIndex"
|
||||
@ -87,7 +96,7 @@
|
||||
:format-search-domains="formatSearchDomains"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
v-else-if="isActionVisible(group.action)"
|
||||
class="action-item"
|
||||
:key="group.action?.id || `${index}-${group.actionIndex}`"
|
||||
:class="{
|
||||
@ -139,10 +148,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="group.action?.type === 'system'" class="system-action">
|
||||
<div class="system-action-content">
|
||||
{{ group.action.content }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="group.action?.type === 'system' && getSubAgentSystemNoticeLabel(group.action)"
|
||||
class="sub-agent-system-summary-line"
|
||||
>
|
||||
{{ getSubAgentSystemNoticeLabel(group.action) }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@ -224,6 +234,7 @@
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="(action, actionIndex) in msg.actions || []"
|
||||
v-if="isActionVisible(action)"
|
||||
:key="action.id || `${index}-${actionIndex}`"
|
||||
class="action-item"
|
||||
:class="{
|
||||
@ -274,10 +285,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="action.type === 'system'" class="system-action">
|
||||
<div class="system-action-content">
|
||||
{{ action.content }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="action.type === 'system' && getSubAgentSystemNoticeLabel(action)"
|
||||
class="sub-agent-system-summary-line"
|
||||
>
|
||||
{{ getSubAgentSystemNoticeLabel(action) }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@ -423,12 +435,25 @@ const userName = computed(() => {
|
||||
}
|
||||
return personalization.form.user_name || '用户';
|
||||
});
|
||||
const filteredMessages = computed(() =>
|
||||
(props.messages || []).filter(m => !(m && m.metadata && m.metadata.system_injected_image) && m.role !== 'system')
|
||||
);
|
||||
const filteredMessages = computed(() => {
|
||||
const source = props.messages || [];
|
||||
const droppedRoleSystem = source.filter(m => m && m.role === 'system');
|
||||
if (droppedRoleSystem.length > 0) {
|
||||
console.log('[DEBUG_SYSTEM][render] filteredMessages 过滤了 role=system 消息', {
|
||||
count: droppedRoleSystem.length,
|
||||
samples: droppedRoleSystem.slice(0, 3)
|
||||
});
|
||||
}
|
||||
return source.filter(m => !(m && m.metadata && m.metadata.system_injected_image) && m.role !== 'system');
|
||||
});
|
||||
const latestMessageIndex = computed(() => filteredMessages.value.length - 1);
|
||||
const SUB_AGENT_DONE_LABEL_RE = /^子智能体\d+\s*任务完成$/;
|
||||
const SUB_AGENT_DONE_PREFIX_RE = /^(?:✅\s*)?子智能体\s*#?\s*(\d+)\s*任务摘要[::]/;
|
||||
const RENDERABLE_ACTION_TYPES = new Set(['thinking', 'text', 'tool', 'append', 'append_payload', 'system']);
|
||||
const nowMs = ref(Date.now());
|
||||
let timerHandle: number | null = null;
|
||||
const debugLoggedSystemActionKeys = new Set<string>();
|
||||
const debugLoggedUnknownActionKeys = new Set<string>();
|
||||
|
||||
const DEFAULT_GENERATING_TEXT = '生成中…';
|
||||
const rootEl = ref<HTMLElement | null>(null);
|
||||
@ -486,6 +511,88 @@ function formatDurationMs(durationMs: number): string {
|
||||
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function getSubAgentSystemNoticeLabel(action: any): string | null {
|
||||
if (!action || action.type !== 'system') {
|
||||
return null;
|
||||
}
|
||||
const content = typeof action.content === 'string' ? action.content.trim() : '';
|
||||
const key = action?.id || `no-id-${content.slice(0, 32)}`;
|
||||
if (!content) {
|
||||
if (!debugLoggedSystemActionKeys.has(`${key}-empty`)) {
|
||||
console.log('[DEBUG_SYSTEM][render] system action 内容为空', { action });
|
||||
debugLoggedSystemActionKeys.add(`${key}-empty`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (action.variant === 'sub_agent_done' && SUB_AGENT_DONE_LABEL_RE.test(content)) {
|
||||
if (!debugLoggedSystemActionKeys.has(`${key}-variant-pass`)) {
|
||||
console.log('[DEBUG_SYSTEM][render] 命中 variant 渲染', { action, label: content });
|
||||
debugLoggedSystemActionKeys.add(`${key}-variant-pass`);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
const m = content.match(SUB_AGENT_DONE_PREFIX_RE);
|
||||
if (m && /已完成/.test(content)) {
|
||||
if (!debugLoggedSystemActionKeys.has(`${key}-regex-pass`)) {
|
||||
console.log('[DEBUG_SYSTEM][render] 命中 regex 渲染', { action, normalized: `子智能体${m[1]} 任务完成` });
|
||||
debugLoggedSystemActionKeys.add(`${key}-regex-pass`);
|
||||
}
|
||||
return `子智能体${m[1]} 任务完成`;
|
||||
}
|
||||
if (!debugLoggedSystemActionKeys.has(`${key}-blocked`)) {
|
||||
console.log('[DEBUG_SYSTEM][render] system action 被渲染层拦截', {
|
||||
action,
|
||||
regex: SUB_AGENT_DONE_PREFIX_RE.toString(),
|
||||
completed: /已完成/.test(content)
|
||||
});
|
||||
debugLoggedSystemActionKeys.add(`${key}-blocked`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isActionVisible(action: any): boolean {
|
||||
if (!action) {
|
||||
return false;
|
||||
}
|
||||
const actionType = action.type;
|
||||
if (!RENDERABLE_ACTION_TYPES.has(actionType)) {
|
||||
const key = action?.id || `unknown-${actionType || 'none'}`;
|
||||
if (!debugLoggedUnknownActionKeys.has(key)) {
|
||||
console.log('[DEBUG_SYSTEM][render] 未知 action.type,已跳过渲染', {
|
||||
actionType,
|
||||
action
|
||||
});
|
||||
debugLoggedUnknownActionKeys.add(key);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (action.type !== 'system') {
|
||||
return true;
|
||||
}
|
||||
return !!getSubAgentSystemNoticeLabel(action);
|
||||
}
|
||||
|
||||
function hasRenderableAssistantActions(actions: any[] = []): boolean {
|
||||
return (actions || []).some((action) => isActionVisible(action));
|
||||
}
|
||||
|
||||
function isSubAgentNoticeOnlyMessage(msg: any): boolean {
|
||||
if (!msg || msg.role !== 'assistant') {
|
||||
return false;
|
||||
}
|
||||
const visibleActions = (msg.actions || []).filter((action: any) => isActionVisible(action));
|
||||
if (visibleActions.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
const onlyAction = visibleActions[0];
|
||||
return onlyAction?.type === 'system' && !!getSubAgentSystemNoticeLabel(onlyAction);
|
||||
}
|
||||
|
||||
function isFollowedBySubAgentNotice(index: number): boolean {
|
||||
const next = filteredMessages.value[index + 1];
|
||||
return !!next && isSubAgentNoticeOnlyMessage(next);
|
||||
}
|
||||
|
||||
function assistantWorkLabel(index: number): string {
|
||||
if (index <= 0) {
|
||||
return '';
|
||||
|
||||
@ -85,6 +85,19 @@
|
||||
<div v-else v-html="renderMarkdown(group.content || '', false)"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="group.type === 'system'" class="summary-group sub-agent-system-group">
|
||||
<div class="summary-line-text sub-agent-system-summary-line">
|
||||
<div class="summary-content-wrapper">
|
||||
<span class="summary-preview">{{ group.content || '' }}</span>
|
||||
</div>
|
||||
<div class="summary-status-icon">
|
||||
<svg class="check-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<polyline points="7.5 12 10.5 15 16.5 9" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@ -118,7 +131,7 @@ interface Step {
|
||||
}
|
||||
|
||||
interface BlockGroup {
|
||||
type: 'summary' | 'text';
|
||||
type: 'summary' | 'text' | 'system';
|
||||
id: string;
|
||||
actions?: Action[];
|
||||
content?: string;
|
||||
@ -204,7 +217,7 @@ const blockGroups = computed(() => {
|
||||
props.actions.forEach((action, idx) => {
|
||||
if (action.type === 'thinking' || action.type === 'tool') {
|
||||
currentGroup.push(action);
|
||||
} else if (action.type === 'text') {
|
||||
} else if (action.type === 'text' || action.type === 'system') {
|
||||
// 先保存当前的 thinking/tool 组
|
||||
if (currentGroup.length > 0) {
|
||||
// 使用第一个 action 的 id 作为 group id,保证稳定性
|
||||
@ -217,13 +230,26 @@ const blockGroups = computed(() => {
|
||||
groupIndex++;
|
||||
currentGroup = [];
|
||||
}
|
||||
// 添加 text 组
|
||||
groups.push({
|
||||
type: 'text',
|
||||
id: `text-${idx}`,
|
||||
content: action.content,
|
||||
streaming: action.streaming
|
||||
});
|
||||
if (action.type === 'text') {
|
||||
const text = typeof action.content === 'string' ? action.content : '';
|
||||
if (!text.trim()) {
|
||||
return;
|
||||
}
|
||||
// 添加 text 组
|
||||
groups.push({
|
||||
type: 'text',
|
||||
id: `text-${idx}`,
|
||||
content: text,
|
||||
streaming: action.streaming
|
||||
});
|
||||
} else {
|
||||
// 添加 system 组(用于子智能体完成通知,保持顺序)
|
||||
groups.push({
|
||||
type: 'system',
|
||||
id: `system-${idx}`,
|
||||
content: action.content
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -494,6 +520,18 @@ watch(() => props.actions, () => {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.sub-agent-system-group {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sub-agent-system-group .sub-agent-system-summary-line {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.sub-agent-system-group .sub-agent-system-summary-line:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* 摘要行(只有文字) */
|
||||
.summary-line-text {
|
||||
display: grid;
|
||||
|
||||
@ -289,15 +289,25 @@ export const useChatStore = defineStore('chat', {
|
||||
msg.streamingText = '';
|
||||
msg.currentStreamingType = null;
|
||||
},
|
||||
addSystemMessage(content: string) {
|
||||
addSystemMessage(content: string, meta: any = null) {
|
||||
const msg = this.ensureAssistantMessage();
|
||||
clearAwaitingFirstContent(msg);
|
||||
console.log('[DEBUG_SYSTEM][store] addSystemMessage 写入 actions', {
|
||||
content,
|
||||
meta,
|
||||
currentMessageIndex: this.currentMessageIndex,
|
||||
actionsBefore: Array.isArray(msg?.actions) ? msg.actions.length : -1
|
||||
});
|
||||
msg.actions.push({
|
||||
id: randomId('system'),
|
||||
type: 'system',
|
||||
content,
|
||||
variant: meta?.variant || null,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
console.log('[DEBUG_SYSTEM][store] addSystemMessage 写入完成', {
|
||||
actionsAfter: Array.isArray(msg?.actions) ? msg.actions.length : -1
|
||||
});
|
||||
},
|
||||
getActiveThinkingAction(msg: any) {
|
||||
if (!msg || !Array.isArray(msg.actions)) {
|
||||
|
||||
@ -363,6 +363,26 @@
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.message-block.message-block--sub-agent-notice {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.message-block.message-block--before-sub-agent-notice {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.message-block.message-block--before-sub-agent-notice .minimal-blocks-container {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.message-block.message-block--before-sub-agent-notice .minimal-blocks-container > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.message-block.message-block--sub-agent-notice .minimal-blocks-container {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.user-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -937,6 +957,14 @@ body[data-theme='dark'] .more-icon {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sub-agent-system-summary-line {
|
||||
padding: 0 20px 0 15px;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
color: var(--claude-text-secondary);
|
||||
}
|
||||
|
||||
.append-block,
|
||||
.append-placeholder {
|
||||
margin: 12px 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user