diff --git a/server/chat_flow_task_main.py b/server/chat_flow_task_main.py
index d17cb41..db12867 100644
--- a/server/chat_flow_task_main.py
+++ b/server/chat_flow_task_main.py
@@ -152,6 +152,7 @@ _VALID_USER_MESSAGE_SOURCES = {
"goal_prompt",
"goal_review",
"compression",
+ "compression_handoff",
"permission",
"sandbox",
"skill",
@@ -164,7 +165,7 @@ def _user_message_ui_defaults(source: str, *, auto_user_message_event: bool = Fa
return {"visibility": "hidden", "starts_work": False}
if normalized in {"guidance", "notify"}:
return {"visibility": "compact", "starts_work": False}
- if normalized in {"goal_review", "compression", "sub_agent", "background_command"}:
+ if normalized in {"goal_review", "compression", "compression_handoff", "sub_agent", "background_command"}:
return {"visibility": "compact", "starts_work": True}
if normalized == "presend":
return {"visibility": "chat", "starts_work": True}
@@ -1085,16 +1086,22 @@ async def handle_task_with_sender(
if guide_message:
finalize_user_work_timer()
finalize_run_versioning_checkpoint("auto_deep_compress_handoff")
- await handle_task_with_sender(
- web_terminal,
- workspace,
- guide_message,
- [],
- sender,
- client_sid,
- username,
- []
- )
+ # 压缩后的续接引导语单独标记来源,使其以 compact 形式展示,
+ # 而不是混入普通用户消息流。
+ setattr(web_terminal, "_current_user_message_source", "compression_handoff")
+ try:
+ await handle_task_with_sender(
+ web_terminal,
+ workspace,
+ guide_message,
+ [],
+ sender,
+ client_sid,
+ username,
+ []
+ )
+ finally:
+ setattr(web_terminal, "_current_user_message_source", "user")
return
finalize_user_work_timer()
finalize_run_versioning_checkpoint("auto_deep_compress_end")
@@ -1570,16 +1577,22 @@ async def handle_task_with_sender(
if deep_result.get("success") and guide_message:
finalize_user_work_timer()
finalize_run_versioning_checkpoint("deep_compress_handoff")
- await handle_task_with_sender(
- web_terminal,
- workspace,
- guide_message,
- [],
- sender,
- client_sid,
- username,
- []
- )
+ # 压缩后的续接引导语单独标记来源,使其以 compact 形式展示,
+ # 而不是混入普通用户消息流。
+ setattr(web_terminal, "_current_user_message_source", "compression_handoff")
+ try:
+ await handle_task_with_sender(
+ web_terminal,
+ workspace,
+ guide_message,
+ [],
+ sender,
+ client_sid,
+ username,
+ []
+ )
+ finally:
+ setattr(web_terminal, "_current_user_message_source", "user")
return
finalize_user_work_timer()
finalize_run_versioning_checkpoint("deep_compress_end")
diff --git a/server/chat_flow_task_support.py b/server/chat_flow_task_support.py
index ab16d07..ddd9fb6 100644
--- a/server/chat_flow_task_support.py
+++ b/server/chat_flow_task_support.py
@@ -19,6 +19,7 @@ _VALID_SOURCES = {
"goal_prompt",
"goal_review",
"compression",
+ "compression_handoff",
"permission",
"sandbox",
"skill",
@@ -32,7 +33,7 @@ def _runtime_message_ui_defaults(src: str, *, inline: bool = False) -> Dict[str,
return {"visibility": "hidden", "starts_work": False}
if normalized in {"guidance", "notify"}:
return {"visibility": "compact", "starts_work": False}
- if normalized in {"goal_review", "compression", "sub_agent", "background_command", "presend"}:
+ if normalized in {"goal_review", "compression", "compression_handoff", "sub_agent", "background_command", "presend"}:
return {"visibility": "compact", "starts_work": True}
if normalized == "goal":
# Backward-compatible fallback for old callers. Inline goal injections are
diff --git a/server/conversation.py b/server/conversation.py
index 56a5969..803c483 100644
--- a/server/conversation.py
+++ b/server/conversation.py
@@ -1207,6 +1207,7 @@ def compress_conversation(conversation_id, terminal: WebTerminal, workspace: Use
images=[],
conversation_id=new_conversation_id,
videos=[],
+ message_source="compression_handoff",
)
response_payload["auto_task_started"] = True
response_payload["auto_task_id"] = task_rec.task_id
diff --git a/static/src/components/chat/ChatArea.vue b/static/src/components/chat/ChatArea.vue
index 35c393e..734b2ec 100644
--- a/static/src/components/chat/ChatArea.vue
+++ b/static/src/components/chat/ChatArea.vue
@@ -11,44 +11,49 @@
'message-block--before-sub-agent-notice': isFollowedBySubAgentNotice(index)
}"
>
-
-
+
+
简略消息显示审核、子智能体等紧凑系统消息的显示形式。
+
+
+
+
+
+
+
{
);
});
+const currentCompactMessageDisplay = computed(() => experiments.value.compactMessageDisplay);
+
+const compactMessageDisplayLabel = computed(() => {
+ return (
+ compactMessageDisplayOptions.find(
+ (option) => option.value === currentCompactMessageDisplay.value
+ )?.label || '完整信息'
+ );
+});
+
const usageSummary = ref({
total_input_tokens: 0,
total_output_tokens: 0,
@@ -2084,6 +2133,26 @@ const handleBlockDisplayModeChange = (mode: 'traditional' | 'stacked' | 'minimal
personalization.setBlockDisplayMode(mode);
};
+const compactMessageDisplayOptions = [
+ {
+ id: 'full',
+ label: '完整信息',
+ desc: '审核、子智能体等系统消息显示完整原始内容',
+ value: 'full' as const
+ },
+ {
+ id: 'brief',
+ label: '简略信息',
+ desc: '仅以一行横线概要替代(如「目标审核完成」)',
+ value: 'brief' as const
+ }
+];
+
+const selectCompactMessageDisplay = (mode: 'full' | 'brief') => {
+ personalization.setCompactMessageDisplay(mode);
+ closeDropdown();
+};
+
const openAdminPanel = () => {
window.open('/admin/monitor', '_blank', 'noopener');
personalization.closeDrawer();
diff --git a/static/src/stores/personalization.ts b/static/src/stores/personalization.ts
index 32c673e..ac1bb04 100644
--- a/static/src/stores/personalization.ts
+++ b/static/src/stores/personalization.ts
@@ -2,6 +2,7 @@ import { defineStore } from 'pinia';
import { useModelStore } from './model';
export type BlockDisplayMode = 'traditional' | 'stacked' | 'minimal';
+export type CompactMessageDisplay = 'full' | 'brief';
type RunMode = 'fast' | 'thinking' | 'deep';
type PermissionMode = 'readonly' | 'approval' | 'auto_approval' | 'unrestricted';
@@ -53,6 +54,7 @@ interface PersonalForm {
interface ExperimentState {
blockDisplayMode: BlockDisplayMode;
+ compactMessageDisplay: CompactMessageDisplay;
}
interface PersonalizationState {
@@ -142,7 +144,8 @@ const defaultForm = (): PersonalForm => ({
});
const defaultExperimentState = (): ExperimentState => ({
- blockDisplayMode: 'stacked'
+ blockDisplayMode: 'stacked',
+ compactMessageDisplay: 'full'
});
const loadExperimentState = (): ExperimentState => {
@@ -168,8 +171,18 @@ const loadExperimentState = (): ExperimentState => {
blockDisplayMode = parsed.stackedBlocksEnabled ? 'stacked' : 'traditional';
}
+ let compactMessageDisplay: CompactMessageDisplay =
+ defaultExperimentState().compactMessageDisplay;
+ if (
+ typeof parsed?.compactMessageDisplay === 'string' &&
+ ['full', 'brief'].includes(parsed.compactMessageDisplay)
+ ) {
+ compactMessageDisplay = parsed.compactMessageDisplay;
+ }
+
return {
- blockDisplayMode
+ blockDisplayMode,
+ compactMessageDisplay
};
} catch (error) {
console.warn('无法读取实验功能设置:', error);
@@ -760,6 +773,13 @@ export const usePersonalizationStore = defineStore('personalization', {
};
this.persistExperiments();
},
+ setCompactMessageDisplay(mode: CompactMessageDisplay) {
+ this.experiments = {
+ ...this.experiments,
+ compactMessageDisplay: mode
+ };
+ this.persistExperiments();
+ },
setCommunicationStyle(style: 'default' | 'human_like') {
this.form = {
...this.form,
diff --git a/static/src/styles/components/chat/_chat-area.scss b/static/src/styles/components/chat/_chat-area.scss
index 282b00e..228dc07 100644
--- a/static/src/styles/components/chat/_chat-area.scss
+++ b/static/src/styles/components/chat/_chat-area.scss
@@ -511,6 +511,38 @@
border: 1px solid var(--theme-control-border);
}
+/* 简略信息:以横跨显示区域的横线包裹一行概要文字(类似 markdown 的 --- 分隔线)。 */
+.user-message.user-message--brief {
+ align-items: stretch;
+ width: 100%;
+}
+
+.user-message.user-message--brief .compact-brief-line {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ width: min(760px, 92%);
+ margin: 2px auto;
+ color: var(--claude-text-secondary);
+ user-select: none;
+}
+
+.user-message.user-message--brief .compact-brief-line::before,
+.user-message.user-message--brief .compact-brief-line::after {
+ content: '';
+ flex: 1 1 auto;
+ height: 0;
+ border-top: 1px solid var(--theme-control-border, var(--claude-border));
+}
+
+.user-message.user-message--brief .compact-brief-text {
+ flex: 0 0 auto;
+ font-size: 12.5px;
+ letter-spacing: 0.04em;
+ white-space: nowrap;
+ opacity: 0.9;
+}
+
.user-message .message-text .user-skill-link {
color: #2563eb;
font-weight: 600;
diff --git a/static/src/utils/messageVisibility.ts b/static/src/utils/messageVisibility.ts
index 69bfd7d..b1f207c 100644
--- a/static/src/utils/messageVisibility.ts
+++ b/static/src/utils/messageVisibility.ts
@@ -6,6 +6,7 @@ const COMPACT_FALLBACK_SOURCES = new Set([
'guidance',
'goal_review',
'compression',
+ 'compression_handoff',
'sub_agent',
'background_command'
]);
@@ -72,7 +73,7 @@ export function messageStartsWork(message: any): boolean {
if (source === 'guidance' || source === 'goal_prompt' || source === 'skill') {
return false;
}
- if (source === 'goal_review' || source === 'compression' || isLegacyGoalReview(message)) {
+ if (source === 'goal_review' || source === 'compression' || source === 'compression_handoff' || isLegacyGoalReview(message)) {
return true;
}
return LEGACY_STARTS_WORK_SOURCES.has(source || 'user');