feat: v0.7.0 四阶段全量迭代 — 修复面收口 · 安全纵深 · 架构还债 · 能力演进
CI / 类型检查 + Lint + 单元测试 (push) Failing after 5m45s
CI / 全量测试 (Electron ABI) (push) Failing after 5m22s
CI / 产物编译验证 (push) Successful in 10m3s

P1 修复面收口: v0.6.3 截断自愈推全量(Anthropic/Ollama/非流式/引擎兜底); SSE 上游错误帧检测进重试通道;
clearMessages 摘要游标根治; truncateResult 内联图片白名单统一; 前端四 bug(确认弹窗锁死/MemoryViewer/
Virtuoso Footer/abort 尾部过滤) + reasoning 缓冲跨迭代污染; 托盘通知过滤与新建会话死链接线

P2 安全纵深: MCP 审批闭环(ConfirmationHook×PolicyEngine 联动+重名拒注册); SSRF 收敛 ssrf-guard 共享模块
(web_fetch 双通道校验+重定向终态复检); Electron 加固(preload CJS 化→sandbox:true/CSP/权限白名单/will-navigate);
run_command cmd.exe 白名单通道元字符守门; diff_viewer 10MB 预检; Anthropic thinking 预算下限; Agnes 思考显式关闭

P3 架构还债: OpenAICompatibleAdapter 中间基类收敛四家样板; 错误分类单轨化(删 mapError/getFetchSignal,
超时显式 ETIMEDOUT); PRAGMA user_version 迁移版本化; 死代码清理专项(cn.ts/SHORTCUTS/ContextMenu 分支/
getWindowState/modifiedArgs/sandbox 空壳); i18next 引入; a11y 第一轮; SearXNG 页批量草稿模型统一

P4 能力演进: Ollama pull 可取消/capabilities 探测/num_ctx 实测缓存; UpdateService feed 比对式自动更新
(app:updateCheck IPC + StatusBar 入口); MiMo providerOptions(web_search 服务端工具/strict JSON);
web_fetch extract_mode=markdown(turndown); network.proxyUrl 全局代理(Chromium sessions+undici dispatcher)

测试: 264 → 507 用例(Electron ABI 全绿零跳过), 覆盖引擎压缩管线/重试竞速/MEMORY.md 闸门/file_editor 五操作/
filesystem 七工具实体夹具/git 真实仓库/SSE 错误帧/全线截断自愈/Provider 请求形态矩阵/SSRF 表测/钩子分级矩阵/
OutputValidator 全量/SLO 指标/MCP 安全纯函数/task_manager 链路/渲染层纯域/i18n 桥契约
This commit is contained in:
2026-08-27 17:06:58 +08:00
parent b6e2a8bd25
commit 3940716dc2
78 changed files with 6369 additions and 1341 deletions
+1 -9
View File
@@ -26,15 +26,7 @@ import {
} from 'lucide-react';
import { useUIStore, type ThemeMode } from '@renderer/stores/ui-store';
import { useAgentStore } from '@renderer/stores/agent-store';
const PROVIDER_LABELS: Record<string, string> = {
deepseek: 'DeepSeek',
agnes: 'Agnes',
mimo: 'MiMo',
ollama: 'Ollama',
openai: 'OpenAI',
anthropic: 'Anthropic',
};
import { PROVIDER_LABELS } from '@renderer/lib/constants';
export function Header(): React.JSX.Element {
const sidebarVisible = useUIStore((s) => s.sidebarVisible);
+8 -12
View File
@@ -179,18 +179,13 @@ export function Sidebar(): React.JSX.Element {
return; // 不创建本地假会话
}
}
const newSession: Session = {
id: `s_${Date.now()}`,
title: '新会话',
createdAt: Date.now(),
updatedAt: Date.now(),
messageCount: 0,
pinned: false,
archived: false,
};
useSessionStore.getState().addSession(newSession);
setCurrentSession(newSession.id);
loadSessionMessages(newSession.id);
// v0.6.4 修复: 与 M-9 注释对齐 —— IPC 桥不可用(window.metona.sessions.create
// 不存在)时同样不构造本地假会话。假会话重启后即蒸发,且会污染 session 列表状态。
// 直接提示用户(正常构建下 preload 必然提供该 API,此分支仅在桥接损坏时触达)。
console.error('[Sidebar] window.metona.sessions.create is unavailable — IPC bridge broken');
import('@metona-team/metona-toast')
.then((mod) => mod.default.error('IPC 桥不可用:无法创建会话'))
.catch(() => {});
};
return (
@@ -517,6 +512,7 @@ function SessionItem({
<IconButton
className="delete-btn"
size="small"
aria-label={`删除会话 ${session.title}`}
onClick={handleDelete}
sx={{
opacity: 0,
+47 -1
View File
@@ -22,6 +22,7 @@ export function StatusBar(): React.JSX.Element {
const [version, setVersion] = useState(
typeof __APP_VERSION__ !== 'undefined' && __APP_VERSION__ ? `v${__APP_VERSION__}` : 'dev',
);
const [checkingUpdate, setCheckingUpdate] = useState(false);
useEffect(() => {
// M-28 修复: 添加 cancelled 标志防止组件卸载后 setState
@@ -82,7 +83,52 @@ export function StatusBar(): React.JSX.Element {
{/* 右侧:版本 + 设置 */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, flexShrink: 0 }}>
<Typography variant="caption" sx={{ color: 'text.disabled', fontSize: 10 }}>{version}</Typography>
{/* v0.6.4 P4-2: 版本号可点击 → 手动检查更新(feed 比对式) */}
<Tooltip title={`点击检查更新(当前 ${version}`}>
<Typography
variant="caption"
component="button"
onClick={() => {
if (checkingUpdate) return;
setCheckingUpdate(true);
window.metona?.app
?.updateCheck()
.then((result) => {
if (result.status === 'available') {
import('@metona-team/metona-toast')
.then((mod) =>
mod.default.info(`发现新版本 ${result.latestVersion},点击此通知或设置中打开下载页`, {
onClick: result.downloadUrl
? () =>
void window.metona?.app?.openExternal(result.downloadUrl as string)
: undefined,
}),
)
.catch(() => {});
} else if (result.status === 'up-to-date') {
import('@metona-team/metona-toast')
.then((mod) => mod.default.success('已是最新版本'))
.catch(() => {});
} else {
import('@metona-team/metona-toast')
.then((mod) => mod.default.warning(result.message))
.catch(() => {});
}
})
.catch((err) => console.error('[StatusBar] update check failed:', err))
.finally(() => setCheckingUpdate(false));
}}
sx={{
color: checkingUpdate ? 'warning.main' : 'text.disabled',
fontSize: 10,
cursor: 'pointer',
border: 0, p: 0, bgcolor: 'transparent', lineHeight: 1,
'&:hover': { color: 'primary.main' },
}}
>
{checkingUpdate ? '检查中…' : version}
</Typography>
</Tooltip>
<Tooltip title="设置 (Ctrl+,)">
<IconButton size="small" onClick={openSettings} sx={{ color: 'text.secondary', width: 28, height: 28 }}>
<Settings size={14} />