- prompt改造:子智能体注入AGENTS.md/执行环境/工作区信息/skills/项目记忆 - 工具增加:read_skill/recall_project_memory/todo_create/todo_update_task/save_webpage - 上下文压缩:深度压缩机制,记录current_context_tokens,默认150k阈值可配置 - 模型升级:sub_agent_models.json支持thinkmode_status/extra_parameter,与主智能体对齐 - 角色管理三层结构:源码树预设multi_agent_roles/ + 运行态host/web预设 + web按用户隔离 - 启动同步:initialize_system调用sync_preset_roles同步预设到host和web运行态 - 模式判断:API用session.host_mode,工具用data_dir路径判断,不依赖IS_HOST_MODE - 前端:个人空间新增子智能体管理页(角色CRUD/压缩阈值/模型选择),复用个人空间样式 - 入口改造:登录页移除多智能体按钮,QuickMenu加模式切换项,运行中对话禁止切换 - 工具调整:多智能体模式create_sub_agent去掉timeout/deliverables_dir参数 - skill禁止:sub-agent-guide在多智能体模式下禁止阅读 - .agents/统一为.astrion/路径修复
403 lines
13 KiB
TypeScript
403 lines
13 KiB
TypeScript
// @ts-nocheck
|
||
import { mapState, mapWritableState } from 'pinia';
|
||
import { useConnectionStore } from '../stores/connection';
|
||
import { useFileStore } from '../stores/file';
|
||
import { useUiStore } from '../stores/ui';
|
||
import { useConversationStore } from '../stores/conversation';
|
||
import { useModelStore } from '../stores/model';
|
||
import { useChatStore } from '../stores/chat';
|
||
import { useInputStore } from '../stores/input';
|
||
import { useToolStore } from '../stores/tool';
|
||
import { useResourceStore } from '../stores/resource';
|
||
import { useFocusStore } from '../stores/focus';
|
||
import { useUploadStore } from '../stores/upload';
|
||
import { useMonitorStore } from '../stores/monitor';
|
||
import { usePolicyStore } from '../stores/policy';
|
||
import { useSubAgentStore } from '../stores/subAgent';
|
||
import { useBackgroundCommandStore } from '../stores/backgroundCommand';
|
||
import { usePersonalizationStore } from '../stores/personalization';
|
||
import { getToolStatusText, getToolDescription } from '../utils/chatDisplay';
|
||
import { toolFaceKey } from '../utils/avatarFace';
|
||
|
||
// 取最后一段连续的 tool actions(对应模型一次并行调用的一批工具)
|
||
function getLatestToolSegment(actions) {
|
||
if (!Array.isArray(actions)) return [];
|
||
let last = -1;
|
||
for (let i = actions.length - 1; i >= 0; i--) {
|
||
if (actions[i]?.type === 'tool') {
|
||
last = i;
|
||
break;
|
||
}
|
||
if (actions[i]?.type === 'thinking') break;
|
||
}
|
||
if (last < 0) return [];
|
||
let start = last;
|
||
while (start > 0 && actions[start - 1]?.type === 'tool') start--;
|
||
return actions.slice(start, last + 1);
|
||
}
|
||
|
||
const AVATAR_RUNNING_TOOL_STATUS = new Set([
|
||
'preparing',
|
||
'running',
|
||
'pending',
|
||
'queued',
|
||
'awaiting_approval',
|
||
'pending_approval',
|
||
'awaiting_user_answer'
|
||
]);
|
||
const AVATAR_TERMINAL_STATUS = new Set([
|
||
'completed',
|
||
'failed',
|
||
'timeout',
|
||
'terminated',
|
||
'cancelled'
|
||
]);
|
||
|
||
export const computed = {
|
||
...mapWritableState(useConnectionStore, [
|
||
'isConnected',
|
||
'socket',
|
||
'stopRequested',
|
||
'projectPath',
|
||
'agentVersion',
|
||
'thinkingMode',
|
||
'runMode'
|
||
]),
|
||
...mapState(useFileStore, ['contextMenu', 'fileTree', 'expandedFolders', 'todoList']),
|
||
...mapWritableState(useUiStore, [
|
||
'sidebarCollapsed',
|
||
'workspaceCollapsed',
|
||
'chatDisplayMode',
|
||
'panelMode',
|
||
'panelMenuOpen',
|
||
'leftWidth',
|
||
'rightWidth',
|
||
'rightCollapsed',
|
||
'isResizing',
|
||
'resizingPanel',
|
||
'minPanelWidth',
|
||
'maxPanelWidth',
|
||
'quotaToast',
|
||
'toastQueue',
|
||
'confirmDialog',
|
||
'easterEgg',
|
||
'isMobileViewport',
|
||
'mobileOverlayMenuOpen',
|
||
'activeMobileOverlay'
|
||
]),
|
||
...mapWritableState(useConversationStore, [
|
||
'conversations',
|
||
'conversationsLoading',
|
||
'hasMoreConversations',
|
||
'loadingMoreConversations',
|
||
'currentConversationId',
|
||
'currentConversationTitle',
|
||
'searchQuery',
|
||
'searchTimer',
|
||
'searchResults',
|
||
'conversationInsertAnimations',
|
||
'conversationListAnimationMode',
|
||
'pendingDeletingConversationIds',
|
||
'searchActive',
|
||
'searchInProgress',
|
||
'searchMoreAvailable',
|
||
'searchOffset',
|
||
'searchTotal',
|
||
'conversationsOffset',
|
||
'conversationsLimit',
|
||
'runningWorkspaceTasks',
|
||
'acknowledgedCompletedTaskIds',
|
||
'workspaceGroups'
|
||
]),
|
||
...mapWritableState(useModelStore, ['currentModelKey']),
|
||
...mapState(useModelStore, ['models']),
|
||
...mapWritableState(useChatStore, [
|
||
'messages',
|
||
'currentMessageIndex',
|
||
'streamingMessage',
|
||
'expandedBlocks',
|
||
'autoScrollEnabled',
|
||
'userScrolling',
|
||
'thinkingScrollLocks'
|
||
]),
|
||
...mapWritableState(useInputStore, [
|
||
'inputMessage',
|
||
'inputLineCount',
|
||
'inputIsMultiline',
|
||
'inputIsFocused',
|
||
'quickMenuOpen',
|
||
'toolMenuOpen',
|
||
'settingsOpen',
|
||
'imagePickerOpen',
|
||
'videoPickerOpen',
|
||
'selectedImages',
|
||
'selectedVideos',
|
||
'goalModeArmed',
|
||
'goalRunning',
|
||
'goalProgress',
|
||
'goalDialogOpen'
|
||
]),
|
||
resolvedRunMode() {
|
||
const allowed = ['fast', 'thinking', 'deep'];
|
||
if (allowed.includes(this.runMode)) {
|
||
return this.runMode;
|
||
}
|
||
return this.thinkingMode ? 'thinking' : 'fast';
|
||
},
|
||
headerRunModeOptions() {
|
||
return [
|
||
{ value: 'fast', label: '快速模式', desc: '低思考,响应更快' },
|
||
{ value: 'thinking', label: '思考模式', desc: '更长思考,综合回答' },
|
||
{ value: 'deep', label: '深度思考', desc: '持续推理,适合复杂任务' }
|
||
];
|
||
},
|
||
headerRunModeLabel() {
|
||
const current = this.headerRunModeOptions.find((o) => o.value === this.resolvedRunMode);
|
||
return current ? current.label : '快速模式';
|
||
},
|
||
currentModelLabel() {
|
||
const modelStore = useModelStore();
|
||
return modelStore.currentModel?.label || '未选择模型';
|
||
},
|
||
policyUiBlocks() {
|
||
const store = usePolicyStore();
|
||
return store.uiBlocks || {};
|
||
},
|
||
adminDisabledModels() {
|
||
const store = usePolicyStore();
|
||
return store.disabledModelSet;
|
||
},
|
||
modelOptions() {
|
||
const disabledSet = this.adminDisabledModels || new Set();
|
||
const options = this.models || [];
|
||
return options.map((opt) => ({
|
||
...opt,
|
||
disabled: disabledSet.has(opt.key)
|
||
}));
|
||
},
|
||
titleRibbonVisible() {
|
||
return !this.isMobileViewport && this.chatDisplayMode === 'chat';
|
||
},
|
||
chatContainerStyle() {
|
||
const minHeight = 90;
|
||
const raw = Number(this.composerReservedHeight || 0);
|
||
const height = Number.isFinite(raw) ? Math.max(minHeight, Math.round(raw)) : minHeight;
|
||
const growth = Math.max(0, height - minHeight);
|
||
return {
|
||
'--composer-base-height': `calc(${minHeight}px + var(--app-bottom-inset, 0px))`,
|
||
'--composer-reserved-height': `calc(${height}px + var(--app-bottom-inset, 0px))`,
|
||
'--composer-growth-height': `${growth}px`
|
||
};
|
||
},
|
||
...mapWritableState(useToolStore, [
|
||
'preparingTools',
|
||
'activeTools',
|
||
'toolActionIndex',
|
||
'toolStacks',
|
||
'toolSettings',
|
||
'toolSettingsLoading'
|
||
]),
|
||
...mapWritableState(useResourceStore, [
|
||
'tokenPanelCollapsed',
|
||
'currentContextTokens',
|
||
'currentConversationTokens',
|
||
'projectStorage',
|
||
'containerStatus',
|
||
'containerNetRate',
|
||
'usageQuota'
|
||
]),
|
||
...mapWritableState(useFocusStore, ['focusedFiles']),
|
||
...mapWritableState(useUploadStore, ['uploading', 'mediaUploading']),
|
||
...mapState(useMonitorStore, {
|
||
monitorIsLocked: (store) => store.isLocked
|
||
}),
|
||
displayModeSwitchDisabled() {
|
||
return !!this.policyUiBlocks.block_virtual_monitor;
|
||
},
|
||
displayLockEngaged() {
|
||
return !!this.compressionInProgress || !!this.compressing || !!this.currentWorkspaceHasRunningTask;
|
||
},
|
||
currentWorkspaceHasRunningTask() {
|
||
if (!(this.versioningHostMode || this.dockerProjectMode) || !this.currentHostWorkspaceId) {
|
||
return false;
|
||
}
|
||
const tasks = Array.isArray(this.runningWorkspaceTasks) ? this.runningWorkspaceTasks : [];
|
||
return tasks.some((task: any) => {
|
||
const status = String(task?.status || '');
|
||
return (
|
||
task?.workspace_id === this.currentHostWorkspaceId &&
|
||
task?.conversation_id !== this.currentConversationId &&
|
||
['pending', 'running', 'cancel_requested'].includes(status)
|
||
);
|
||
});
|
||
},
|
||
anyWorkspaceHasRunningTask() {
|
||
const tasks = Array.isArray(this.runningWorkspaceTasks) ? this.runningWorkspaceTasks : [];
|
||
return tasks.some((task: any) => {
|
||
const status = String(task?.status || '');
|
||
return ['pending', 'running', 'cancel_requested'].includes(status);
|
||
});
|
||
},
|
||
streamingUi() {
|
||
return this.streamingMessage || this.hasPendingToolActions();
|
||
},
|
||
composerBusy() {
|
||
const monitorLock = this.monitorIsLocked && this.chatDisplayMode === 'monitor';
|
||
return (
|
||
this.streamingUi ||
|
||
this.taskInProgress ||
|
||
monitorLock ||
|
||
this.stopRequested ||
|
||
this.compressionInProgress ||
|
||
this.compressing
|
||
);
|
||
},
|
||
composerStreamingForInput() {
|
||
return this.composerBusy && !this.displayLockEngaged;
|
||
},
|
||
composerHeroActive() {
|
||
return (this.blankHeroActive || this.blankHeroExiting) && !this.composerInteractionActive;
|
||
},
|
||
showStatusAvatar() {
|
||
return usePersonalizationStore().form?.show_status_avatar !== false;
|
||
},
|
||
composerInteractionActive() {
|
||
const hasText = !!(this.inputMessage && this.inputMessage.trim().length > 0);
|
||
const hasImages = Array.isArray(this.selectedImages) && this.selectedImages.length > 0;
|
||
return this.quickMenuOpen || hasText || hasImages;
|
||
},
|
||
showScrollToBottomButton() {
|
||
return this.chatDisplayMode === 'chat' && !this.stickIsNearBottom;
|
||
},
|
||
rightPanelsStyle() {
|
||
const w = Math.min(this.rightWidth || 420, 600);
|
||
const t = this.terminalPanelOpen;
|
||
const g = this.gitChangesPanelOpen;
|
||
const ratio = this.rightSplitRatio ?? 0.5;
|
||
const r = Math.max(0.15, Math.min(0.85, Number(ratio) || 0.5));
|
||
let rows: string;
|
||
if (t && g) rows = `${r}fr 2px ${1 - r}fr`;
|
||
else if (t) rows = '1fr 0px 0fr';
|
||
else if (g) rows = '0fr 0px 1fr';
|
||
else rows = '0fr 0px 0fr';
|
||
return {
|
||
width: w + 'px',
|
||
gridTemplateRows: rows
|
||
};
|
||
},
|
||
// 状态形象:驱动 StatusAvatar(欢迎页 + 对话末尾指示器)
|
||
avatarStatus() {
|
||
const messages = Array.isArray(this.messages) ? this.messages : [];
|
||
const lastAssistant = (() => {
|
||
for (let i = messages.length - 1; i >= 0; i--) {
|
||
if (messages[i]?.role === 'assistant') return messages[i];
|
||
}
|
||
return null;
|
||
})();
|
||
|
||
// 后台运行计数(用于 work 文案)
|
||
const subAgentStore = useSubAgentStore();
|
||
const bgStore = useBackgroundCommandStore();
|
||
const countRunning = (arr) =>
|
||
(Array.isArray(arr) ? arr : []).filter((item) => {
|
||
if (!item) return false;
|
||
const status = String(item?.status || '').toLowerCase();
|
||
return status ? !AVATAR_TERMINAL_STATUS.has(status) : true;
|
||
}).length;
|
||
const agentCount = countRunning(subAgentStore.subAgents);
|
||
const cmdCount = countRunning(bgStore.commands);
|
||
const bgText = (() => {
|
||
const parts = [];
|
||
if (cmdCount > 0) parts.push(`${cmdCount}个后台指令`);
|
||
if (agentCount > 0) parts.push(`${agentCount}个后台智能体`);
|
||
return parts.length ? `${parts.join(',')}运行中...` : '';
|
||
})();
|
||
|
||
const intentEnabled = !!usePersonalizationStore().form?.tool_intent_enabled;
|
||
|
||
// 1) 思考中
|
||
const isThinking =
|
||
!!lastAssistant &&
|
||
(lastAssistant.currentStreamingType === 'thinking' || lastAssistant.activeThinkingId != null);
|
||
|
||
// 2) 工具执行中(取最后一段并行工具,过滤未完成的)
|
||
let runningTools = [];
|
||
if (lastAssistant) {
|
||
const segment = getLatestToolSegment(lastAssistant.actions || []);
|
||
runningTools = segment.filter((a) => {
|
||
const s = String(a?.tool?.status || '').toLowerCase();
|
||
return a?.tool?.awaiting_content || !s || AVATAR_RUNNING_TOOL_STATUS.has(s);
|
||
});
|
||
}
|
||
if (runningTools.length === 0) {
|
||
const mapActions = [
|
||
...Array.from(this.preparingTools?.values?.() || []),
|
||
...Array.from(this.activeTools?.values?.() || [])
|
||
];
|
||
runningTools = mapActions
|
||
.map((item) => (item?.type === 'tool' ? item : { type: 'tool', tool: item }))
|
||
.filter((a) => {
|
||
const tool = a?.tool;
|
||
if (!tool) return false;
|
||
const s = String(tool?.status || '').toLowerCase();
|
||
return tool?.awaiting_content || !s || AVATAR_RUNNING_TOOL_STATUS.has(s);
|
||
});
|
||
}
|
||
const hasMapTools =
|
||
(this.preparingTools && this.preparingTools.size > 0) ||
|
||
(this.activeTools && this.activeTools.size > 0);
|
||
|
||
// 3) 运行/等待态标志
|
||
const running =
|
||
this.streamingMessage ||
|
||
this.taskInProgress ||
|
||
this.waitingForSubAgent ||
|
||
this.waitingForBackgroundCommand ||
|
||
this.stopRequested ||
|
||
hasMapTools ||
|
||
runningTools.length > 0 ||
|
||
agentCount > 0 ||
|
||
cmdCount > 0;
|
||
|
||
// ---- 决策 ----
|
||
if (isThinking) {
|
||
return { mode: 'think', toolKeys: [], toolTexts: [], text: '思考中...', tracking: false };
|
||
}
|
||
if (runningTools.length > 0) {
|
||
const keys = runningTools.map((a) => toolFaceKey(a?.tool?.name));
|
||
const getFinalIntentText = (tool: any) => {
|
||
if (!tool) return '';
|
||
const full = tool.intent_full || '';
|
||
const rendered = tool.intent_rendered || '';
|
||
// 只在 intent 打字效果完成后才显示完整文案,避免头像上出现逐字动画
|
||
return full && rendered === full ? full : '';
|
||
};
|
||
const toolTexts = runningTools.map((a) => {
|
||
return intentEnabled ? getFinalIntentText(a?.tool) : '';
|
||
});
|
||
let text = toolTexts[0] || '';
|
||
if (runningTools.length > 1) {
|
||
text = toolTexts[0] || '';
|
||
} else if (!text) {
|
||
text = intentEnabled ? getFinalIntentText(runningTools[0]?.tool) : '';
|
||
}
|
||
return { mode: 'tool', toolKeys: keys, toolTexts, text, tracking: false };
|
||
}
|
||
if (running) {
|
||
// 工作态:等 API / 后台运行 / 流式输出正文
|
||
let text = bgText;
|
||
if (!text) {
|
||
const awaitingMsg = lastAssistant && lastAssistant.awaitingFirstContent ? lastAssistant : null;
|
||
if (awaitingMsg) {
|
||
text =
|
||
(typeof awaitingMsg.generatingLabel === 'string' && awaitingMsg.generatingLabel.trim()) ||
|
||
'';
|
||
}
|
||
}
|
||
return { mode: 'work', toolKeys: [], toolTexts: [], text, tracking: false };
|
||
}
|
||
// 空闲
|
||
return { mode: 'idle', toolKeys: [], toolTexts: [], text: '', tracking: true };
|
||
}
|
||
};
|