fix(release): finalize v1.18.0

This commit is contained in:
coso
2026-04-24 11:12:20 +08:00
parent 95127a18e3
commit 2746152a21
10 changed files with 89 additions and 35 deletions
+2 -2
View File
@@ -594,12 +594,12 @@ jobs:
find "$bundle_dir" -maxdepth 4 -type f | sort
done
- name: Fail when macOS notarized build is unavailable
- name: Fail when macOS release build is unavailable
if: startsWith(matrix.platform, 'macos') && steps.build_macos_primary.outcome == 'failure' && steps.build_macos_retry.outcome == 'failure'
shell: bash
run: |
set -euo pipefail
echo "::error::macOS notarization failed twice. Blocking release instead of publishing a signed-only artifact."
echo "::error::macOS release build failed twice before a notarized artifact became available. See the previous build attempt logs for the actual error."
exit 1
- name: Resolve macOS bundle dir
+8 -1
View File
@@ -6,7 +6,7 @@
- 本次发布目标 tag 为 `v1.18.0`。
- 本说明按当前待递交工作树重新整理,覆盖这次准备一起递交的版本同步、Rust/runtime、前端工作台、SceneApp、Memory、automation、bridge、roadmap 文档与 Playwright 复盘材料,不再沿用旧版“只写版本号”的简化摘要。
- 截至本次整理,待递交范围包含 `154` 个已跟踪文件改动、`27` 个未跟踪新增项,总计 `16506` 行新增、`1787` 行删除。
- 本次重新发布还补充纳入了聊天空态、技能入口、图片预览容错,以及 macOS release workflow 诊断文案修正,确保 `v1.18.0` 对外指向的是当前完整提交状态。
### 递交范围
@@ -23,6 +23,13 @@
### 重点更新
#### 0. 补充收口:发布前端构建阻断与 release 诊断信息校正
- `src/components/agent/chat/components/{EmptyState,ImageTaskViewer}.tsx`、`src/components/agent/chat/skill-selection/{CharacterMentionPanel,inputCapabilitySections}.ts` 与配套测试继续收口聊天空态、技能入口和图片预览体验。
- `src/components/provider-pool/api-key/ProviderModelList.tsx` 修正 provider 模型来源判断的类型收窄问题,避免 release 构建因前端 TypeScript 报错提前中断。
- `ImageTaskViewer.tsx` 为补图预览和弹窗预览统一补齐 `RenderableTaskImage` 的 fallback 渲染,避免新 props 约束下的构建失败。
- `.github/workflows/release.yml` 将原先误导性的 “macOS notarization failed twice” 兜底报错改成“release build failed before notarized artifact became available”,方便后续直接定位真实前序构建错误,而不是被公证文案误导。
#### 1. Agent runtime、任务层与模型层主线继续 current 化
- `src-tauri/crates/agent/src/session_execution_runtime.rs`、`protocol.rs`、`provider_safety.rs`、`credential_bridge.rs`,以及 `src-tauri/src/commands/aster_agent_cmd/{request_model_resolution,runtime_turn,dto,subagent_runtime}.rs` 继续扩展 runtime 路由、任务画像、provider 安全边界、会话上下文与事件投影。
+20
View File
@@ -5842,3 +5842,23 @@
- `vitest` 通过:`3 files / 43 tests passed`
- `eslint` 通过
- `verify:gui-smoke` 通过:`workspace-ready / browser-runtime / site-adapters / agent-service-skill-entry / agent-runtime-tool-surface / agent-runtime-tool-surface-page`
- 首页 `EmptyState` 与 slash 首屏当前又继续前推了一刀,避免 `我的方法` 页已经收成前台起手层,但首页推荐 shelf 和 `/` 首屏里还残留 `围绕最近成果 / 围绕最近复盘 / 结果模板 / 我的方法` 这类 badge-first 呈现:
- `EmptyState` 当前已把结果卡和续接卡的 badge 降为可选辅助字段;默认不再把 `结果模板 / 我的方法 / 围绕最近...` 顶在卡头
- 首页推荐卡当前继续保留“为什么推荐这条”,但这层信息已退回正文 `meta / contextSummary`,不再用 badge 抢标题层
- slash `先拿结果` 当前已把 curated task 行项目右侧 `kindLabel` 收掉;分组标题负责解释“这是结果模板”,行内只保留标题和合同描述
- slash 里的最近复盘横幅当前也已去掉 badge pill,只保留 `最近复盘已更新`、摘要和 `继续去...` 动作
- 因此用户当前沿着 `首页 -> 结果入口` 和 `slash -> 先拿结果` 两条主链时,看到的更接近“为什么现在该从这条开始”,而不是“这是哪个标签下的哪个对象”:
- 首页结果入口当前会先展示动作对象与推荐原因
- slash 首屏当前会先展示结果模板本身与启动合同,不再把标签塞到标题右侧
- 最近复盘 / 当前参考 / 最近成果这些高价值信号当前仍保留,但统一退到了正文说明层
- 这一步继续遵守 P1 / P2 的 current 边界:
- 不新增首页专属推荐状态机
- 不新增 slash 首屏第二套结果模板协议
- 不改结果模板排序、launcher、`onSelectCuratedTask` 与现有 route 主链,只做前台呈现减法
- 这次子改动的定向验证当前已通过:
- `npm exec vitest run "src/components/agent/chat/components/EmptyState.test.tsx" "src/components/agent/chat/skill-selection/CharacterMention.test.tsx" "src/components/agent/chat/skill-selection/inputCapabilitySections.test.ts"`
- `npx eslint "src/components/agent/chat/components/EmptyState.tsx" "src/components/agent/chat/components/EmptyState.test.tsx" "src/components/agent/chat/skill-selection/inputCapabilitySections.ts" "src/components/agent/chat/skill-selection/inputCapabilitySections.test.ts" "src/components/agent/chat/skill-selection/CharacterMentionPanel.tsx" "src/components/agent/chat/skill-selection/CharacterMention.test.tsx"`
- `npm run verify:gui-smoke -- --timeout-ms 180000`
- `vitest` 通过:`3 files / 140 tests passed`
- `eslint` 通过
- `verify:gui-smoke` 通过:`workspace-ready / browser-runtime / site-adapters / agent-service-skill-entry / agent-runtime-tool-surface / agent-runtime-tool-surface-page`
@@ -592,10 +592,9 @@ describe("EmptyState", () => {
);
expect(reviewCard?.textContent).toContain("复盘这个账号/项目");
expect(reviewCard?.textContent).toContain("围绕最近复盘");
expect(reviewCard?.textContent).toContain("复盘:短视频编排");
expect(breakdownCard?.textContent).toContain("拆解一条爆款内容");
expect(breakdownCard?.textContent).toContain("围绕最近复盘");
expect(breakdownCard?.textContent).toContain("复盘:短视频编排");
expect(reviewBanner?.textContent).toContain("最近复盘已更新");
expect(reviewBanner?.textContent).toContain("短视频编排 · 补证据");
expect(reviewBanner?.textContent).toContain("这轮结果还缺证据");
@@ -2156,7 +2155,7 @@ describe("EmptyState", () => {
await Promise.resolve();
});
expect(container.textContent).toContain("围绕当前参考");
expect(container.textContent).toContain("参考:品牌风格样本");
expect(container.textContent).toContain("内容主稿生成");
});
@@ -2221,7 +2220,7 @@ describe("EmptyState", () => {
await Promise.resolve();
});
expect(container.textContent).toContain("围绕最近成果");
expect(container.textContent).toContain("成果:账号复盘结论");
expect(container.textContent).toContain("复盘这个账号/项目");
});
@@ -559,7 +559,7 @@ type RecommendationShelfItem =
key: string;
title: string;
summary: string;
badge: string;
badge?: string;
hint: string;
meta: string;
contextSummary?: string;
@@ -573,7 +573,7 @@ type RecommendationShelfItem =
key: string;
title: string;
summary: string;
badge: string;
badge?: string;
hint: string;
meta: string;
contextSummary?: string;
@@ -587,7 +587,7 @@ interface ContinuationShelfItem {
key: string;
title: string;
summary: string;
badge: string;
badge?: string;
usedAt: number;
testId: string;
onSelect: () => void;
@@ -1309,7 +1309,6 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
key: template.id,
title: template.title,
summary: template.summary,
badge: featured.badgeLabel,
hint: template.outputHint,
meta: `${metaPrefix}${buildCuratedTaskCapabilityDescription(template, {
includeSummary: false,
@@ -1489,7 +1488,6 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
]
.filter((segment) => segment.length > 0)
.join(" · "),
badge: "结果模板",
usedAt: template.recentUsedAt as number,
testId: `entry-continuation-solution-${template.id}`,
onSelect: () =>
@@ -1525,7 +1523,6 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
]
.filter((segment) => segment.length > 0)
.join(" · "),
badge: "我的方法",
usedAt,
testId: `entry-continuation-method-${skill.key}`,
onSelect: () => {
@@ -1572,7 +1569,6 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
]
.filter((segment) => segment.length > 0)
.join(" · "),
badge: "我的方法",
usedAt,
testId: `entry-continuation-method-${skill.id}`,
onSelect: () => {
@@ -1815,7 +1811,6 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
{reviewFeedbackBanner ? (
<RecommendationSignalBanner data-testid="entry-review-feedback-banner">
<RecommendationSignalBannerHeader>
<RecommendationShelfBadge>围绕最近复盘</RecommendationShelfBadge>
<RecommendationSignalBannerTitle>
最近复盘已更新:{reviewFeedbackBanner.title}
</RecommendationSignalBannerTitle>
@@ -1847,7 +1842,9 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
<RecommendationLeadCard
type="button"
data-testid={leadRecommendationItem.testId}
title={`${leadRecommendationItem.badge} · ${leadRecommendationItem.summary} · ${leadRecommendationItem.meta}`}
title={[leadRecommendationItem.summary, leadRecommendationItem.meta]
.filter((segment) => segment.trim().length > 0)
.join(" · ")}
onClick={() => {
leadRecommendationItem.onSelect();
}}
@@ -1856,9 +1853,11 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
<RecommendationLeadEyebrow>
{resolveLeadRecommendationEyebrow(creationReplaySurface)}
</RecommendationLeadEyebrow>
<RecommendationShelfInlineBadge>
{leadRecommendationItem.badge}
</RecommendationShelfInlineBadge>
{leadRecommendationItem.badge ? (
<RecommendationShelfInlineBadge>
{leadRecommendationItem.badge}
</RecommendationShelfInlineBadge>
) : null}
</RecommendationLeadEyebrowRow>
<RecommendationLeadTitle>
{leadRecommendationItem.title}
@@ -1897,7 +1896,9 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
key={item.key}
type="button"
data-testid={item.testId}
title={`${item.badge} · ${item.summary} · ${item.meta}`}
title={[item.summary, item.meta]
.filter((segment) => segment.trim().length > 0)
.join(" · ")}
onClick={() => {
item.onSelect();
}}
@@ -1906,9 +1907,11 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
<RecommendationAssistCardTitle>
{item.title}
</RecommendationAssistCardTitle>
<RecommendationShelfInlineBadge>
{item.badge}
</RecommendationShelfInlineBadge>
{item.badge ? (
<RecommendationShelfInlineBadge>
{item.badge}
</RecommendationShelfInlineBadge>
) : null}
</RecommendationAssistCardHeader>
<RecommendationAssistCardSummary>
{item.contextSummary
@@ -1934,7 +1937,7 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
key={item.key}
type="button"
data-testid={item.testId}
title={`${item.badge} · ${item.summary}`}
title={item.summary}
onClick={() => {
item.onSelect();
}}
@@ -1943,7 +1946,9 @@ export const EmptyState: React.FC<EmptyStateProps> = ({
<RecommendationAssistCardTitle>
{item.title}
</RecommendationAssistCardTitle>
<RecommendationShelfBadge>{item.badge}</RecommendationShelfBadge>
{item.badge ? (
<RecommendationShelfBadge>{item.badge}</RecommendationShelfBadge>
) : null}
</RecommendationAssistCardHeader>
<RecommendationAssistCardSummary>
{item.summary}
@@ -846,6 +846,21 @@ export function ImageTaskViewer({
src={selectedOutput.url}
alt={selectedOutput.prompt || "图片任务结果"}
className="h-full w-full object-contain"
renderFallback={(reason) => (
<div className="flex h-full min-h-[320px] items-center justify-center px-6 text-center">
<div className="max-w-sm space-y-3">
<Sparkles className="mx-auto h-8 w-8 text-slate-400" />
<div className="text-sm font-semibold text-slate-900">
{reason === "error" ? "预览加载失败" : "暂无可预览图片"}
</div>
<div className="text-sm leading-6 text-slate-500">
{reason === "error"
? "这张图片暂时无法在预览窗口中展示,请稍后重试或在新窗口打开。"
: "当前没有可展示的图片结果。"}
</div>
</div>
</div>
)}
/>
) : null}
{selectedTaskOutputs.length > 1 ? (
@@ -926,6 +941,18 @@ export function ImageTaskViewer({
"w-full object-cover",
resolveOutputTileAspectClass(selectedTask?.layoutHint),
)}
renderFallback={() => (
<div
className={cn(
"flex w-full items-center justify-center bg-slate-50 px-3 text-center text-[11px] font-medium text-slate-400",
resolveOutputTileAspectClass(
selectedTask?.layoutHint,
),
)}
>
预览失败
</div>
)}
/>
<span className="absolute left-2 top-2 inline-flex h-6 min-w-6 items-center justify-center rounded-full border border-slate-200/80 bg-white/95 px-1.5 text-[11px] font-semibold text-slate-700 shadow-sm shadow-slate-950/5">
{resolveOutputDisplayIndex(index, output.slotIndex)}
@@ -2707,7 +2707,6 @@ describe("CharacterMention", () => {
await typeSlashAndWait(textarea);
expect(document.body.textContent).toContain("围绕最近成果");
expect(document.body.textContent).toContain("成果:本周账号复盘线索");
const resultTemplateSection = Array.from(
@@ -2757,7 +2756,6 @@ describe("CharacterMention", () => {
const banner = document.body.querySelector(
'[data-testid="input-capability-section-banner-result-templates"]',
);
expect(banner?.textContent).toContain("围绕最近复盘");
expect(banner?.textContent).toContain("最近复盘已更新:短视频编排 · 补证据");
expect(banner?.textContent).toContain("这轮结果还缺证据");
expect(banner?.textContent).toContain("这轮复盘更建议优先回到");
@@ -333,9 +333,11 @@ export const CharacterMentionPanel: React.FC<CharacterMentionPanelProps> = ({
<div className="flex flex-wrap items-start justify-between gap-2">
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-center gap-2">
<span className="rounded-full border border-emerald-200 bg-emerald-50 px-2 py-0.5 text-[10px] font-medium text-emerald-700">
{section.banner.badge}
</span>
{section.banner.badge ? (
<span className="rounded-full border border-emerald-200 bg-emerald-50 px-2 py-0.5 text-[10px] font-medium text-emerald-700">
{section.banner.badge}
</span>
) : null}
<div className="text-xs font-semibold leading-5 text-slate-900">
{section.banner.title}
</div>
@@ -110,7 +110,7 @@ export interface InputCapabilitySection {
heading: string;
items: InputCapabilityDescriptor[];
banner?: {
badge: string;
badge?: string;
title: string;
summary: string;
footnote?: string;
@@ -1276,8 +1276,6 @@ function buildSlashCapabilitySections(
}),
icon: "sparkles" as const,
iconClassName: "mr-2 h-4 w-4 text-amber-600",
kindLabel:
featuredCuratedTaskTemplateMap.get(task.id)?.badgeLabel ?? task.badge,
task,
launchInputValues: launchContext.launchPrefill?.inputValues,
referenceMemoryIds: launchContext.mergedReferenceMemoryIds,
@@ -1311,7 +1309,6 @@ function buildSlashCapabilitySections(
null;
return {
badge: "围绕最近复盘",
title: `最近复盘已更新:${latestReviewSignal.title}`,
summary: truncateSectionBannerText(
[
@@ -593,7 +593,6 @@ export const ProviderModelList: React.FC<ProviderModelListProps> = ({
preservesCurrentProviderCustomModels ||
(!autoFetchCapability.requiresLiveModelTruth &&
(normalizedSource === "Catalog" ||
normalizedSource === "CustomModels" ||
normalizedSource === "LocalFallback"));
if (result && result.models) {