MUL-3134: remove execution log event count

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Naiyuan Qing
2026-06-08 17:21:15 +08:00
parent df9538dc0c
commit 587e952d45
7 changed files with 79 additions and 56 deletions

View File

@@ -0,0 +1,72 @@
// @vitest-environment jsdom
import { cleanup, screen } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { AgentTask } from "@multica/core/types";
import { renderWithI18n } from "../../test/i18n";
const mockState = vi.hoisted(() => ({
taskMessagesOptions: vi.fn(),
}));
vi.mock("@multica/core/chat/queries", () => ({
taskMessagesOptions: mockState.taskMessagesOptions,
}));
vi.mock("../../common/actor-avatar", () => ({
ActorAvatar: () => <span data-testid="actor-avatar" />,
}));
vi.mock("../../common/task-transcript", () => ({
TranscriptButton: ({ title }: { title?: string }) => (
<button type="button">{title ?? "Transcript"}</button>
),
}));
vi.mock("./terminate-task-confirm-dialog", () => ({
TerminateTaskConfirmDialog: () => null,
}));
import { ActiveTaskRow } from "./execution-log-section";
function makeTask(overrides: Partial<AgentTask> = {}): AgentTask {
return {
id: "task-1",
agent_id: "agent-1",
runtime_id: "runtime-1",
issue_id: "issue-1",
status: "running",
priority: 0,
dispatched_at: null,
started_at: "2026-06-08T08:00:00Z",
completed_at: null,
result: null,
error: null,
created_at: "2026-06-08T08:00:00Z",
trigger_summary: "Started from comment",
...overrides,
};
}
beforeEach(() => {
cleanup();
vi.clearAllMocks();
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-06-08T08:05:04Z"));
});
afterEach(() => {
vi.useRealTimers();
});
describe("ActiveTaskRow", () => {
it("renders running status as elapsed time only", () => {
renderWithI18n(<ActiveTaskRow task={makeTask()} issueId="issue-1" />);
expect(screen.getByText("5m 04s")).toBeInTheDocument();
expect(screen.queryByText(/events?/i)).not.toBeInTheDocument();
expect(screen.getByText("Started from comment")).toBeInTheDocument();
expect(screen.getByText("View transcript")).toBeInTheDocument();
expect(mockState.taskMessagesOptions).not.toHaveBeenCalled();
});
});

View File

@@ -15,9 +15,7 @@ import {
} from "@multica/ui/components/ui/tooltip";
import { ActorAvatar } from "../../common/actor-avatar";
import { formatDuration } from "../../agents/components/agent-activity-hover-content";
import { TranscriptButton, buildTimeline } from "../../common/task-transcript";
import { taskMessagesOptions } from "@multica/core/chat/queries";
import { RunningStat } from "./running-stat";
import { TranscriptButton } from "../../common/task-transcript";
import { failureReasonLabel } from "../../agents/components/tabs/task-failure";
import { useT } from "../../i18n";
import { TerminateTaskConfirmDialog } from "./terminate-task-confirm-dialog";
@@ -245,15 +243,10 @@ function useStatusLabel(status: AgentTask["status"]): string {
}
}
// One active (running / queued / dispatched / parked) task row. Exported so
// the issue-detail header live-chip popover renders the exact same row as
// this panel — same trigger text, same status treatment, same hover-reveal
// Logs/Stop. The popover hosting it must use `keepMounted` so this row (and
// its internal confirm dialog) survives the popover closing on Stop click.
// Running rows read the shared per-task message cache (taskMessagesOptions,
// kept live by useRealtimeSync's global task:message handler) so every surface
// — this panel, the header-chip popover, and the transcript dialog — shows the
// same live "N events (elapsed)" and the same streaming Logs from one source.
// One active (running / queued / dispatched / parked) task row. Running rows
// keep status to a single live elapsed timer; transcript and stop stay available
// as hover actions. Transcript content lazy-loads on click via TranscriptButton,
// so the row no longer fetches task messages just to render a count.
export function ActiveTaskRow({
task,
issueId,
@@ -268,15 +261,6 @@ export function ActiveTaskRow({
const label = useStatusLabel(task.status);
const trigger = useTriggerText(task);
// Live message stream for this task — only fetched while running. The shared
// cache means the panel row, the popover row, and the chip all dedupe to one
// fetch + one WS-maintained entry.
const { data: msgs } = useQuery({
...taskMessagesOptions(task.id),
enabled: task.status === "running",
});
const items = useMemo(() => (msgs ? buildTimeline(msgs) : undefined), [msgs]);
// Running rows show a live-ticking elapsed timer (the ticking digits carry
// "alive", the duration carries "how long"). Only running rows tick.
const [now, setNow] = useState(() => Date.now());
@@ -320,7 +304,7 @@ export function ActiveTaskRow({
<RowStatus title={label}>
{task.status === "running" ? (
<>
<RunningStat eventCount={msgs?.length ?? 0} elapsed={elapsed} />
<span className="text-info tabular-nums">{elapsed}</span>
<span className="sr-only">{label}</span>
</>
) : (
@@ -332,8 +316,7 @@ export function ActiveTaskRow({
<TranscriptButton
task={task}
agentName=""
isLive
items={items}
isLive={task.status === "running"}
title={t(($) => $.execution_log.transcript_tooltip)}
/>
)}

View File

@@ -1,26 +0,0 @@
"use client";
import { useT } from "../../i18n";
// Shared "running" stat for the header chip and the execution-log / popover
// rows so all three read identically: live event count (primary, info blue)
// followed by the elapsed time in muted parens — e.g. "58 events (2m45s)".
// Both the count and the elapsed are live (event count via the shared
// task-messages cache, elapsed via the caller's 1s tick).
export function RunningStat({
eventCount,
elapsed,
}: {
eventCount: number;
elapsed: string;
}) {
const { t } = useT("issues");
return (
<span className="flex items-center gap-1 text-xs tabular-nums">
<span className="text-info">
{t(($) => $.agent_live.event_count, { count: eventCount })}
</span>
<span className="text-muted-foreground">({elapsed})</span>
</span>
);
}

View File

@@ -307,8 +307,6 @@
"fallback_name": "Agent",
"tool_count_one": "{{count}} tool",
"tool_count_other": "{{count}} tools",
"event_count_one": "{{count}} event",
"event_count_other": "{{count}} events",
"transcript_button": "View transcript",
"stop_button": "Stop",
"stop_tooltip": "Stop agent",

View File

@@ -295,7 +295,6 @@
"queued_elapsed_prefix": "{{elapsed}} 待機中",
"fallback_name": "エージェント",
"tool_count_other": "ツール {{count}} 件",
"event_count_other": "イベント {{count}} 件",
"transcript_button": "トランスクリプトを表示",
"stop_button": "停止",
"stop_tooltip": "エージェントを停止",

View File

@@ -307,8 +307,6 @@
"fallback_name": "에이전트",
"tool_count_one": "도구 {{count}}개",
"tool_count_other": "도구 {{count}}개",
"event_count_one": "이벤트 {{count}}개",
"event_count_other": "이벤트 {{count}}개",
"transcript_button": "트랜스크립트 보기",
"stop_button": "중지",
"stop_tooltip": "에이전트 중지",

View File

@@ -300,7 +300,6 @@
"queued_elapsed_prefix": "已排队 {{elapsed}}",
"fallback_name": "智能体",
"tool_count_other": "{{count}} 次工具调用",
"event_count_other": "{{count}} 个事件",
"transcript_button": "查看记录",
"stop_button": "停止",
"stop_tooltip": "停止智能体",