fix: quiet successful Workbench runtime notices
Pipelines as Code CI / hwlab-nc01-v03-ci-poll- Success
Pipelines as Code CI / hwlab-nc01-v03-ci-poll- Success
This commit is contained in:
@@ -5,6 +5,16 @@ import { describe, expect, it } from "vitest";
|
||||
|
||||
import WorkbenchMessageCard from "./WorkbenchMessageCard.vue";
|
||||
|
||||
function componentStubs() {
|
||||
return {
|
||||
TraceTimeline: { template: "<div />" },
|
||||
LoadingState: { template: "<div />" },
|
||||
MessageMarkdown: { template: "<div />" },
|
||||
ApiErrorDiagnostic: { template: "<div />" },
|
||||
StatusBadge: { template: "<div />" }
|
||||
};
|
||||
}
|
||||
|
||||
describe("WorkbenchMessageCard semantic retry visibility", () => {
|
||||
it("shows Git mirror clone progress as runtime preparation", () => {
|
||||
const wrapper = mount(WorkbenchMessageCard, {
|
||||
@@ -41,11 +51,13 @@ describe("WorkbenchMessageCard semantic retry visibility", () => {
|
||||
}
|
||||
}
|
||||
},
|
||||
global: { stubs: { TraceTimeline: true, LoadingState: true, MessageMarkdown: true, ApiErrorDiagnostic: true, StatusBadge: true } }
|
||||
global: { stubs: componentStubs() }
|
||||
});
|
||||
expect(wrapper.text()).toContain("运行环境准备:正在从 Git mirror 获取 Runner 源码");
|
||||
expect(wrapper.text()).toContain("第 1/3 次源码获取进行中");
|
||||
expect(wrapper.text()).not.toContain("基础设施故障");
|
||||
expect(wrapper.get(".semantic-failure-banner").attributes("data-emphasis")).toBe("quiet");
|
||||
expect(wrapper.find(".semantic-failure-banner code").exists()).toBe(false);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
@@ -86,17 +98,12 @@ describe("WorkbenchMessageCard semantic retry visibility", () => {
|
||||
}
|
||||
},
|
||||
global: {
|
||||
stubs: {
|
||||
TraceTimeline: true,
|
||||
LoadingState: true,
|
||||
MessageMarkdown: true,
|
||||
ApiErrorDiagnostic: true,
|
||||
StatusBadge: true
|
||||
}
|
||||
stubs: componentStubs()
|
||||
}
|
||||
});
|
||||
|
||||
const banner = wrapper.get(".semantic-failure-banner");
|
||||
expect(banner.attributes("data-emphasis")).toBe("alert");
|
||||
expect(banner.text()).toContain("基础设施故障:Runner 镜像仓库暂时不可达");
|
||||
expect(banner.text()).toContain("第 2/5 次重试已安排");
|
||||
expect(banner.text()).toMatch(/预计 \d+ 秒后开始/u);
|
||||
|
||||
@@ -123,7 +123,7 @@ function traceStorageKey(message: ChatMessage): string {
|
||||
return `${props.storageKeyPrefix}.${message.traceId ?? message.runnerTrace?.traceId ?? message.id}`;
|
||||
}
|
||||
|
||||
function semanticRuntimeStatusView(message: ChatMessage, currentTimeMs: number): { title: string; detail: string; tone: string; code: string | null } | null {
|
||||
function semanticRuntimeStatusView(message: ChatMessage, currentTimeMs: number): { title: string; detail: string; tone: string; emphasis: "quiet" | "alert"; code: string | null } | null {
|
||||
const events = Array.isArray(message.runnerTrace?.events) ? message.runnerTrace.events : [];
|
||||
const event = [...events].reverse().find((item) => typeof item.failureDomain === "string" && typeof item.retryPhase === "string");
|
||||
if (!event) return null;
|
||||
@@ -135,10 +135,10 @@ function semanticRuntimeStatusView(message: ChatMessage, currentTimeMs: number):
|
||||
const progress = attempt !== null && maxAttempts !== null ? `第 ${attempt}/${maxAttempts} 次` : "本次";
|
||||
const code = firstText(event.failureCode, event.code);
|
||||
if (code === "git-mirror-fetch-in-progress") {
|
||||
return { title: `运行环境准备:${summary}`, detail: `${progress}源码获取进行中`, tone: "info", code };
|
||||
return { title: `运行环境准备:${summary}`, detail: `${progress}源码获取进行中`, tone: "info", emphasis: "quiet", code };
|
||||
}
|
||||
if (code === "git-mirror-fetch-completed") {
|
||||
return { title: `运行环境准备:${summary}`, detail: "源码获取完成,正在启动 Runner", tone: "recovered", code };
|
||||
return { title: `运行环境准备:${summary}`, detail: "源码获取完成,正在启动 Runner", tone: "recovered", emphasis: "quiet", code };
|
||||
}
|
||||
let detail = "正在判定有限重试";
|
||||
let tone = "warning";
|
||||
@@ -159,6 +159,7 @@ function semanticRuntimeStatusView(message: ChatMessage, currentTimeMs: number):
|
||||
title: `${domain}:${summary}`,
|
||||
detail,
|
||||
tone,
|
||||
emphasis: "alert",
|
||||
code
|
||||
};
|
||||
}
|
||||
@@ -198,10 +199,11 @@ function finiteInteger(value: unknown): number | null {
|
||||
<button v-if="detailEnabled && message.role === 'agent'" class="message-detail-button" type="button" aria-label="运行详情" title="运行详情" @click="emit('details', message)">!</button>
|
||||
</div>
|
||||
</header>
|
||||
<section v-if="semanticRuntimeStatus" class="semantic-failure-banner" :data-tone="semanticRuntimeStatus.tone" role="status" aria-live="polite">
|
||||
<section v-if="semanticRuntimeStatus" class="semantic-failure-banner" :data-tone="semanticRuntimeStatus.tone" :data-emphasis="semanticRuntimeStatus.emphasis" role="status" aria-live="polite" :title="semanticRuntimeStatus.code ? `${semanticRuntimeStatus.title} · ${semanticRuntimeStatus.detail} · ${semanticRuntimeStatus.code}` : undefined">
|
||||
<span class="semantic-status-marker" aria-hidden="true" />
|
||||
<strong>{{ semanticRuntimeStatus.title }}</strong>
|
||||
<span>{{ semanticRuntimeStatus.detail }}</span>
|
||||
<code v-if="semanticRuntimeStatus.code">{{ semanticRuntimeStatus.code }}</code>
|
||||
<code v-if="semanticRuntimeStatus.code && semanticRuntimeStatus.emphasis === 'alert'">{{ semanticRuntimeStatus.code }}</code>
|
||||
</section>
|
||||
<TraceTimeline v-if="message.role === 'agent' && message.runnerTrace" :trace="message.runnerTrace" :auto-expanded="autoExpanded" :storage-key="traceStorageKey(message)" final-response-placement="outer" :outer-final-response-text="visibleText" />
|
||||
<LoadingState v-if="isAwaitingAgentBody(message)" class="message-loading" label="思考中..." compact />
|
||||
@@ -222,15 +224,53 @@ function finiteInteger(value: unknown): number | null {
|
||||
padding: 0.55rem 0.7rem;
|
||||
}
|
||||
|
||||
.semantic-status-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.semantic-failure-banner[data-emphasis="quiet"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
min-width: 0;
|
||||
margin: 0.25rem 0;
|
||||
border-left-width: 2px;
|
||||
border-radius: 0.2rem;
|
||||
background: transparent;
|
||||
padding: 0.2rem 0.45rem;
|
||||
color: #475569;
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.semantic-failure-banner[data-emphasis="quiet"] .semantic-status-marker {
|
||||
display: block;
|
||||
flex: 0 0 0.42rem;
|
||||
width: 0.42rem;
|
||||
height: 0.42rem;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.semantic-failure-banner[data-emphasis="quiet"] strong {
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.semantic-failure-banner[data-emphasis="quiet"] span:not(.semantic-status-marker) {
|
||||
flex: 0 0 auto;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.semantic-failure-banner[data-tone="recovered"] {
|
||||
border-left-color: #15803d;
|
||||
background: #f0fdf4;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.semantic-failure-banner[data-tone="info"] {
|
||||
border-left-color: #2563eb;
|
||||
background: #eff6ff;
|
||||
color: #1e40af;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user