diff --git a/packages/views/chat/components/chat-thread-list.test.tsx b/packages/views/chat/components/chat-thread-list.test.tsx
deleted file mode 100644
index 372c6529e5..0000000000
--- a/packages/views/chat/components/chat-thread-list.test.tsx
+++ /dev/null
@@ -1,166 +0,0 @@
-import { describe, expect, it, vi } from "vitest";
-import { render, screen } from "@testing-library/react";
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { I18nProvider } from "@multica/core/i18n/react";
-import { chatKeys } from "@multica/core/chat/queries";
-import type { Agent, ChatSession } from "@multica/core/types";
-import enChat from "../../locales/en/chat.json";
-
-// ActorAvatar fetches agent presence/photo — stub it to a marker span so the
-// list renders without a QueryClient round-trip, and so we can assert the
-// archived dimming class lands on the avatar.
-vi.mock("../../common/actor-avatar", () => ({
- ActorAvatar: ({ actorId, className }: { actorId: string; className?: string }) => (
-
- ),
-}));
-
-vi.mock("@multica/core/hooks", () => ({
- useWorkspaceId: () => "ws-1",
-}));
-
-vi.mock("@multica/core/agents", () => ({
- useWorkspacePresenceMap: () => ({ byAgent: new Map() }),
-}));
-
-vi.mock("@multica/core/chat", () => {
- const state = { setActiveSession: vi.fn() };
- return {
- useChatStore: Object.assign(
- (selector?: (s: typeof state) => unknown) => (selector ? selector(state) : state),
- { getState: () => state },
- ),
- };
-});
-
-vi.mock("@multica/core/chat/mutations", () => ({
- useDeleteChatSession: () => ({ mutate: vi.fn(), isPending: false }),
- useSetChatSessionPinned: () => ({ mutate: vi.fn() }),
-}));
-
-import { ChatThreadList } from "./chat-thread-list";
-
-const TEST_RESOURCES = { en: { chat: enChat } };
-
-function makeAgent(overrides: Partial & Pick): Agent {
- return {
- workspace_id: "ws-1",
- runtime_id: "runtime-1",
- owner_id: "user-1",
- description: "",
- instructions: "",
- avatar_url: null,
- runtime_mode: "local",
- runtime_config: {},
- custom_args: [],
- visibility: "workspace",
- permission_mode: "public_to",
- invocation_targets: [{ target_type: "workspace", target_id: null }],
- status: "idle",
- max_concurrent_tasks: 1,
- model: "sonnet",
- skills: [],
- created_at: new Date(0).toISOString(),
- updated_at: new Date(0).toISOString(),
- archived_at: null,
- archived_by: null,
- ...overrides,
- } as Agent;
-}
-
-function makeSession(overrides: Partial & Pick): ChatSession {
- return {
- workspace_id: "ws-1",
- title: "A conversation",
- status: "active",
- pinned: false,
- unread_count: 0,
- has_unread: false,
- last_message: {
- content: "hello there",
- role: "assistant",
- created_at: "2026-07-08T00:00:00Z",
- failure_reason: null,
- },
- created_at: "2026-07-08T00:00:00Z",
- updated_at: "2026-07-08T00:00:00Z",
- ...overrides,
- } as ChatSession;
-}
-
-function renderList(sessions: ChatSession[], agents: Agent[]) {
- const qc = new QueryClient();
- qc.setQueryData(chatKeys.pendingTasks("ws-1"), { tasks: [] });
- return render(
-
-
-
-
- ,
- );
-}
-
-describe("ChatThreadList archived-agent handling (MUL-4265)", () => {
- it("marks a session whose agent is archived and dims its avatar", () => {
- const archived = makeAgent({
- id: "agent-archived",
- name: "Retired Bot",
- archived_at: "2026-07-01T00:00:00Z",
- });
- renderList(
- [makeSession({ id: "s-1", agent_id: "agent-archived", title: "Old chat" })],
- [archived],
- );
-
- // The "Archived" tag renders on the row.
- expect(screen.getByText(enChat.list.archived)).toBeInTheDocument();
- // The avatar is desaturated/dimmed.
- expect(screen.getByTestId("avatar-agent-archived").className).toContain("grayscale");
- });
-
- it("does not mark a session whose agent is active", () => {
- const active = makeAgent({ id: "agent-active", name: "Active Bot" });
- renderList(
- [makeSession({ id: "s-2", agent_id: "agent-active", title: "Live chat" })],
- [active],
- );
-
- expect(screen.queryByText(enChat.list.archived)).not.toBeInTheDocument();
- expect(screen.getByTestId("avatar-agent-active").className).not.toContain("grayscale");
- });
-
- it("suppresses the live 'typing' indicator for an archived agent even with a pending task", () => {
- const qc = new QueryClient();
- // A stray pending task on the archived agent's session must NOT render as
- // 'typing…' — a retired agent can never be running.
- qc.setQueryData(chatKeys.pendingTasks("ws-1"), {
- tasks: [{ task_id: "t-1", chat_session_id: "s-3", status: "running" }],
- });
- const archived = makeAgent({
- id: "agent-archived",
- name: "Retired Bot",
- archived_at: "2026-07-01T00:00:00Z",
- });
- render(
-
-
-
-
- ,
- );
-
- expect(screen.queryByText(enChat.list.typing)).not.toBeInTheDocument();
- // Falls back to the last-message preview instead.
- expect(screen.getByText(/hello there/)).toBeInTheDocument();
- });
-});
diff --git a/packages/views/chat/components/chat-thread-list.tsx b/packages/views/chat/components/chat-thread-list.tsx
index 9af987220c..870acab85c 100644
--- a/packages/views/chat/components/chat-thread-list.tsx
+++ b/packages/views/chat/components/chat-thread-list.tsx
@@ -2,7 +2,7 @@
import { useEffect, useMemo, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
-import { Archive, Clock, Loader2, Pin, PinOff, Square, Trash2 } from "lucide-react";
+import { Clock, Loader2, Pin, PinOff, Square, Trash2 } from "lucide-react";
import { cn } from "@multica/ui/lib/utils";
import { useWorkspaceId } from "@multica/core/hooks";
import { useWorkspacePresenceMap } from "@multica/core/agents";
@@ -145,12 +145,8 @@ export function ChatThreadList({
const renderRow = (session: ChatSession) => {
const isCurrent = session.id === activeSessionId;
const agent = agentById.get(session.agent_id) ?? null;
- // A retired agent can't take new work — the conversation is read-only.
- // We keep the row (history stays reachable) but mark it and suppress the
- // live "typing…/waiting" indicators, which can never apply here.
- const agentArchived = !!agent?.archived_at;
const pendingTask = pendingTaskBySessionId.get(session.id);
- const isRunning = !!pendingTask && !agentArchived;
+ const isRunning = !!pendingTask;
// Only "offline" (definitively long-offline) downgrades typing → waiting.
// Unknown/loading presence keeps the optimistic "typing…" so we never
// suppress it just because presence data hasn't landed yet.
@@ -222,17 +218,7 @@ export function ChatThreadList({
{/* Thin ring keeps photo + fallback avatars reading as the same circle
(the fallback's faint bg otherwise looks smaller). */}
{agent ? (
-
+
) : (
)}
@@ -249,12 +235,6 @@ export function ChatThreadList({
0 ? "font-semibold text-foreground" : "font-medium")}>
{titleText}
- {agentArchived && (
-
-
- {t(($) => $.list.archived)}
-
- )}
{timeText}
diff --git a/packages/views/locales/en/chat.json b/packages/views/locales/en/chat.json
index a58f0062e4..96c2fb4426 100644
--- a/packages/views/locales/en/chat.json
+++ b/packages/views/locales/en/chat.json
@@ -143,8 +143,7 @@
"waiting": "Waiting for reply",
"pin": "Pin",
"unpin": "Unpin",
- "pinned": "Pinned",
- "archived": "Archived"
+ "pinned": "Pinned"
},
"header": {
"view_profile": "View agent profile",
diff --git a/packages/views/locales/ja/chat.json b/packages/views/locales/ja/chat.json
index 573860422a..b5ca5af784 100644
--- a/packages/views/locales/ja/chat.json
+++ b/packages/views/locales/ja/chat.json
@@ -140,8 +140,7 @@
"waiting": "返信待ち",
"pin": "ピン留め",
"unpin": "ピン留めを解除",
- "pinned": "ピン留め済み",
- "archived": "アーカイブ済み"
+ "pinned": "ピン留め済み"
},
"header": {
"view_profile": "Agent のプロフィール",
diff --git a/packages/views/locales/ko/chat.json b/packages/views/locales/ko/chat.json
index 8d5f9caf11..eec4860891 100644
--- a/packages/views/locales/ko/chat.json
+++ b/packages/views/locales/ko/chat.json
@@ -140,8 +140,7 @@
"waiting": "답장 대기 중",
"pin": "고정",
"unpin": "고정 해제",
- "pinned": "고정됨",
- "archived": "보관됨"
+ "pinned": "고정됨"
},
"header": {
"view_profile": "Agent 프로필 보기",
diff --git a/packages/views/locales/zh-Hans/chat.json b/packages/views/locales/zh-Hans/chat.json
index a44cebdd70..b7c64c7729 100644
--- a/packages/views/locales/zh-Hans/chat.json
+++ b/packages/views/locales/zh-Hans/chat.json
@@ -140,8 +140,7 @@
"waiting": "等待回复",
"pin": "置顶",
"unpin": "取消置顶",
- "pinned": "已置顶",
- "archived": "已归档"
+ "pinned": "已置顶"
},
"header": {
"view_profile": "查看 Agent 资料",