```
feat(engine): 优化智能体循环中的助手消息处理逻辑 - 在没有工具调用时才添加助手消息到上下文 - 确保工具调用响应正确添加到消息上下文中 - 修复了消息构建的条件逻辑 fix(cron): 改进定时任务调度的时间解析功能 - 添加正则表达式导入用于时间显示解析 - 实现从显示文本中提取毫秒间隔的功能 - 增强整数转换的安全性,避免类型错误 - 优化定时任务配置的解析逻辑 feat(outlook): 增强Outlook集成的功能和稳定性 - 将默认超时时间从10秒增加到180秒 - 为状态检查函数添加可选的验证参数 - 串行执行邮件概览获取操作而非并行 - 改进连接状态验证逻辑 feat(channel): 添加设备名称作为会话标识的选项 - 为终端WebSocket适配器添加新的配置选项 - 实现基于设备名称生成会话对等ID的功能 - 记录原始对等ID和设备名称的元数据 - 支持从设备名称创建会话对等ID feat(skills): 完善技能学习评估系统和进度跟踪 - 在应用启动时自动调度待评估的技能草稿 - 为技能评估工作创建独立的循环工厂 - 实现异步技能评估任务的取消和清理机制 - 添加技能评估进度报告和状态跟踪功能 - 扩展会话列表API以包含更多详细信息 - 防止对不存在的会话进行操作 - 优化技能草稿提交和评估的业务逻辑 perf(skills): 提升技能评估的并发性能 - 实现并行技能案例评估以提高效率 - 添加最大并行案例数的环境变量控制 - 实现实时评估进度更新和回调机制 - 优化评估过程中的资源管理和同步 refactor(services): 创建隔离的智能体循环实例 - 添加创建独立智能体循环的工厂方法 - 确保新循环继承运行时服务配置 - 支持技能评估等需要隔离环境的场景 ```
This commit is contained in:
@ -57,6 +57,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import type { AppLocale } from '@/lib/i18n/core';
|
||||
import { pickAppText } from '@/lib/i18n/core';
|
||||
import { useAppI18n } from '@/lib/i18n/provider';
|
||||
import { nextOutlookAutoLoadTarget, type OutlookAutoLoadView } from '@/lib/outlook-page-state';
|
||||
|
||||
type OutlookFormState = OutlookConnectionPayload;
|
||||
type OutlookView = 'inbox' | 'sent' | 'calendar' | 'settings';
|
||||
@ -368,6 +369,11 @@ export default function OutlookPage() {
|
||||
sent: false,
|
||||
});
|
||||
const [calendarLoading, setCalendarLoading] = useState(false);
|
||||
const [autoLoadAttempted, setAutoLoadAttempted] = useState<Record<OutlookAutoLoadView, boolean>>({
|
||||
inbox: false,
|
||||
sent: false,
|
||||
calendar: false,
|
||||
});
|
||||
const formDirtyRef = React.useRef(formDirty);
|
||||
|
||||
useEffect(() => {
|
||||
@ -399,6 +405,7 @@ export default function OutlookPage() {
|
||||
}, [t]);
|
||||
|
||||
const loadMailboxPage = useCallback(async (view: OutlookMailboxView, skip = 0) => {
|
||||
setAutoLoadAttempted((current) => ({ ...current, [view]: true }));
|
||||
setMailboxLoading((current) => ({ ...current, [view]: true }));
|
||||
try {
|
||||
const nextPage = await getOutlookMessages(view === 'inbox' ? 'inbox' : 'sentitems', {
|
||||
@ -425,6 +432,7 @@ export default function OutlookPage() {
|
||||
}, [t]);
|
||||
|
||||
const loadCalendarPage = useCallback(async (anchorKey: string) => {
|
||||
setAutoLoadAttempted((current) => ({ ...current, calendar: true }));
|
||||
setCalendarLoading(true);
|
||||
try {
|
||||
const range = buildCalendarRange(anchorKey);
|
||||
@ -461,9 +469,7 @@ export default function OutlookPage() {
|
||||
if (!background) {
|
||||
setStatusLoading(false);
|
||||
}
|
||||
if (nextStatus.configured) {
|
||||
await loadOverview(options?.preserveOverview ?? background);
|
||||
} else {
|
||||
if (!nextStatus.configured) {
|
||||
setOverview(null);
|
||||
setOverviewLoading(false);
|
||||
}
|
||||
@ -523,9 +529,6 @@ export default function OutlookPage() {
|
||||
);
|
||||
const isConfigured = Boolean(status?.configured);
|
||||
const isConnected = Boolean(status?.connected);
|
||||
const inboxCount = overview?.recentInbox.length ?? 0;
|
||||
const sentCount = overview?.recentSent.length ?? 0;
|
||||
const eventCount = overview?.todayEvents.length ?? 0;
|
||||
const overviewWarnings = overview?.warnings || [];
|
||||
const testWarnings = testResult?.warnings || [];
|
||||
const statusPending = statusLoading && !status;
|
||||
@ -538,7 +541,6 @@ export default function OutlookPage() {
|
||||
label: t('设置', 'Settings'),
|
||||
hint: t('配置 Outlook 连接', 'Configure the Outlook connection'),
|
||||
icon: Settings2,
|
||||
count: null,
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -549,31 +551,27 @@ export default function OutlookPage() {
|
||||
label: t('收件箱', 'Inbox'),
|
||||
hint: t('最近接收邮件', 'Recently received mail'),
|
||||
icon: Inbox,
|
||||
count: null,
|
||||
},
|
||||
{
|
||||
id: 'sent' as const,
|
||||
label: t('发件箱', 'Sent'),
|
||||
hint: t('最近发送记录', 'Recently sent messages'),
|
||||
icon: Send,
|
||||
count: null,
|
||||
},
|
||||
{
|
||||
id: 'calendar' as const,
|
||||
label: t('日程', 'Calendar'),
|
||||
hint: t('未来 7 天', 'Next 7 days'),
|
||||
icon: CalendarDays,
|
||||
count: overviewPending ? null : eventCount,
|
||||
},
|
||||
{
|
||||
id: 'settings' as const,
|
||||
label: t('设置', 'Settings'),
|
||||
hint: t('连接与状态', 'Connection and status'),
|
||||
icon: Settings2,
|
||||
count: null,
|
||||
},
|
||||
];
|
||||
}, [eventCount, inboxCount, isConfigured, overviewPending, sentCount, t]);
|
||||
}, [isConfigured, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!availableViews.some((view) => view.id === activeView)) {
|
||||
@ -582,20 +580,31 @@ export default function OutlookPage() {
|
||||
}, [activeView, availableViews]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isConfigured) {
|
||||
return;
|
||||
}
|
||||
if (activeView === 'inbox' && !inboxPage && !mailboxLoading.inbox) {
|
||||
const target = nextOutlookAutoLoadTarget({
|
||||
isConfigured,
|
||||
activeView,
|
||||
loaded: {
|
||||
inbox: Boolean(inboxPage),
|
||||
sent: Boolean(sentPage),
|
||||
calendar: Boolean(calendarPage),
|
||||
},
|
||||
loading: {
|
||||
inbox: mailboxLoading.inbox,
|
||||
sent: mailboxLoading.sent,
|
||||
calendar: calendarLoading,
|
||||
},
|
||||
attempted: autoLoadAttempted,
|
||||
});
|
||||
if (target === 'inbox') {
|
||||
void loadMailboxPage('inbox', 0);
|
||||
}
|
||||
if (activeView === 'sent' && !sentPage && !mailboxLoading.sent) {
|
||||
} else if (target === 'sent') {
|
||||
void loadMailboxPage('sent', 0);
|
||||
}
|
||||
if (activeView === 'calendar' && !calendarPage && !calendarLoading) {
|
||||
} else if (target === 'calendar') {
|
||||
void loadCalendarPage(calendarAnchorKey);
|
||||
}
|
||||
}, [
|
||||
activeView,
|
||||
autoLoadAttempted,
|
||||
calendarAnchorKey,
|
||||
calendarLoading,
|
||||
calendarPage,
|
||||
@ -638,6 +647,7 @@ export default function OutlookPage() {
|
||||
setInboxPage(null);
|
||||
setSentPage(null);
|
||||
setCalendarPage(null);
|
||||
setAutoLoadAttempted({ inbox: false, sent: false, calendar: false });
|
||||
setCalendarAnchorKey(toLocalDateKey(new Date()));
|
||||
await loadStatus(true, { forceFormSync: true });
|
||||
setActiveView('inbox');
|
||||
@ -663,6 +673,7 @@ export default function OutlookPage() {
|
||||
setInboxPage(null);
|
||||
setSentPage(null);
|
||||
setCalendarPage(null);
|
||||
setAutoLoadAttempted({ inbox: false, sent: false, calendar: false });
|
||||
setCalendarAnchorKey(toLocalDateKey(new Date()));
|
||||
setActiveView('settings');
|
||||
setFormDirty(false);
|
||||
@ -676,6 +687,7 @@ export default function OutlookPage() {
|
||||
|
||||
const refreshOverview = async () => {
|
||||
await loadStatus(true, { preserveOverview: true });
|
||||
await loadOverview(true);
|
||||
if (activeView === 'inbox') {
|
||||
await loadMailboxPage('inbox', inboxPage?.page.skip ?? 0);
|
||||
} else if (activeView === 'sent') {
|
||||
@ -723,13 +735,6 @@ export default function OutlookPage() {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{isConfigured ? (
|
||||
<>
|
||||
<TopStat label={t('收件箱', 'Inbox')} value={String(inboxCount)} loading={overviewPending} />
|
||||
<TopStat label={t('发件箱', 'Sent')} value={String(sentCount)} loading={overviewPending} />
|
||||
<TopStat label={t('日程', 'Calendar')} value={String(eventCount)} loading={overviewPending} />
|
||||
</>
|
||||
) : null}
|
||||
<Button variant="outline" size="sm" className="h-11" onClick={() => void refreshOverview()}>
|
||||
<RefreshCw className={`mr-2 h-4 w-4 ${refreshing ? 'animate-spin' : ''}`} />
|
||||
{t('刷新', 'Refresh')}
|
||||
@ -783,9 +788,6 @@ export default function OutlookPage() {
|
||||
</span>
|
||||
<div className="text-left">
|
||||
<p className="text-sm font-semibold">{view.label}</p>
|
||||
{typeof view.count === 'number' ? (
|
||||
<p className="text-xs text-muted-foreground">{t(`${view.count} 条`, `${view.count} items`)}</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1210,19 +1212,6 @@ function MiniStat({ label, value }: { label: string; value: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function TopStat({ label, value, loading = false }: { label: string; value: string; loading?: boolean }) {
|
||||
return (
|
||||
<div className="rounded-full border bg-background px-3 py-1 text-sm">
|
||||
<span className="text-muted-foreground">{label}</span>
|
||||
{loading ? (
|
||||
<Skeleton className="ml-2 inline-flex h-4 w-8 align-middle" />
|
||||
) : (
|
||||
<span className="ml-2 font-semibold text-foreground">{value}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MessageCard({
|
||||
title,
|
||||
icon,
|
||||
|
||||
Reference in New Issue
Block a user